release: 2.5.4: audit stage 4 - panel behaviour
test / test (push) Canceled after 0s

Cut without asking, as the owner said on 2026-10-15.

Signing in costs the same for an unknown name (M8) and ends the session the
browser presented (L16); the Account password check has its own limit
(L15). The HTTP server bounds the request, the answer and idle connections;
the full backup moves its own deadline (L14). Monitoring refreshes are
scheduled from htmx:afterRequest with a back-off and a 30 s timeout, and a
lost session sends htmx to sign-in (M11). The Outbound log clamps its page
(L10) and filters by status and day; the multiplier refuses NaN (L11); a
rate-limited refusal is red (L18); Recalculate asks before it drops an
unsaved edit (L19); Reload configuration writes what a restore writes and
removes the keys of deleted domains (L13, L17). The cards layout orders by
number. A notification is owed to each recipient (migration 0011). A DNS
outage is unknown, each resolver with its own time (M14, M15). DMARC
reports too large are refused for their size. An inbound upstream has one
TLS mode, changed for all its domains (L5). Plus the findings of both
reviews (eaa7de0). Commits 07be0cf, 643892a, ca876c1, 80e3979, 2b8e0c0,
b90a2f0, eee089a, 006bed7, 32dfe4a, eaa7de0.

Rolled out to the test server as 2.5.3-post.eaa7de0 (the same code):
migration 0011 applied, schema 11; all programs RUNNING, the container
healthy. Resync's key cleanup checked against the live data: every key
directory is a domain's or the hostname's, nothing to remove.

