Verified the tree against docs/plans/code-review-2026-08.md and finished the
tasks that were only partly done, plus two defects the pass introduced.
- bcrypt: the setup account was still hashed at DefaultCost (10). The work
factor now has one definition, auth.BcryptCost = 12, used everywhere.
- rate limiter: protecting blocked buckets from eviction was unbounded — an
attacker with many source addresses could grow the map by making each one
fail its way to blocked. Blocked buckets are now evicted past
maxBuckets * 4, nearest expiry first, logged once per window. Tests cover
both halves.
- postqueue: the 5s timeout left the output size unbounded; reads are now
capped at 4 MiB and report truncation.
- spec references: the ~76 `spec N` citations CHANGELOG [0.5.0] claimed were
gone are removed from build/, deploy/, the first two migrations and the
release workflow, with the affected comment paragraphs re-wrapped. Comments
only; shell/YAML/SQL syntax re-checked.
- schema-migrations.md: schema section was headed v9 with the chain at v10,
and three links pointed at deleted plan files.
- internal/configsafe: the shared empty/forbidden-character check behind the
OpenDKIM and Postfix config writers; each writer keeps its own forbidden set.
- dropped the parseDomainRateLimitForm/parseAppRateLimitForm pass-throughs
left behind by the parser merge (only their own tests still called them).
- plan doc now carries a status table: what shipped, what was already in
place, what was declined (//go:build linux for the /proc reader) and the
open CSRF-token question.
Not done, each its own phase: typed template data structs, log/slog, DMARC
service and handler tests, error-page/corrupted-backup tests, backup re-auth.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Removes ~30 stale "Phase N" / historical-staging comment references from
code and shell scripts now that v1.0 is done; fixes a stale dashboard
comment claiming applications/send-log were unimplemented; adds a CSRF ADR
to security.md documenting the Origin-check-over-tokens decision; resolves
docs/logo in roadmap.md (directory doesn't exist, criterion already met);
adds a gofmt -l check to CI so unformatted Go fails the build.
The known-limitations write-up for the log-tailer offset gap (the other
Phase 1 item) was already present in architecture.md § Log tailer, so no
change was needed there.
gofmt/go vet/go test clean on both Go modules (main + test/e2e), verified
on the dev server.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Single bookworm-slim image running opendkim + panel + postfix under
supervisord with enforced start ordering (spec 4):
- build/Dockerfile: multi-stage static Go build; runtime installs postfix,
opendkim, cyrus-sasl, supervisor, logrotate; unprivileged panel user (7.6.8).
- build/supervisord.conf: priority ordering opendkim -> panel -> postfix;
crashexit event listener terminates the container on any FATAL process.
- build/postfix-wrapper.sh: waits for both milter sockets (test -S, 30s
timeout) before `postfix start-fg`, exits non-zero on timeout.
- panel: HTTP :8080 stub + /healthz, journal-milter socket stub (so the
wrapper's readiness probe passes), log-tailer stub; SIGTERM graceful stop.
Verified on the dev server: image builds, three processes live, panel serves
the stub, wrapper waits for sockets, and an unrecoverable panel failure brings
the container down cleanly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>