Commit Graph
34 Commits
Author SHA1 Message Date
mixandClaude Sonnet 5 1ee509dcab Version cut 1.9.3: close the panel-redesign docs prep, bump the pin
test / test (push) Canceled after 0s
CHANGELOG.md audited against the full commit history from the repo's
first commit (8ad56ed) through HEAD: version/date headings, entry
completeness, and technical claims against their diffs all check out,
no discrepancies found.

The Unreleased section (panel-redesign mockups, contract, and
decisions; preflight plan) is docs-only prep — development.md's own
rule would hold the cut until a product stage ships — but the owner
asked for a version cut now, so it becomes 1.9.3 (PATCH) and a fresh
Unreleased opens. Pin bumped in deploy/docker-compose.yml, README's
quickstart, guide.md, and development.md's planned-cuts table, which
also gains the new row (renumbering inbound-antispam-panel to 11).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 14:17:50 +03:00
mixandClaude Sonnet 5 7c8a98a1cf 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>
2026-09-22 13:22:22 +03:00
mixandClaude Opus 5 ab2cd885c4 review follow-up: close the gaps left by the phase1-6 pass
test / test (push) Has been cancelled
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>
2026-08-19 20:03:05 +03:00
mixandCursor 4a51db277b release: 1.9.1
test / test (push) Has been cancelled
Fix port-25 Postfix startup when inbound relay, DMARC ingest, or antispam milter are enabled; consolidate schema-migrations and inbound antispam/quarantine roadmap docs in the changelog.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-19 00:47:45 +03:00
mixandCursor b4a9b93cf2 release: 1.9.0
test / test (push) Has been cancelled
Application client IP allow-list restricts which addresses may submit as a SASL login; level-2 rate limits override the domain ceiling per application (higher or lower, capped at L1). Migration 0009, authips form, milter enforcement, export/import, and operator docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 23:51:41 +03:00
mixandCursor 33ee08f7b6 release: 1.8.0
test / test (push) Has been cancelled
Add in-panel Help: CSS-only drawer with seeded Status and domain topics,
«?» entry points on cards, and guide.md boundary for what stays in-repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 23:00:08 +03:00
mixandCursor 27aeadc71d release: 1.7.0
test / test (push) Has been cancelled
Receive DMARC aggregate reports on port 25 and show parsed summaries in the panel. Close Unreleased; pin compose and docs to 1.7.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 22:53:36 +03:00
mixandCursor c1ec4fbd79 release: 1.6.0
test / test (push) Has been cancelled
Add 30-day send statistics and auto level-2 rate limits on the domain page. Close Unreleased; pin compose and docs to 1.6.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 22:26:51 +03:00
mixandCursor 8538b8d5f6 release: 1.5.0
test / test (push) Has been cancelled
Close Unreleased for send-log retention in Settings; pin compose and docs to 1.5.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 22:01:26 +03:00
mixandCursor d0bf4a4226 release: 1.4.0
test / test (push) Has been cancelled
Close Unreleased for inbound relay; pin compose and docs to 1.4.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 23:20:18 +03:00
mixandCursor 0d98d92642 Add optional inbound relay (backup-MX) behind INBOUND_RELAY_ENABLE.
test / test (push) Has been cancelled
Port 25 accepts only configured domains and listed recipients, then forwards to an upstream; the outbound path is unchanged when the flag is off.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 23:17:30 +03:00
mixandCursor 6218540211 release: 1.3.1
test / test (push) Has been cancelled
Close Unreleased for retry-policy panel, persistent queue, and self-contained backups; pin compose and docs to 1.3.1.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 22:19:42 +03:00
mixandCursor 41c3e6e896 Persist Postfix queue and ship self-contained full backups.
Move the mail queue under /data so recreate no longer drops deferred mail, and archive data/, compose, .env, and certs/ together for restore on a fresh host.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 14:36:55 +03:00
mixandCursor 5598b62e4e release: 1.3.0
test / test (push) Has been cancelled
Close Unreleased for security, tests, docs, and release CI; pin compose and docs to 1.3.0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:51:29 +03:00
mixandCursor 71048922b9 release: 1.2.5
test / test (push) Has been cancelled
Close Unreleased for the rate-limit form polish; pin compose and docs to 1.2.5.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 01:04:19 +03:00
mixandCursor 6671bd3393 release: 1.2.4
test / test (push) Has been cancelled
release / prepare (push) Has been cancelled
release / build (amd64, ubuntu-latest) (push) Has been cancelled
release / build (arm64, ubuntu-24.04-arm) (push) Has been cancelled
release / merge (push) Has been cancelled
Close Unreleased for the level-2 rate-limit semantics change and DNS Type
field height fix; pin compose and docs to 1.2.4.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 23:21:28 +03:00
mixandCursor 601e183e0c release: 1.2.3
Close CHANGELOG [Unreleased] as 1.2.3; bump the compose pin and image references. Includes domain detail layout polish and Domains add-row.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 22:52:19 +03:00
9420e30d6f release: 1.2.2
test / test (push) Has been cancelled
Close CHANGELOG [Unreleased] as 1.2.2; bump the compose pin and image references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 00:34:09 +03:00
4015ecd70d release: 1.2.1
test / test (push) Has been cancelled
Close CHANGELOG [Unreleased] as 1.2.1; bump the compose pin and image references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 23:03:54 +03:00
bc005dea85 release: 1.2.0
test / test (push) Has been cancelled
Close CHANGELOG [Unreleased] as 1.2.0; correct missing entries; bump the
compose pin and image references.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 17:39:11 +03:00
mixandCursor e34d81498e release: 1.1.0
Close CHANGELOG [Unreleased] as 1.1.0; remove erroneous DMARC entries
retroactively copied into older sections; bump the compose pin and image
references.

