From 977d63a2ea31df263c5e7725abea94908cc68939 Mon Sep 17 00:00:00 2001 From: Mix Date: Mon, 12 Oct 2026 10:04:44 +0300 Subject: [PATCH] roadmap: inbound quarantine is an idea, not a candidate The owner, 2026-10-12: take quarantine out of the main plan, it is an idea to think about. Its index row and section leave roadmap.md; a new closing section, "Ideas, not on the roadmap", keeps a paragraph and the link to the notes in plans/inbound-quarantine.md. panel-notifications no longer lists "mail is held in quarantine" among its events. The plans that called it a candidate say what it is now. The drawn screens stay unbuilt, as they were. Co-Authored-By: Claude Opus 5.5 --- docs/plans/inbound-antispam-panel.md | 4 +-- docs/plans/inbound-quarantine.md | 2 +- docs/plans/panel-redesign.md | 6 ++-- docs/roadmap.md | 43 ++++++++++++---------------- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/docs/plans/inbound-antispam-panel.md b/docs/plans/inbound-antispam-panel.md index 0a4ebf0..f914640 100644 --- a/docs/plans/inbound-antispam-panel.md +++ b/docs/plans/inbound-antispam-panel.md @@ -34,8 +34,8 @@ Lists let the operator correct false positives/negatives (e.g. always allow ## Quarantine -**Not in this plan.** Held-mail review/release is tracked separately as roadmap -candidate [inbound-quarantine](inbound-quarantine.md). The journal may still +**Not in this plan.** Held-mail review/release is an idea kept outside the roadmap, +[inbound-quarantine](inbound-quarantine.md). The journal may still record a `quarantine` **decision label** when the external filter reports it, without SelfPost storing the message. diff --git a/docs/plans/inbound-quarantine.md b/docs/plans/inbound-quarantine.md index d97315a..3b29123 100644 --- a/docs/plans/inbound-quarantine.md +++ b/docs/plans/inbound-quarantine.md @@ -1,6 +1,6 @@ # Plan: inbound-quarantine -**Status:** candidate — design TBD; needs explicit agreement before any code +**Status:** an idea to think about, not on the roadmap (owner, 2026-10-12) — design TBD; needs explicit agreement before any code **Version:** TBD (`1.x` MINOR if opt-in and backward-compatible; may warrant `2.x` if it changes inbound storage or backup contract). diff --git a/docs/plans/panel-redesign.md b/docs/plans/panel-redesign.md index d183633..a5ba08e 100644 --- a/docs/plans/panel-redesign.md +++ b/docs/plans/panel-redesign.md @@ -235,7 +235,7 @@ the first step of that feature's plan — not invented during implementation. | Roadmap item | Where it lands | Built from | Drawn | |---|---|---|---| | 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 (candidate) | 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 | +| 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 | | password-reset (candidate) | a link on sign-in, two signed-out screens (request, set new password) | the signed-out layout | **no** | @@ -539,5 +539,5 @@ only when both are done. Every stage-2 step ends with the evidence of order suits development, preflight, delivery-log-storage, panel-notifications, password-reset (two screens to draw first), queue as a table with the inbound queue, csrf-tokens -(one hidden field in the form partial once security.md is settled), -inbound-quarantine after its open questions. +(one hidden field in the form partial once security.md is settled). +inbound-quarantine is an idea outside the roadmap. diff --git a/docs/roadmap.md b/docs/roadmap.md index 80a20b4..8433f5f 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -31,7 +31,6 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md). | preflight | Installation check page (`/preflight`) | candidate | — | [plans/preflight.md](plans/preflight.md) | | contributing | `CONTRIBUTING.md` | candidate | — | — | | inbound-antispam-panel | Inbound antispam journal + allow/deny lists | shipped `2.1.0` | 12/12 | [plans/inbound-antispam-panel.md](plans/inbound-antispam-panel.md) | -| inbound-quarantine | Inbound spam quarantine (hold / review / release) | candidate | — | [plans/inbound-quarantine.md](plans/inbound-quarantine.md) | | csrf-tokens | Per-session CSRF tokens on state-changing forms | candidate | — | — | | template-data-typing | Typed structs for template data instead of `map[string]any` | candidate | — | — | | structured-logging | `log/slog` with levels and fields | candidate | — | — | @@ -124,8 +123,8 @@ action) plus editable allow/deny lists that tune the external filter — without shipping rspamd or another engine inside the image. **Boundary:** metadata journal and list sync only; SelfPost does not run the -filter ([product.md](product.md)). Quarantine: -[inbound-quarantine](#inbound-quarantine). Design closed in +filter ([product.md](product.md)). Quarantine is not on the roadmap +([Ideas](#ideas-not-on-the-roadmap)). Design closed in [plans/inbound-antispam-panel.md](plans/inbound-antispam-panel.md) § Decisions. **Done when:** with inbound relay enabled, the operator sees journal rows @@ -140,27 +139,6 @@ journal PII; global-admin-only RBAC. --- -## inbound-quarantine - -**Goal:** optional hold for suspicious inbound mail — review in the panel, then -release to upstream, discard, or tune lists. Design not settled. - -**Boundary:** expands inbound from pure relay toward short-lived message -storage; may conflict with "no mailboxes" in [product.md](product.md) until -product explicitly agrees. Alternatives (rspamd-only quarantine vs SelfPost -store) are listed in [plans/inbound-quarantine.md](plans/inbound-quarantine.md). - -**Done when:** TBD after open questions in the plan are answered. - -**Dependencies / risks:** inbound relay `[1.4.0]`; storage/retention/backup -size; malware in held MIME; overlap with -[inbound-antispam-panel](#inbound-antispam-panel). No implementation checklist -yet. - -**Version:** TBD; `candidate` until explicitly agreed. - ---- - ## preflight **Goal:** a `/preflight` page (global-admin only) that verifies instance-level @@ -361,7 +339,7 @@ the kit's `log_pane`. **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, mail is held in quarantine; for the global +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. @@ -520,3 +498,18 @@ explicit major). Squashing the current short chain is not a reason to cut 2.x on its own. A missed gate leaves a mid-chain 1.x database silently stuck. **Version:** `2.x` major only; not a 1.x item. +--- + +## Ideas, not on the roadmap + +Thoughts kept so they are not lost. They are not candidates: nothing here is +waiting for an OK, has a version or takes a place in the order above. An idea +joins the index only when the owner says so. + +- **Inbound quarantine** (moved here by the owner on 2026-10-12: "an idea to + think about") - hold suspicious inbound mail, review it in the panel, then + release it to the upstream or discard it. It would take the relay from + passing mail on to storing it for a while, against "no mailboxes" in + [product.md](product.md). Notes and open questions: + [plans/inbound-quarantine.md](plans/inbound-quarantine.md); the screens + `in-quarantine` and `in-quarantine-message` are drawn and stay unbuilt.