A notification interface, with SMTP as the built-in notifier #4

Closed
opened 2026-09-05 11:53:47 +00:00 by tiagoagueda · 0 comments
Owner

Observation

i want this suite to be as modular as possible hence the plugins desire from the start. we can have a native implementation of smtp for the kind of job #3 needs, but one of the first plugins i want to be built is "postulo-apprise" where we can implement several ways of notifications

This issue is the interface and the built-in notifier. The Apprise plugin is filed separately and depends on this.

What happens today

Postulo sends no notifications of any kind. The wiki says so plainly (Tracking applications: "Reminders do not notify you"): a reminder appears on the dashboard when its time comes and nowhere else. The only outbound email is django-allauth's — verification and password reset — through Django's MAILERS.

The plugin system covers exactly one kind of thing: capture sources, discovered through the postulo.sources entry-point group by src/postulo/plugins/registry.py. Sources are stateless — a URL and some HTML in, a JobPostingData out — so nothing was built for a plugin that needs per-user configuration or secrets.

Background work: django-tasks-db is installed and its worker exists, but nothing has ever enqueued a task and no scheduler runs. Notifications are the first feature that genuinely needs one.

What changes

1. A second plugin kind. An entry-point group postulo.notifiers, with the loader in plugins/registry.py generalised to handle more than one group. The contract, in the same spirit as sources — no base class, a handful of names:

  • name, version
  • config_fields() — what this notifier needs from a person (an address; an Apprise URL), so Postulo can build the settings form without knowing the plugin
  • send(notification, config) — deliver one message
  • optionally test(config) — behind a "send a test" button

2. Per-user channels. A NotificationChannel model: owner, plugin name, configuration, enabled, and which events it wants. Configuration will hold secrets (an Apprise URL embeds a bot token), so it is never rendered back in full, and is either encrypted at rest or the reason it is not gets written down.

3. The events. An initial, small set, each a plain dataclass any notifier can format:

  • a reminder falling due
  • a capture arriving through the API (the extension will make these frequent)
  • optionally a periodic digest: what is waiting for a reply, what is due this week

Not status changes the person made themselves — telling somebody what they just did is noise.

4. The built-in SMTP notifier. Uses MAILERS, so it shares configuration with #3's verification mail and there is one place to set up email.

5. Scheduling. Reminders falling due need something to notice. The plan settled on a management command driven by the host's cron (manage.py send_due_reminders), because a single-instance application does not need a worker for one job a minute; the alternative is finally using the django-tasks-db worker. Either way the container gets a documented way to run it — a cron-style sidecar in Compose, or a loop in the entrypoint.

6. Documentation. Tracking applications stops saying reminders do not notify; Configuration gains the channel settings; docs/PLUGINS.md gains a notifier section beside the source one.

Classification

An enhancement, and not a breaking change: off unless a person configures a channel, no schema removed, no operator action required on upgrade. The scheduling piece is the only thing an operator has to opt into, and it is documented rather than forced.

Open questions

  1. Immediate delivery, a digest, or both per channel?
  2. Cron-driven command or the tasks worker — the first is simpler to explain, the second is the thing already installed.
  3. Encryption of channel configuration at rest: a key derived from SECRET_KEY, a dedicated POSTULO_FIELD_KEY, or accept that the database already holds the person's whole job search and document the trade-off?
  4. Whether the SMTP notifier should offer plain text and HTML, or plain text only.
## Observation > i want this suite to be as modular as possible hence the plugins desire from the start. we can have a native implementation of smtp for the kind of job #3 needs, but one of the first plugins i want to be built is "postulo-apprise" where we can implement several ways of notifications This issue is the interface and the built-in notifier. The Apprise plugin is filed separately and depends on this. ## What happens today Postulo sends **no notifications of any kind**. The wiki says so plainly (*Tracking applications*: "Reminders do not notify you"): a reminder appears on the dashboard when its time comes and nowhere else. The only outbound email is django-allauth's — verification and password reset — through Django's `MAILERS`. The plugin system covers exactly one kind of thing: capture sources, discovered through the `postulo.sources` entry-point group by `src/postulo/plugins/registry.py`. Sources are stateless — a URL and some HTML in, a `JobPostingData` out — so nothing was built for a plugin that needs per-user configuration or secrets. Background work: `django-tasks-db` is installed and its worker exists, but nothing has ever enqueued a task and no scheduler runs. Notifications are the first feature that genuinely needs one. ## What changes **1. A second plugin kind.** An entry-point group `postulo.notifiers`, with the loader in `plugins/registry.py` generalised to handle more than one group. The contract, in the same spirit as sources — no base class, a handful of names: - `name`, `version` - `config_fields()` — what this notifier needs from a person (an address; an Apprise URL), so Postulo can build the settings form without knowing the plugin - `send(notification, config)` — deliver one message - optionally `test(config)` — behind a "send a test" button **2. Per-user channels.** A `NotificationChannel` model: owner, plugin name, configuration, enabled, and which events it wants. Configuration will hold secrets (an Apprise URL embeds a bot token), so it is never rendered back in full, and is either encrypted at rest or the reason it is not gets written down. **3. The events.** An initial, small set, each a plain dataclass any notifier can format: - a reminder falling due - a capture arriving through the API (the extension will make these frequent) - optionally a periodic digest: what is waiting for a reply, what is due this week Not status changes the person made themselves — telling somebody what they just did is noise. **4. The built-in SMTP notifier.** Uses `MAILERS`, so it shares configuration with #3's verification mail and there is one place to set up email. **5. Scheduling.** Reminders falling due need something to notice. The plan settled on a management command driven by the host's cron (`manage.py send_due_reminders`), because a single-instance application does not need a worker for one job a minute; the alternative is finally using the `django-tasks-db` worker. Either way the container gets a documented way to run it — a cron-style sidecar in Compose, or a loop in the entrypoint. **6. Documentation.** *Tracking applications* stops saying reminders do not notify; *Configuration* gains the channel settings; `docs/PLUGINS.md` gains a notifier section beside the source one. ## Classification An enhancement, and not a breaking change: off unless a person configures a channel, no schema removed, no operator action required on upgrade. The scheduling piece is the only thing an operator has to opt into, and it is documented rather than forced. ## Open questions 1. Immediate delivery, a digest, or both per channel? 2. Cron-driven command or the tasks worker — the first is simpler to explain, the second is the thing already installed. 3. Encryption of channel configuration at rest: a key derived from `SECRET_KEY`, a dedicated `POSTULO_FIELD_KEY`, or accept that the database already holds the person's whole job search and document the trade-off? 4. Whether the SMTP notifier should offer plain text and HTML, or plain text only.
tiagoagueda added this to the 0.2.0 milestone 2026-09-05 11:53:47 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
Postulo/postulo#4
No description provided.