aaa94120be0f2c64b03859b5ced697e009ca2b4e
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
98e09fef4a |
health: sockets dialled, the certificate Postfix serves, "deferred" only for the queue
test / test (push) Canceled after 0s
Audit 2026-10-12, stage 2c, "Health says what is served"; the pages of
ea8b6bf's mockup.
Sockets (A-38). A unix socket's file outlives a killed OpenDKIM or panel
and refuses every connect() (unix(7)); Health said "Listening" from a
stat. CheckSocket and CheckMilter's unix case now dial: refused is
"Nothing listens on the ... socket: the program that made it has
stopped", EACCES is unknown (the panel may not connect), anything else
"does not answer". The probe closes before a byte, which no milter logs:
libmilter takes the EOF as SMFIC_EOF without a line (sendmail
8.17.1.9-2+deb12u2, the image's libmilter1.0.1, libmilter/comm.c
mi_rd_cmd(), engine.c mi_engine() - its source is now in dev/upstream),
OpenDKIM's mlfi_close() has no connection to free, go-milter v0.4.1
returns quietly on io.EOF (session.go, HandleMilterCommands()). The page
polls every 5 s while in view, so this mattered.
Certificate (A-65). Postfix serves build/postfix-config.sh's copy,
refreshed by cert-reload every TLS_RELOAD_INTERVAL_SECONDS. The panel now
reads smtpd_tls_cert_file from postconf -h at its start (as the retry
policy) and TLS_RELOAD_INTERVAL_SECONDS; CheckServedCertificate checks the
served copy and, when the mounted file differs byte for byte (cert-reload's
own cmp -s), names it as Renewal: its expiry and mtime, two facts on the
card. Waiting is fine for the interval: a nearing expiry of the served one
is then no warning (an expiry still an error); past interval + 1 h it is a
warning pointing at cert-reload's lines. The notifier sends no expiry
notice while a renewal waits on time.
Wording (D-P1, D-P3, and the same for the spam filter): Sender check
note, help topics sender-check, upstream, filter, certificate - word for
word as in the mockup; the filter row's detail too.
Tests: stale socket file (Linux) refused for CheckSocket and CheckMilter;
CheckServedCertificate (same, waiting, late, expired + waiting, no served
path); intervalText; the card's facts; the notifier quiet while a renewal
waits, speaking once it is late. Stand (2026-10-15, make e2e, exit 0):
new health_shows_what_is_served - the three sockets dialled ok by the
panel's user; a certificate mounted without its key is refused by
cert-reload ("do not belong together") and the card shows the served
expiry, "Renewal expires" with the mounted one's, "Renewal mounted", and
"Postfix still serves the certificate above."; a matching pair is then
served again. go vet and go test ./... pass on the server (24 packages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
ce363f48dd |
logtail, health, panel: cite the source of what they rely on (assumptions part A)
Comments only, no behaviour changed. Each place the log tailer, Health and cmd/panel rely on another program now says where that is written, for the version the image runs: Postfix 3.7.11 (log_adhoc(), msg_logger_print(), smtpd_whatsup(), cleanup_milter.c, qmgr_active.c, postlogd.c, showq.c, pipe.c), logrotate 3.21.0, supervisor 4.2.5, proc(5). Measurements stay measurements and say where and when; test fixtures say whether they were seen or composed. Where the source contradicts the code the comment does not bless it but names the register entry (A-8 expiry, A-12 the ISO stamp no Postfix writes, A-15 the last recipient, A-21/A-23 sasl_username, A-33 postlogd's old inode, A-38 a socket file is not a listener, A-65 the certificate served); those are fixed in their own commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
f37424d9fc |
panel-redesign: the spam filter's socket is one of the milter sockets
The owner's decision of 2026-10-10, as the plan has it (Who sees what:
for the global administrator the filter's socket is one of the milter
sockets on Health and Overview). Written by Sonnet, reviewed by Opus.
- internal/health: CheckMilter probes an address in Postfix's two
forms. unix:/path is the existing stat; inet:host:port is a TCP
dial with a 1 s cap, closed at once, nothing written. An address in
neither form is reported as not checkable, never as fine. Details
are plain sentences ("No answer from antispam:11332 within 1 s.",
"Connection to ... refused.", "The name ... could not be
resolved.") and never the Go error text.
- Health: with the filter on, a third row "Spam filter - inbound"
with the configured address; it counts in the socket status and in
Overview's Milter sockets card. A filter that does not answer is an
error when INBOUND_ANTISPAM_MILTER_ACTION is tempfail (inbound mail
is deferred) and a warning otherwise (mail goes through
unfiltered), and the row says which. With the filter off nothing
changes.
- Both pages are global-only; a test checks a domain administrator
never receives the address.
The dial runs with the other checks on every request and poll of
Overview and Health (5 s active, 30 s idle, none while the tab is
hidden): a dead filter costs a poll at most 1 s, and a live one sees a
connect-and-close at that rhythm per open tab.
Not changed: fixtures and screenshots (the mockup draws two sockets),
help texts. The CHANGELOG line comes with the documentation step, which
is editing that file now.
go test -count=1 ./internal/health/... ./internal/web/... ./cmd/panel
ok (seven packages)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
60e08e802e |
release: 1.2.0: Panel 1.2.0-1.2.5: layout, RBAC, tests, GUI and CI fixes
[1.2.0] The second MINOR after 1.0.0: domain administrators with per-domain scope, a panel visual refresh on the SelfPost palette, and refinements to navigation and the send log. Upgrading runs one SQLite migration (`0005_panel_users`); the single administrator becomes a global user. Added: - panel: **domain-admin role** — global administrators manage panel users and Fixed: - Docker build: `LICENSE` is no longer excluded by `.dockerignore`, so the - panel: on signed-in pages with a narrow card (**Settings**, the user form) Changed: - panel: navigation session icons are distinct — **Settings** uses a gear, - panel: `/` redirects domain administrators to `/domains`; global users still - panel: **Sign out** in the navigation column uses the same type size and - panel: **visual style** brought in line with the SelfPost mark — brick accent - panel: the send log's **status is a badge**, in the same ok/warn/error/unknown … (1 more in CHANGELOG) [1.2.1] Panel refinements after 1.2.0: navigation icons, status-badge centreing, drill-down back-link placement, and safeguards for the sole global administrator. Upgrading is a tag bump; no migration. Fixed: - panel: status badge text sat low in the box (and below the heading or label - panel: the **Users** navigation icon was two full silhouettes with staggered - panel: the **Settings** navigation icon was a sun-with-rays (circle plus - panel: the user create/edit form placed «Back to users» at the bottom of the Changed: - panel: the user edit form disables role change and delete for the only global - panel: the user create/edit form hides **Assigned domains** when the role is - panel: **Settings** shows panel credentials and DMARC aggregate reports side [1.2.2] Status page layout after 1.2.1: paired cards in a wide column, denser machine and check copy, and a user-form checkbox fix. Upgrading is a tag bump; no migration. Changed: - panel: **Status** is wide again so paired cards fill the column. Layout: Fixed: - panel: cards inside `.split` used `margin: 0 auto`, which in a CSS grid - panel: on the user create/edit form, **Assigned domains** checkboxes stacked [1.2.3] Domain detail layout and panel polish after 1.2.2. Upgrading is a tag bump; no migration. Changed: - panel: the domain detail page is wide with paired cards (DKIM+SPF ‖ DMARC; - panel: Domains list — **Add domain** sits beside the domain field; the - panel: page URLs, browser titles, and headings are aligned — **Settings** is Fixed: - image: `mail.log` rotation no longer silently stops when the build context [1.2.4] Level-2 rate-limit semantics inverted after 1.2.3, plus a small DNS field height fix. Upgrading is a tag bump; no migration. Changed: - rate limiting (level 2): domain ceilings apply to every client IP (no IP Fixed: - panel: on the domain DNS status grid, the Type (TXT) field height matches [1.2.5] Rate-limit form polish after 1.2.4. Upgrading is a tag bump; no migration. Changed: - panel: rate-limit forms refined — level-1 backstop as muted copy on the Squashed CHANGELOG sections: [1.2.0], [1.2.1], [1.2.2], [1.2.3], [1.2.4], [1.2.5]. Full detail: CHANGELOG.md (1.2.0, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5). |
||
|
|
8ce050ef7f |
release: 0.1.0: Phases 0-11: panel, Postfix/DKIM, milter, backup, Docker image
Initial feature-complete implementation of the v1.0 specification (phases 0-11 of `docs/implementation-plan.md`). Added: - Panel (Go, single static binary) with SQLite persistence, one-time - Domain management with per-domain DKIM (RSA-2048, generated in pure Go) and - Application (sender identity) management: SASL credentials via `sasldb2`, - Full Postfix relay config generated from env at container start: SMTPS 465, - Journal milter (pure Go, `go-milter`) recording every send to `send_log`; … (6 more in CHANGELOG) Full detail: CHANGELOG.md (0.1.0). |