panel-notifications: the design questions, closed with the owner
test / test (push) Canceled after 0s
test / test (push) Canceled after 0s
Discussed on 2026-10-12, one question at a time; still a candidate. plans/panel-notifications.md records the answers: - Four events in the first version: a domain's DNS record stops matching, a level-2 limit refuses mail, the certificate is about to expire, a message is in the queue for more than three days (his number; the roadmap said one). - From postmaster@<hostname>, not from a sending domain: with several the choice is arbitrary, and a certificate is no domain's subject. An SPF record for the hostname makes such mail pass; Server > Health will check it. - One checkbox per user instead of a choice per event; one instance switch. - Hourly checks; a DNS mismatch is sent after two checks in a row; "cleared" messages for DNS and the queue only; certificate at 14 and 3 days; a limit at most once a day. roadmap.md follows, and no longer says the screens are drawn: the box was taken out of the mockups on 2026-10-10. Docs only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Plan: panel-notifications
|
||||
|
||||
**Status:** candidate - the design questions are closed with the owner (2026-10-12); taking it into work still needs his word
|
||||
**Date:** 2026-10-12
|
||||
**Version:** `2.3.0` MINOR (one migration; opt-out by a switch).
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
The panel writes to its administrators when something needs them, instead of
|
||||
waiting to be opened.
|
||||
|
||||
## Decisions (owner, 2026-10-12)
|
||||
|
||||
### Events of the first version
|
||||
|
||||
Four, each of them something the panel can already tell and a person has to
|
||||
act on:
|
||||
|
||||
| Event | Who is told |
|
||||
|---|---|
|
||||
| A sending domain's DNS record stopped matching (SPF, DKIM, DMARC) | global users, and `domain` users the domain is assigned to |
|
||||
| A level-2 rate limit refused mail | the same, for the domain of the limit |
|
||||
| The TLS certificate is about to expire | global users |
|
||||
| A message has been in the queue for more than **three days** | global users |
|
||||
|
||||
Three days, not one (owner: "one day is not that much"): Postfix keeps a
|
||||
message five, so two are left to act before it bounces. The threshold is a
|
||||
constant, not a setting.
|
||||
|
||||
Left for later: a process or milter socket down (a relay in that state often
|
||||
cannot send), an inbound upstream unreachable, DMARC failures appearing
|
||||
(noisy). Mail held in quarantine is not an event: quarantine is an idea
|
||||
outside the roadmap.
|
||||
|
||||
### Sender and path
|
||||
|
||||
From `postmaster@<SELFPOST_HOSTNAME>`, submitted locally. Not from one of the
|
||||
sending domains (owner): with several domains the choice is arbitrary, and a
|
||||
message about the certificate has no business coming from a domain.
|
||||
|
||||
The server's name usually has no DKIM key. One DNS record is enough for such
|
||||
mail to pass SPF and DMARC on the name itself: `v=spf1 a -all` for the
|
||||
hostname. Server › Health gains that check next to reverse DNS, and the guide
|
||||
describes it. Without the record notifications are still sent.
|
||||
|
||||
Notifications are the panel's own mail: they are not rows of the Outbound log
|
||||
and count against no level-2 limit.
|
||||
|
||||
A relay that cannot send cannot say so; none of the four events is "the relay
|
||||
is down", and that is why.
|
||||
|
||||
### Who chooses what
|
||||
|
||||
- One switch for the instance under **Server › Settings**.
|
||||
- One checkbox per user under **Account**, "send me notifications" - on when
|
||||
the profile has an e-mail. No choice per event: four events, none of them
|
||||
noise. Per-event choice comes when there are more and some are.
|
||||
- A user without an e-mail gets nothing.
|
||||
|
||||
### When a message is sent
|
||||
|
||||
A background check once an hour for DNS, certificate and queue.
|
||||
|
||||
| Event | Sent | "Cleared" message |
|
||||
|---|---|---|
|
||||
| DNS mismatch | after two checks in a row found it (it has held for an hour). A resolver that did not answer is not a mismatch | after two good checks in a row |
|
||||
| Certificate | 14 days before expiry, and again 3 days before if it is still the same certificate | none |
|
||||
| Level-2 limit refused mail | at the first refusal for a domain or application, then at most once a day for the same limit | none |
|
||||
| Queue older than three days | when such a message appears | one, when none is left |
|
||||
|
||||
Event state is kept in the database, so a restart announces nothing a second
|
||||
time.
|
||||
|
||||
## To settle while building
|
||||
|
||||
- How the panel submits: `sendmail` as the unprivileged panel user, and what
|
||||
that needs of the image.
|
||||
- Whether a notification carries a link to the panel - there is no setting
|
||||
for the panel's own address.
|
||||
- Where the refusal of a level-2 limit is noticed: in the journal milter as
|
||||
it refuses, or from the `rejected` rows of the send log on the hourly check.
|
||||
- Schema: the user's checkbox, the instance switch, the event state table.
|
||||
|
||||
## Screens
|
||||
|
||||
Not drawn. The Notifications box was taken out of the `account` and
|
||||
`settings` mockups on 2026-10-10 (owner: 2.0 ships no box for a feature that
|
||||
is not built). Drawing them again - one checkbox on Account, one switch on
|
||||
Server › Settings, the SPF check on Server › Health - is the first step, shown
|
||||
to the owner before any code ([panel-redesign.md](panel-redesign.md)
|
||||
§ Enforcement).
|
||||
|
||||
## Implementation checklist (draft; counted from the day the item is agreed)
|
||||
|
||||
- [ ] Mockups: Account checkbox, Settings switch, Health SPF check, help topic; accepted by the owner (`design:` commit)
|
||||
- [ ] Migration and store: user flag, instance switch, event state
|
||||
- [ ] Sending: local submission from `postmaster@<hostname>`, message text
|
||||
- [ ] Background checker: DNS, certificate, queue; the rules of *When a message is sent*
|
||||
- [ ] Level-2 refusal event
|
||||
- [ ] Panel: Account, Settings, Health check, help
|
||||
- [ ] guide.md, architecture.md, security.md, schema-migrations.md
|
||||
- [ ] `go vet`, `go test`, e2e: an event reaches a mailbox once and clears once
|
||||
- [ ] Technical review
|
||||
- [ ] Security review
|
||||
@@ -237,7 +237,7 @@ the first step of that feature's plan — not invented during implementation.
|
||||
| inbound-antispam-panel (agreed, `2.1.0`) | journal = **Inbound › Log** (add client IP and engine to the mockup's columns; `quarantine` joins the decisions); lists = new **Inbound › Filter lists**, global-only because the lists are instance-wide | list layout: add form + two tables, `status_tag`, filter form in `box_head`; the log links *Allow / Deny sender* to the lists | `in-log`, `in-filter-lists` |
|
||||
| inbound-quarantine (an idea, off the roadmap since 2026-10-12) | new **Inbound › Quarantine**, list + detail with *Release* / *Discard* | list layout; detail like `out-message` with `facts`, `log_pane` for headers only, Release beside a `sp-danger-zone` Discard | `in-quarantine`, `in-quarantine-message` — the open questions of its plan (where mail lives, what release means, retention, RBAC) stay open; the mockup assumes SelfPost-held mail, release to upstream, 14 days, delegated like the rest of Inbound |
|
||||
| preflight (candidate) | new **Server › Preflight**, next to Health: Health is what runs now, Preflight is the deeper on-demand installation check with a test e-mail form | `postmark` verdict, a table of checks with `status_tag` and a *What to do* column, one form box for the test e-mail | `preflight` |
|
||||
| panel-notifications (candidate) | per-user event choice on **Account**; one switch under **Server › Settings** | checkboxes in a box, `help` | **no** — drawn once, taken out of `account` and `settings` on 2026-10-10 (owner): 2.0 ships no box for a feature that is not built; redrawn as the first step of its plan |
|
||||
| panel-notifications (candidate) | one checkbox per user on **Account** (no choice per event, owner 2026-10-12); one switch under **Server › Settings** | a checkbox in a box, `help` | **no** — drawn once, taken out of `account` and `settings` on 2026-10-10 (owner): 2.0 ships no box for a feature that is not built; redrawn as the first step of its plan |
|
||||
| password-reset (candidate) | a link on sign-in, two signed-out screens (request, set new password) | the signed-out layout | **no** |
|
||||
| delivery-log-storage (candidate) | the *Delivery log* box of **Outbound › Log › message**, filled from a table instead of a grep of today's `mail.log` | `log_pane`, unchanged | `out-message` |
|
||||
| csrf-tokens | no screen; every form gains a hidden field — forms are rendered through one helper so the token cannot be forgotten | — | n/a |
|
||||
|
||||
+24
-19
@@ -35,7 +35,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
|
||||
| template-data-typing | Typed structs for template data instead of `map[string]any` | candidate | — | — |
|
||||
| structured-logging | `log/slog` with levels and fields | candidate | — | — |
|
||||
| review-2026-08-followups | Remaining findings of the 2026-08-19 code review | candidate | — | — |
|
||||
| panel-notifications | E-mail notifications about important events | candidate | — | — |
|
||||
| panel-notifications | E-mail notifications about important events | candidate, design closed | — | [plans/panel-notifications.md](plans/panel-notifications.md) |
|
||||
| delivery-log-storage | Keep a message's `mail.log` lines with its send-log row | shipped `2.2.0` | 8/8 | [plans/delivery-log-storage.md](plans/delivery-log-storage.md) |
|
||||
| password-reset | Password reset by e-mail | candidate | — | — |
|
||||
| phone-desktop-mode | A phone browser in "desktop site" mode gets the phone navigation | candidate | — | — |
|
||||
@@ -338,30 +338,35 @@ checklist: [plans/delivery-log-storage.md](plans/delivery-log-storage.md).
|
||||
## panel-notifications
|
||||
|
||||
**Goal:** the panel tells its administrators when something needs them instead
|
||||
of waiting to be opened: a DNS record stops matching, a domain or application
|
||||
hits its rate limit and mail is refused, an inbound upstream is unreachable,
|
||||
DMARC failures appear; for the global
|
||||
administrator also the TLS certificate nearing expiry, a process or milter
|
||||
socket down, a queue older than a day.
|
||||
of waiting to be opened. First version, four events: a sending domain's DNS
|
||||
record stops matching, a level-2 rate limit refuses mail, the TLS certificate
|
||||
is about to expire, a message has been in the queue for more than three days.
|
||||
|
||||
**Boundary:** e-mail only, sent through the relay itself from
|
||||
`postmaster@<hostname>`. Each user picks events under **Account**; a domain
|
||||
administrator hears only about assigned domains. One message when a problem
|
||||
starts and one when it clears — no streams, no digests in v1. One instance
|
||||
switch under **Server › Settings**. No webhooks, no chat integrations.
|
||||
**Boundary:** e-mail only, from `postmaster@<hostname>`, submitted locally -
|
||||
not from one of the sending domains. One checkbox per user under **Account**
|
||||
(no choice per event), one instance switch under **Server › Settings**; a
|
||||
domain administrator hears only about assigned domains. No streams, no
|
||||
digests, no webhooks, no chat integrations. Not in the first version: a
|
||||
process or milter down, an inbound upstream unreachable, DMARC failures.
|
||||
|
||||
**Done when:** an event from the list reaches the account e-mail of every user
|
||||
who chose it and may see its subject, once on start and once on clear; a user
|
||||
without an e-mail gets nothing; the switch turns it all off.
|
||||
**Done when:** each of the four events reaches the account e-mail of every
|
||||
user who has notifications on and may see its subject, by the rules of the
|
||||
plan (a "cleared" message for DNS and the queue only); a user without an
|
||||
e-mail gets nothing; the switch turns it all off.
|
||||
|
||||
**Dependencies / risks:** the account e-mail (2.0 baseline, see
|
||||
[plans/panel-redesign.md](plans/panel-redesign.md) § Account e-mail); a relay
|
||||
that cannot send cannot report that it cannot send — server events need the
|
||||
direct-to-MX path or stay panel-only; event state must survive restarts so a
|
||||
reboot does not re-announce everything. Screens are drawn: `account`,
|
||||
`settings`.
|
||||
that cannot send cannot report that it cannot send - none of the four events
|
||||
is "the relay is down"; mail from the bare hostname needs an SPF record for
|
||||
it to be believed, which Server › Health will check; event state must survive
|
||||
restarts so a reboot does not re-announce everything. The screens are not
|
||||
drawn: the Notifications box was taken out of `account` and `settings` on
|
||||
2026-10-10 and is redrawn as the first step.
|
||||
|
||||
**Version:** `2.x` MINOR; `candidate`.
|
||||
**Version:** `2.3.0` MINOR; `candidate` - the design questions were closed
|
||||
with the owner on 2026-10-12
|
||||
([plans/panel-notifications.md](plans/panel-notifications.md)); taking it into
|
||||
work still needs his word.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user