Version cut 1.9.2: close the code review pass, bump the pin

The 2026-08-19 full-tree code review's fixes were sitting in Unreleased
alongside in-progress, docs-only panel-redesign prep. Per development.md
§ Roadmap track, docs-only prep gets no version cut until its product
stage ships, so the review-pass entries (all real code/behaviour fixes,
plus migration 0010, the DMARC-zip and port-25 e2e additions) move to a
new [1.9.2] section; the panel-redesign and preflight roadmap items stay
in a fresh Unreleased. Also drops two duplicate CHANGELOG bullets
(bcrypt cost, rate-limiter eviction) that restated an earlier entry
verbatim, and updates every 1.9.1 pin reference (docker-compose image,
README quickstart, guide.md, development.md's planned-cuts table,
schema-migrations.md's migration-0010 row, roadmap.md's review-closure
links) to 1.9.2.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-09-22 13:22:22 +03:00
co-authored by Claude Sonnet 5
parent d83d326217
commit 7c8a98a1cf
7 changed files with 32 additions and 31 deletions
+21 -21
View File
@@ -5,22 +5,6 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
## [Unreleased] ## [Unreleased]
Review pass over `1.4.0`…`1.9.1`: port 25 no longer shares the submission
milter chain, DMARC reports are validated before they are stored, and the auto
rate limit is correct for a non-default level-1 window. Schema migration
`0010` (index only). Upgrading is a tag bump.
A full-tree code review on 2026-08-19 (architecture, code quality, security,
GUI, tests, documentation, AGPL compliance; 38 findings) produced most of what
follows. Its four open items live in [roadmap.md](docs/roadmap.md); its working
document is not part of the release.
### Changed
- README hero line and a **Positioning** block in `docs/product.md` record the
tagline (`YOUR SMTP. YOUR RULES.`) and hero copy; the stamp subtitle stays
`SELF-HOSTED SMTP RELAY`. `docs/assets/selfpost-proof.html` updated to match.
### Added ### Added
- Panel redesign, account e-mail and notifications: a user's e-mail is its own - Panel redesign, account e-mail and notifications: a user's e-mail is its own
@@ -155,6 +139,21 @@ document is not part of the release.
([docs/plans/preflight.md](docs/plans/preflight.md)) — instance-level ([docs/plans/preflight.md](docs/plans/preflight.md)) — instance-level
infrastructure verification (rDNS, TLS, ports, HELO banner, proxy headers, infrastructure verification (rDNS, TLS, ports, HELO banner, proxy headers,
DKIM socket, test email). Status: candidate. DKIM socket, test email). Status: candidate.
## [1.9.2] - 2026-09-22
Review pass over `1.4.0`…`1.9.1`: port 25 no longer shares the submission
milter chain, DMARC reports are validated before they are stored, and the auto
rate limit is correct for a non-default level-1 window. Schema migration
`0010` (index only). Upgrading is a tag bump.
A full-tree code review on 2026-08-19 (architecture, code quality, security,
GUI, tests, documentation, AGPL compliance; 38 findings) produced most of what
follows. Its four open items live in [roadmap.md](docs/roadmap.md); its working
document is not part of the release.
### Added
- SQLite migration `0010_send_log_app_login_index.sql` — - SQLite migration `0010_send_log_app_login_index.sql` —
`idx_send_log_app_login_created_at`. The journal-milter counts an `idx_send_log_app_login_created_at`. The journal-milter counts an
application's messages on every `MAIL FROM`, and the domain page computes application's messages on every `MAIL FROM`, and the domain page computes
@@ -170,6 +169,12 @@ document is not part of the release.
survived three releases because the suite only ever exercised the flags-off survived three releases because the suite only ever exercised the flags-off
path. path.
### Changed
- README hero line and a **Positioning** block in `docs/product.md` record the
tagline (`YOUR SMTP. YOUR RULES.`) and hero copy; the stamp subtitle stays
`SELF-HOSTED SMTP RELAY`. `docs/assets/selfpost-proof.html` updated to match.
### Fixed ### Fixed
- The setup account's password is hashed at bcrypt cost `12` like every other - The setup account's password is hashed at bcrypt cost `12` like every other
@@ -199,17 +204,12 @@ document is not part of the release.
guard; one check now returns a context-specific message for self-demotion. guard; one check now returns a context-specific message for self-demotion.
- Settings flash text now reflects what actually changed and only mentions - Settings flash text now reflects what actually changed and only mentions
session sign-out when the password changed. session sign-out when the password changed.
- Login/setup rate-limiter bucket eviction now prefers the nearest-expiry bucket
and does not evict currently blocked buckets.
- Help page/drawer links now use `SourceURL` (`{{.SourceURL}}/blob/main/docs/...`) - Help page/drawer links now use `SourceURL` (`{{.SourceURL}}/blob/main/docs/...`)
instead of a hardcoded repository URL. instead of a hardcoded repository URL.
- Help drawer close controls include accessibility attributes (`role="button"`, - Help drawer close controls include accessibility attributes (`role="button"`,
`aria-label`) for the scrim and close action. `aria-label`) for the scrim and close action.
- Password hashing now uses explicit bcrypt cost `12` in settings/user password
updates, instead of `bcrypt.DefaultCost`.
- Test coverage expanded for DMARC map token injection rejection and email - Test coverage expanded for DMARC map token injection rejection and email
validation edge cases (ASCII/local-part/domain/freemail cases). validation edge cases (ASCII/local-part/domain/freemail cases).
- **Port 25 no longer runs the submission milter chain.** `smtpd_milters` is - **Port 25 no longer runs the submission milter chain.** `smtpd_milters` is
now always overridden on `smtp/inet` — to the optional antispam milter, or now always overridden on `smtp/inet` — to the optional antispam milter, or
to nothing. Previously, with `INBOUND_ANTISPAM_MILTER` unset, inbound mail to nothing. Previously, with `INBOUND_ANTISPAM_MILTER` unset, inbound mail
+1 -1
View File
@@ -112,7 +112,7 @@ docker run --rm -d --name selfpost-try \
-e SELFPOST_HOSTNAME=mail.local.test \ -e SELFPOST_HOSTNAME=mail.local.test \
-e PANEL_COOKIE_SECURE=false \ -e PANEL_COOKIE_SECURE=false \
-v selfpost-try-data:/data \ -v selfpost-try-data:/data \
ghcr.io/mixeme/selfpost:1.9.1 ghcr.io/mixeme/selfpost:1.9.2
``` ```
**Get the setup URL** (pick one): **Get the setup URL** (pick one):
+1 -1
View File
@@ -21,7 +21,7 @@
services: services:
selfpost: selfpost:
image: ghcr.io/mixeme/selfpost:1.9.1 image: ghcr.io/mixeme/selfpost:1.9.2
restart: unless-stopped restart: unless-stopped
environment: environment:
SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}" SELFPOST_HOSTNAME: "${SELFPOST_HOSTNAME:?set the mail/panel hostname, e.g. mail.example.com}"
+3 -2
View File
@@ -107,7 +107,7 @@ than the default «meaningful step» rule in [§ Commits and release build](#com
2. Git tag `vX.Y.Z` and publish the GitHub Release only on explicit request (see 2. Git tag `vX.Y.Z` and publish the GitHub Release only on explicit request (see
[§ Release image](#release-image)). [§ Release image](#release-image)).
**Planned version cuts** (from pin `1.9.1`; adjust if semver changes mid-track): **Planned version cuts** (from pin `1.9.2`; adjust if semver changes mid-track):
| Stage | ID | Cut | | Stage | ID | Cut |
|---|---|---| |---|---|---|
@@ -119,7 +119,8 @@ than the default «meaningful step» rule in [§ Commits and release build](#com
| 6 | panel-docs | `1.8.0` | | 6 | panel-docs | `1.8.0` |
| 7 | application auth IPs | `1.9.0` | | 7 | application auth IPs | `1.9.0` |
| 8 | port-25 Postfix startup fix | `1.9.1` (PATCH) | | 8 | port-25 Postfix startup fix | `1.9.1` (PATCH) |
| 9 | inbound-antispam-panel | `1.10.0` (planned) | | 9 | 2026-08-19 code review pass | `1.9.2` (PATCH) |
| 10 | inbound-antispam-panel | `1.10.0` (planned) |
Docs-only prep (checklists in plans, no product code) uses the same per-step Docs-only prep (checklists in plans, no product code) uses the same per-step
commit rule but **no** version cut until the next product stage ships. commit rule but **no** version cut until the next product stage ships.
+1 -1
View File
@@ -173,7 +173,7 @@ cat ./data/setup-token
#### Fixed image tag #### Fixed image tag
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`), `deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
deliberately never `:latest`. The current pin is `1.9.1`. Intermediate deliberately never `:latest`. The current pin is `1.9.2`. Intermediate
CHANGELOG sections (`0.2.0`…`0.6.0`) record development cuts from before that CHANGELOG sections (`0.2.0`…`0.6.0`) record development cuts from before that
image was published. Pinning matters because of the backup version check (see image was published. Pinning matters because of the backup version check (see
[Full backup and restore](#full-backup-and-restore)): the panel binary's [Full backup and restore](#full-backup-and-restore)): the panel binary's
+2 -2
View File
@@ -54,7 +54,7 @@ listed above and none of them urgent: [csrf-tokens](#csrf-tokens),
[template-data-typing](#template-data-typing), [template-data-typing](#template-data-typing),
[structured-logging](#structured-logging) and [structured-logging](#structured-logging) and
[review-2026-08-followups](#review-2026-08-followups). What that review [review-2026-08-followups](#review-2026-08-followups). What that review
produced is in [CHANGELOG.md](../CHANGELOG.md) `[Unreleased]`; its own document produced is in [CHANGELOG.md](../CHANGELOG.md) `[1.9.2]`; its own document
was removed once these four items carried everything still open, and stays in was removed once these four items carried everything still open, and stays in
git history. git history.
@@ -262,7 +262,7 @@ estimate: 3–4 hours.
**Goal:** the leftovers of the 2026-08-19 full-tree code review that are too **Goal:** the leftovers of the 2026-08-19 full-tree code review that are too
small to be roadmap items of their own. Everything still open from that review small to be roadmap items of their own. Everything still open from that review
is in the table below; the rest of it shipped in is in the table below; the rest of it shipped in
[CHANGELOG.md](../CHANGELOG.md) `[Unreleased]`. [CHANGELOG.md](../CHANGELOG.md) `[1.9.2]`.
| Area | Item | | Area | Item |
|---|---| |---|---|
+3 -3
View File
@@ -23,9 +23,9 @@ Documentation.
| Database file | `/data/selfpost.db` (bind mount) | | Database file | `/data/selfpost.db` (bind mount) |
| Compatibility | 1.x MINOR releases must boot a `1.0.0` data directory | | Compatibility | 1.x MINOR releases must boot a `1.0.0` data directory |
Last updated in **[Unreleased]** (`0010_send_log_app_login_index`); **1.9.1** Last updated in **[1.9.2]** (`0010_send_log_app_login_index`); **1.9.1**
had no schema change. The release column below records the cut a migration had no schema change. The release column below records the cut a migration
shipped in — fill `0010`'s in at the next version cut. shipped in.
--- ---
@@ -65,7 +65,7 @@ replace the embedded set (see [Planned 2.x squash](#planned-2x-squash)).
| 7 | `0007_rate_limit_auto.sql` | 1.6.0 | DDL | `rate_limits.mode`, `auto_multiplier`, `auto_updated_at` | | 7 | `0007_rate_limit_auto.sql` | 1.6.0 | DDL | `rate_limits.mode`, `auto_multiplier`, `auto_updated_at` |
| 8 | `0008_dmarc_reports.sql` | 1.7.0 | DDL | `dmarc_reports`, `dmarc_report_records` | | 8 | `0008_dmarc_reports.sql` | 1.7.0 | DDL | `dmarc_reports`, `dmarc_report_records` |
| 9 | `0009_application_auth_ips.sql` | 1.9.0 | DDL + data | `applications.auth_ip_restrict`, `auth_allowed_ips`; move legacy app `rate_limits.allowed_ips` into auth columns; clear those IPs on rate limits | | 9 | `0009_application_auth_ips.sql` | 1.9.0 | DDL + data | `applications.auth_ip_restrict`, `auth_allowed_ips`; move legacy app `rate_limits.allowed_ips` into auth columns; clear those IPs on rate limits |
| 10 | `0010_send_log_app_login_index.sql` | Unreleased | DDL | `idx_send_log_app_login_created_at` — app-scoped rate-limit counts and per-application send statistics were scanning the `created_at` range | | 10 | `0010_send_log_app_login_index.sql` | 1.9.2 | DDL | `idx_send_log_app_login_created_at` — app-scoped rate-limit counts and per-application send statistics were scanning the `created_at` range |
**Kind:** *DDL* — schema only; *data* — `INSERT`/`UPDATE` that must stay correct **Kind:** *DDL* — schema only; *data* — `INSERT`/`UPDATE` that must stay correct
for operators upgrading from older 1.x images. for operators upgrading from older 1.x images.