release: 2.5.0: audit stages 1 and 2 - the delivery log and the tenant boundary
test / test (push) Canceled after 0s

Stage 1: a cut queue listing no longer bounces held mail; the reconcile
sweep closes only queued rows, by id, marked "reconcile", and its fate is
measured (delivery_log_days, migration 0007); delivery lines are read at
the head of the line; a renewed TLS certificate is served without a
restart; Health counts the message a hung panel deferred.
Stage 2: logins folded to lower case and unique without regard to case;
the last global administrator kept inside the transaction, users saved
whole; a domain import held to the forms' rules; log rows by domain id
(migration 0008); the 550 reply names no other domain; which key OpenDKIM
signs with, tested. Plus the findings of both reviews of each stage.

Rolled out to the test server as 2.4.2-post.ef18ef2 (the same code):
migrations 0007 and 0008 ran on the live database - schema 8, 68/68 send-log
and 10/10 inbound rows tied to their domain, integrity ok.

CHANGELOG cut; pin 2.5.0 in deploy/docker-compose.yml, README (with the
local-build note), guide; development.md "from pin"; roadmap row and the
recommended order; stages 1 and 2 marked shipped in the plan. No tag (the
owner's command only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-14 16:22:04 +03:00
co-authored by Claude Opus 5.5
parent ef18ef21a0
commit 5be109b871
7 changed files with 20 additions and 12 deletions
+7
View File
@@ -5,6 +5,13 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
## [Unreleased] ## [Unreleased]
## [2.5.0] - 2026-10-14
Stages 1 and 2 of the 2026-10-12 audit: mail the delivery log showed wrong,
and the boundary between tenants. Two migrations: `0007` adds the delivery
log's own measurement, `0008` makes application logins unique without regard
to case and ties every log row to its domain by id (filled once on upgrade).
### Fixed ### Fixed
- **A queue too large to list no longer closes its own messages.** The - **A queue too large to list no longer closes its own messages.** The
+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.4.2 \ > docker build -f build/Dockerfile --build-arg VERSION=2.5.0 \
> -t ghcr.io/mixeme/selfpost:2.4.2 . > -t ghcr.io/mixeme/selfpost:2.5.0 .
> ``` > ```
```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.4.2 ghcr.io/mixeme/selfpost:2.5.0
``` ```
**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.4.2 image: ghcr.io/mixeme/selfpost:2.5.0
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}"
+2 -2
View File
@@ -596,7 +596,7 @@ has already run is never edited — the next file is added; the published
port 25 holds `554 … Relay access denied`), `550 5.1.1` (port 25 unknown port 25 holds `554 … Relay access denied`), `550 5.1.1` (port 25 unknown
recipients), any `4xx` reply (panel down). recipients), any `4xx` reply (panel down).
### Stage 1 — mail that is shown wrong (H3, H2, M12, M13) → `2.5.0` ### Stage 1 — mail that is shown wrong (H3, H2, M12, M13) → `2.5.0` (shipped 2026-10-14)
- [x] **`QueueIDs` on a truncated listing returns an error**, as `postqueueJSON` - [x] **`QueueIDs` on a truncated listing returns an error**, as `postqueueJSON`
does; `reconcile` already treats an error as no information. Test: a 5 MiB does; `reconcile` already treats an error as no information. Test: a 5 MiB
@@ -654,7 +654,7 @@ has already run is never edited — the next file is added; the published
the rule), § Sender check "what it costs"; guide § Delivery log, § Health. the rule), § Sender check "what it costs"; guide § Delivery log, § Health.
— **Sonnet** — **Sonnet**
### Stage 2 — the tenant boundary and the registry (H4, M7, M9, M10) → in `2.5.0` (migration `0008`) ### Stage 2 — the tenant boundary and the registry (H4, M7, M9, M10) → in `2.5.0` (migration `0008`; shipped 2026-10-14)
- [x] **Logins lower-cased** at validation (owner, 2026-10-12: fold, do not - [x] **Logins lower-cased** at validation (owner, 2026-10-12: fold, do not
refuse) and `applications.login COLLATE NOCASE` (migration `0008` rebuilds refuse) and `applications.login COLLATE NOCASE` (migration `0008` rebuilds
+1 -1
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.4.2`; adjust if semver changes mid-track): **Planned version cuts** (from pin `2.5.0`; adjust if semver changes mid-track):
| Stage | ID | Cut | | Stage | ID | Cut |
|---|---|---| |---|---|---|
+3 -3
View File
@@ -184,14 +184,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.4.2`. **No 2.x image is deliberately never `:latest`. The current pin is `2.5.0`. **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.4.2 \ docker build -f build/Dockerfile --build-arg VERSION=2.5.0 \
-t ghcr.io/mixeme/selfpost:2.4.2 . -t ghcr.io/mixeme/selfpost:2.5.0 .
``` ```
`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
+3 -2
View File
@@ -42,7 +42,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`; next stages 1+2 → `2.5.0`) | 22/62 | [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`; next stage 2b → `2.5.1`) | 22/62 | [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
@@ -50,7 +50,8 @@ development convenience): **panel-redesign** shipped as `2.0.0` (2026-10-11) and
kit — styling its two screens once instead of twice. **delivery-log-storage** kit — styling its two screens once instead of twice. **delivery-log-storage**
shipped as `2.2.0` the same day, **panel-notifications** as `2.3.0` and 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). [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.
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]`