Co-Authored-By: Cursor <cursoragent@cursor.com>
2026-08-10 23:30:46 +03:00
mixandCursor c713f51f24 release: retag 1.1.0 as 1.0.1
test / test (push) Has been cancelled
The cut is docs and packaging plus one bugfix — PATCH, not MINOR. v1.1.0 was never tagged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 21:42:40 +03:00
mixandClaude Sonnet 5 0ed36e9624 release: 1.1.0
test / test (push) Has been cancelled
Close the [Unreleased] section as 1.1.0 and move the deploy pin, the README
trial command, and the guide's stated pin to that tag.

Corrections to the section while cutting it:

- The GHCR "unknown blob" retry was filed under Unreleased, but the commit
  carrying it is what v1.0.0 points at. Moved into the 1.0.0 section.
- docs/plans/ and the roadmap's restructure into a 1.x+ tracker were missing
  entirely; the plans were described only as "translated", which reads as if
  they predated this release. Filed under Added.
- docs/product.md's reframing of the future line -- agreed 1.x+ extensions,
  inbound relay targeting a MINOR by default, approval needed only for
  candidates -- was unrecorded.
- The Account/Settings rename also touched the operator guide.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-09 23:39:53 +03:00
178424eaf6 release: cut 1.0.0
Pin compose and local trial to ghcr.io/mixeme/selfpost:1.0.0, close the
CHANGELOG cut, and retire implementation-plan / v1.x-closure-plan.

Includes the post-cut startup fixes needed for a green release e2e gate:
root-owned TLS copies for postfix check, maillog_file_prefixes for /data,
hostname gate and traversable /data, panel /healthz before setup, and
setup-token / TempDir reclaim via docker exec.

Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-09 12:33:29 +03:00
1f548dfc7a fix(logtail): keep mail.log in /data and reconcile stuck rows (v1.x closure phase 2)
Move the delivery log from the ephemeral /var/log to /data/log/mail.log so
the lines that resolve a queued send-log row survive a container recreate.
postlogd writes it as postfix, the panel reads it through the selfpost group
(dir 2750, file 0640, normalised every start); backups exclude log/.

Close the residual gap with a queue sweep: rows queued for over two minutes
whose id postqueue -p no longer lists are marked bounced. The sweep waits
until the tailer has read the log to its end and does nothing when the queue
cannot be listed, so a message in flight is never touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 11:35:27 +03:00
mixemeandCursor 5a5b642eac docs: split README into overview and operator guide for release
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 10:36:24 +03:00
mixandCursor 7f24b2923c docs: D3 backup tar path, D4 README/compose fixes, D5 plan sync
Document stopped-container tar backup with WAL warning and manifest
consumption; refresh status banner and port-587 note; align
implementation-plan B.1 with actual session behaviour on password change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 00:28:59 +03:00
mixandCursor b40e14d65c docs: D1 Operations/Rate limiting and D2 env reference (README)
Close documentation-plan findings 1-3 and part of 10: panel operations
guide, two-level rate limits, public env table with TRUSTED_PROXY_CIDR
warning, and compose wiring for the proxy CIDR variable.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 00:26:03 +03:00
mixandClaude Opus 5 4003a299a6 dnscheck: query recursive resolvers directly, not the system one
The PTR check reported a correctly published record as wrong. The lookups
went through the container's resolver (127.0.0.11) which forwards to the
host's systemd-resolved, and systemd-resolved synthesises the reverse
lookup of the machine's own addresses from the local hostname rather than
asking public DNS. On the production host that meant

    203.0.113.10 -> provider-assigned-hostname (does not match)