CHANGELOG cut; pin 2.5.4 in deploy/docker-compose.yml, README, guide (and
its sample restore message); development.md "from pin" and the stage's row;
roadmap row and recommended order; stage 4 marked shipped in the plan. No
tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-15 12:51:50 +03:00
co-authored by Claude Opus 5.5
parent eaa7de0754
commit d6ed6bcb30
7 changed files with 22 additions and 14 deletions
+8 -1
View File
@@ -5,7 +5,14 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
## [Unreleased] ## [Unreleased]
Migration `0011` (a table, nothing existing changes). ## [2.5.4] - 2026-10-15
Stage 4 of the 2026-10-12 audit: the panel's behaviour. Signing in tells
nothing by its timing and ends a planted session; the server bounds its
connections; monitoring pages recover from a failed refresh; the Outbound
log filters by status and by day; a notification is owed to each recipient;
a DNS outage is unknown, not wrong; Reload configuration writes everything a
restore writes. Migration `0011` (a table, nothing existing changes).
### Added ### Added
+3 -3
View File
@@ -112,8 +112,8 @@ outbound mail will not reach the real internet without DNS, PTR, and port 25.
> local image and pulls nothing: > local image and pulls nothing:
> >
> ```sh > ```sh
> docker build -f build/Dockerfile --build-arg VERSION=2.5.3 \ > docker build -f build/Dockerfile --build-arg VERSION=2.5.4 \
> -t ghcr.io/mixeme/selfpost:2.5.3 . > -t ghcr.io/mixeme/selfpost:2.5.4 .
> ``` > ```
```sh ```sh
@@ -122,7 +122,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:2.5.3 ghcr.io/mixeme/selfpost:2.5.4
``` ```
**Get the setup URL** (pick one): **Get the setup URL** (pick one):
+1 -1
View File
@@ -23,7 +23,7 @@
services: services:
selfpost: selfpost:
image: ghcr.io/mixeme/selfpost:2.5.3 image: ghcr.io/mixeme/selfpost:2.5.4
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}"
+1 -1
View File
@@ -1183,7 +1183,7 @@ for the milter sockets; a CLI killed by SIGKILL cannot clean its temporary
file, and a handler for the gentler signals buys little for a one-off file, and a handler for the gentler signals buys little for a one-off
command whose leftover is the owner's alone. command whose leftover is the owner's alone.
### Stage 4 — panel behaviour (M8, M11, L10–L19) → `2.5.4` ### Stage 4 — panel behaviour (M8, M11, L10–L19) → `2.5.4` (shipped 2026-10-15)
- [x] Login: dummy bcrypt on unknown user; a test that measures both paths within - [x] Login: dummy bcrypt on unknown user; a test that measures both paths within
a tolerance. Account password change gets its own limiter bucket. Login a tolerance. Account password change gets its own limiter bucket. Login
+2 -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 `2.5.3`; adjust if semver changes mid-track): **Planned version cuts** (from pin `2.5.4`; adjust if semver changes mid-track):
| Stage | ID | Cut | | Stage | ID | Cut |
|---|---|---| |---|---|---|
@@ -135,7 +135,7 @@ than the default «meaningful step» rule in [§ Commits and release build](#com
| 22 | audit-2026-10-12 stage 2b: the log tailer | `2.5.1` | | 22 | audit-2026-10-12 stage 2b: the log tailer | `2.5.1` |
| 23 | audit-2026-10-12 stage 2c: the sender check reads From: as OpenDKIM does | `2.5.2` | | 23 | audit-2026-10-12 stage 2c: the sender check reads From: as OpenDKIM does | `2.5.2` |
| 24 | audit-2026-10-12 stage 3: persistence and restore | `2.5.3` | | 24 | audit-2026-10-12 stage 3: persistence and restore | `2.5.3` |
| 25 | audit-2026-10-12 stage 4 | `2.5.4` | | 25 | audit-2026-10-12 stage 4: panel behaviour | `2.5.4` |
| 26 | audit-2026-10-12 stage 5 | `2.6.0` | | 26 | audit-2026-10-12 stage 5 | `2.6.0` |
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
+4 -4
View File
@@ -190,14 +190,14 @@ 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 `2.5.3`. **No 2.x image is deliberately never `:latest`. The current pin is `2.5.4`. **No 2.x image is
published on ghcr.io yet** (the newest there is `1.3.0`); until one is, build published on ghcr.io yet** (the newest there is `1.3.0`); until one is, build
it from the checkout under the pinned name, and Compose uses the local image it from the checkout under the pinned name, and Compose uses the local image
instead of pulling: instead of pulling:
```sh ```sh
docker build -f build/Dockerfile --build-arg VERSION=2.5.3 \ docker build -f build/Dockerfile --build-arg VERSION=2.5.4 \
-t ghcr.io/mixeme/selfpost:2.5.3 . -t ghcr.io/mixeme/selfpost:2.5.4 .
``` ```
`VERSION` must equal the tag: it is the version the backup check compares `VERSION` must equal the tag: it is the version the backup check compares
@@ -861,7 +861,7 @@ A version mismatch at step 5 refuses to start and leaves `/data` untouched —
the panel exits with a message naming the tag to use, e.g.: the panel exits with a message naming the tag to use, e.g.:
``` ```
backup: this backup was created by SelfPost 2.5.2 but this image is 2.5.3 — restore into the matching image (selfpost:2.5.2) backup: this backup was created by SelfPost 2.5.3 but this image is 2.5.4 — restore into the matching image (selfpost:2.5.3)
``` ```
Fix the tag in `docker-compose.yml` (build that tag as under [Fixed image Fix the tag in `docker-compose.yml` (build that tag as under [Fixed image
+3 -2
View File
@@ -45,7 +45,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
| from-header-binding | Hold a message's `From:` header against the application that sends it | shipped `2.4.0` | 9/9 | [plans/from-header-binding.md](plans/from-header-binding.md) | | from-header-binding | Hold a message's `From:` header against the application that sends it | shipped `2.4.0` | 9/9 | [plans/from-header-binding.md](plans/from-header-binding.md) |
| panel-redesign | Panel redesign (accepted mockups, design contract) | shipped `2.0.0` | 27/27 | [plans/panel-redesign.md](plans/panel-redesign.md) | | panel-redesign | Panel redesign (accepted mockups, design contract) | shipped `2.0.0` | 27/27 | [plans/panel-redesign.md](plans/panel-redesign.md) |
| schema-squash | Squash SQLite migrations into a 2.x baseline | built in panel-redesign (stage 1) | — | [plans/panel-redesign.md](plans/panel-redesign.md) | | schema-squash | Squash SQLite migrations into a 2.x baseline | built in panel-redesign (stage 1) | — | [plans/panel-redesign.md](plans/panel-redesign.md) |
| audit-2026-10-12 | Follow-ups of the 2026-10-12 full-tree code audit, seven stages | in progress (stage 0 shipped `2.4.2`, stages 1+2 `2.5.0`, 2b `2.5.1`, 2c `2.5.2`, 3 `2.5.3`; next stage 4 → `2.5.4`) | 68/81 | [audit-2026-10-12.md](audit-2026-10-12.md) | | audit-2026-10-12 | Follow-ups of the 2026-10-12 full-tree code audit, seven stages | in progress (stage 0 shipped `2.4.2`, stages 1+2 `2.5.0`, 2b `2.5.1`, 2c `2.5.2`, 3 `2.5.3`, 4 `2.5.4`; next stage 5 → `2.6.0`) | 68/81 | [audit-2026-10-12.md](audit-2026-10-12.md) |
**Recommended order** (not binding; owner, 2026-10-09: feature order follows **Recommended order** (not binding; owner, 2026-10-09: feature order follows
development convenience): **panel-redesign** shipped as `2.0.0` (2026-10-11) and development convenience): **panel-redesign** shipped as `2.0.0` (2026-10-11) and
@@ -55,7 +55,8 @@ shipped as `2.2.0` the same day, **panel-notifications** as `2.3.0` and
**from-header-binding** as `2.4.0`. Stage 0 of **from-header-binding** as `2.4.0`. Stage 0 of
[audit-2026-10-12](audit-2026-10-12.md) shipped as `2.4.2` (2026-10-14), its [audit-2026-10-12](audit-2026-10-12.md) shipped as `2.4.2` (2026-10-14), its
stages 1 and 2 as `2.5.0` the same day, stage 2b as `2.5.1`, stage 2c as stages 1 and 2 as `2.5.0` the same day, stage 2b as `2.5.1`, stage 2c as
`2.5.2` (2026-10-15), and stage 3 as `2.5.3` the same day. `2.5.2` (2026-10-15), and stages 3 and 4 as `2.5.3` and `2.5.4` the same
day.
Nothing is agreed after it: every remaining Nothing is agreed after it: every remaining
item is a candidate. panel-docs shipped in item is a candidate. panel-docs shipped in
[CHANGELOG.md](../CHANGELOG.md) `[1.8.0]`; dmarc-reports in `[1.7.0]` [CHANGELOG.md](../CHANGELOG.md) `[1.8.0]`; dmarc-reports in `[1.7.0]`