while public DNS has had 203.0.113.10 -> selfpost.example.com all along.

These checks exist to report what a receiving mail server sees, so they
now dial recursive resolvers themselves, defaulting to 1.1.1.1, 8.8.8.8
and 9.9.9.9 and overridable with SELFPOST_DNS_RESOLVERS. The e2e stand
sets it to its CoreDNS, which the `dns:` directive alone no longer covers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 21:53:24 +03:00
mix 419af4af95 deploy: pin compose image tag to the actual 0.1.0 release
It was still pointing at 1.0.0, a version that was never tagged or
released (CHANGELOG and git tags top out at 0.1.0/v0.0.1).
2026-08-03 21:25:03 +03:00
mixandClaude Sonnet 5 538a4b6603 feat: implement B.1 — persist login sessions in SQLite with sliding idle timeout
Sessions move from an in-memory map (absolute 12h TTL) to a `sessions`
table (migration 0002), storing only the SHA-256 of the token. Expiry is
now a sliding idle window (PANEL_SESSION_IDLE_DAYS, default 7, no
absolute cap), extended at most once an hour and never by the
monitoring screens' background polling (GET + HX-Request), so a
forgotten open tab doesn't keep a session alive indefinitely. A login
now survives a container restart or redeploy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-02 23:21:49 +03:00
mix cc01e44ab8 deploy: add CAP_KILL so opendkim reload on domain add/remove works
supervisord runs as root inside the container but cap_drop: ALL still
blocked it from signaling opendkim (a different uid) — cross-uid
kill() checks CAP_KILL regardless of the caller's uid. Domain add was
failing in prod with "unknown problem sending sig opendkim ...
PermissionError: Operation not permitted".
2026-07-15 22:30:20 +03:00
mixandClaude Opus 4.8 fe2844441b deploy: add CAP_FOWNER/CAP_FSETID so entrypoint permission-fix works
Bringing up the production Apache stack for real surfaced a latent bug
in the Phase 10 hardening: cap_drop: ALL with only NET_BIND_SERVICE/
CHOWN/SETUID/SETGID/DAC_OVERRIDE left the root startup phase unable to
chmod the /data dirs it had just chowned to the panel user (needs
CAP_FOWNER) or set their setgid bit (needs CAP_FSETID). The container
crash-looped on "chmod: Operation not permitted". Phase 10 never caught
this because its compose up hit a port conflict before full boot.

Add FOWNER and FSETID to cap_add and document what each capability is
for. Verified: container now starts clean under the hardened compose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 22:18:44 +03:00
mixandClaude Sonnet 5 61f525e2d7 Phase 10: deployment (Apache compose + proxy fragments, CI release) + docs
- deploy/docker-compose.yml: pinned-tag ghcr image, hardened (cap_drop ALL +
  minimal cap_add, no-new-privileges, panel bound to 127.0.0.1 only). Apache
  itself runs on the host (spec 10.5), fragment at deploy/apache/.
- Alternative reverse-proxy fragments: nginx (+certbot sidecar), Caddy
  (automatic ACME), Traefik (+acme.json PEM extraction script).
- .github/workflows/release.yml: tag-triggered ghcr.io publish, version piped
  from the git tag into both the binary ldflags and the image tag (spec 10.1).
- Closed a gap from Phase 1: logrotate was installed but never invoked;
  wired up build/logrotate-mail.conf + logrotate-loop.sh + a supervisor
  program (copytruncate, since postlogd holds mail.log open with nothing to
  signal on rotation).
- README rewritten: site requirements checklist, reverse-proxy comparison,
  DNS setup (server- vs domain-level), IP warmup, backup/restore vs domain
  export/import, fixed-tag rationale, machine requirements.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 21:23:19 +03:00