Owner, 2026-10-18: "Надо почистить документы от устаревшего и сделанного." - Deleted, shipped: plans/delivery-log-storage, from-header-binding, inbound-antispam-panel, inbound-upstream-result, panel-dkim-signing, panel-notifications, and the own-mta-builds analysis (its three rows shipped as 2.6.1, 2.7.0, 2.8.0). What outlived them moved first: when a filter-list counter is worth a migration, and the notifications' "when" table, into architecture.md; the sender check's own-process trigger into security.md. Code comments that cited them point at architecture.md or security.md; CHANGELOG keeps their names, unlinked. - Kept, marked closed: audit-2026-10-12, assumptions-2026-10-14 and security-audit-2026-10-17 - the code cites their finding ids in 50-84 files. Their file:line refs are of the tree each audited. - roadmap.md: open work only - stage H of the security audit (the owner's), go-milter-upstream (the owner posts), and queue-table, which the redesign left undecided and only its own shipped section still named; Dropped and Ideas as they were. 2.x versioning instead of 1.x. - development.md: the 47-row planned-version-cuts table (history, in the CHANGELOG) gone; a stage's last step now removes its roadmap item and plan. Docs map lists the closed audits. - panel-redesign.md: the done checklist out; Room for roadmap items says which drawn screens are unbuilt and why. - Stale facts fixed: schema-migrations.md and architecture.md at schema 14 (0013's users columns and user_links, 0014's secure TLS mode); security.md's accepted risk "profile saved without the password" - the e-mail asks for it since 2.14.0. Tests: go build, go vet; go test of cmd and the touched internal packages pass except logtail's three Windows-bound rotation tests (rename of an open file; CI is Linux). Only comments changed in Go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+28
-14
@@ -177,9 +177,15 @@ smtp/inet` removes Debian's stock smtpd). Outbound delivery still uses the
|
||||
off the journal on each view of Filter lists - the rows a list decided (the
|
||||
deny rule's reply, the allow rule's symbol, which the journal milter keeps
|
||||
ahead of its six-symbol cap) matched against the entries as rspamd matches
|
||||
them. When that stops being good enough is written in
|
||||
[plans/inbound-antispam-panel.md](plans/inbound-antispam-panel.md) § Filter
|
||||
list usage.
|
||||
them. So a count reaches back as far as the journal does, a row counts for
|
||||
every fitting entry of the deciding list, and a count is of messages,
|
||||
roughly. A counter kept with each entry (hits and last hit on
|
||||
`inbound_spam_list`, a migration) is worth it when any one of these holds
|
||||
(owner, 2026-10-12): the journal is cut by its 10 000-row cap rather than by
|
||||
age (the tailer's prune keeps removing rows while the oldest row of
|
||||
Inbound › Log is younger than the retention); the retention is set short,
|
||||
towards its 7 days; counts are wanted that outlive the journal; or the page
|
||||
is slow - past a few hundred entries, measure it.
|
||||
**Inbound relay** (`INBOUND_RELAY_ENABLE=true`): accepts only
|
||||
`relay_domains` + `relay_recipient_maps` (`reject_unauth_destination`,
|
||||
`reject_unlisted_recipient`). Maps under `/data/postfix/` (`relay_domains`,
|
||||
@@ -235,8 +241,7 @@ smtp/inet` removes Debian's stock smtpd). Outbound delivery still uses the
|
||||
The panel signs outgoing mail itself, in a role of its own, `dkim-signer`
|
||||
([cmd/panel/signer.go](../cmd/panel/signer.go),
|
||||
[internal/milter/sign.go](../internal/milter/sign.go)), since `2.8.0`; until
|
||||
then OpenDKIM did, in a program of its own
|
||||
([plans/panel-dkim-signing.md](plans/panel-dkim-signing.md)).
|
||||
then OpenDKIM did, in a program of its own (CHANGELOG `[2.8.0]`).
|
||||
|
||||
- **Keys** — `/data/dkim/keys/<name>/<selector>.private`, PKCS#1 PEM, one
|
||||
for each sending domain and one for the server's own name
|
||||
@@ -353,7 +358,7 @@ milter, § Postfix above, port 25):
|
||||
(`300s` / `4000s` / `5d` / `0`) with a muted note on the box.
|
||||
2. **sender check** — unix socket `SENDER_CHECK_SOCKET`, first in the chain
|
||||
and **strict** (`default_action=tempfail`): § Sender check below.
|
||||
3. **DKIM signer** (`2.8.0`, [plans/panel-dkim-signing.md](plans/panel-dkim-signing.md))
|
||||
3. **DKIM signer** (`2.8.0`, § DKIM signing)
|
||||
— unix sockets `DKIM_SIGN_SOCKET` (applications' mail on 465/587, signed
|
||||
with the key of the domain the SASL login belongs to) and
|
||||
`DKIM_SIGN_LOCAL_SOCKET` (the panel's own mail, `non_smtpd_milters`,
|
||||
@@ -397,8 +402,8 @@ generates pass no milter.
|
||||
### Sender check
|
||||
|
||||
[internal/milter/sender.go](../internal/milter/sender.go) holds a message's
|
||||
`From:` header against the application that sends it
|
||||
([plans/from-header-binding.md](plans/from-header-binding.md)). Postfix holds
|
||||
`From:` header against the application that sends it (since `2.4.0`;
|
||||
`Sender:` too since `2.8.0`). Postfix holds
|
||||
the envelope sender against the SASL login, and the signer signs with the key
|
||||
of the login's domain (§ DKIM signing): the signature's `d=` is that domain,
|
||||
and a receiver holds it to the domain of `From:` (DMARC, RFC 7489 § 3.1.1).
|
||||
@@ -504,10 +509,15 @@ the hole it closed was the same.
|
||||
[internal/notify](../internal/notify/notify.go) reports four things to the
|
||||
users of the panel: a sending domain's DNS record that stopped matching, a
|
||||
level-2 rate limit that refused mail, the TLS certificate about to expire, a
|
||||
message more than three days in the queue. The rules of when a message goes
|
||||
out are the owner's and are written in
|
||||
[plans/panel-notifications.md](plans/panel-notifications.md) § When a message
|
||||
is sent; the operator's view is guide § Notifications.
|
||||
message more than three days in the queue. The operator's view is guide
|
||||
§ Notifications. When a message goes out is the owner's rule (2026-10-12):
|
||||
|
||||
| Subject | Sent | "Cleared" message |
|
||||
|---|---|---|
|
||||
| DNS mismatch | when it has held for an hour (two checks, below); a resolver that did not answer is not a mismatch | the same way back |
|
||||
| Certificate | 14 days before expiry, and 3 days before if it is still the same certificate | none |
|
||||
| Level-2 limit refused mail | at the first refusal for a sending domain, then at most once a day for it | none |
|
||||
| Queue older than three days | when such a message appears | one, when none is left |
|
||||
|
||||
- **Readings.** Once an hour, starting three minutes after the process does:
|
||||
every sending domain's records through a DNS checker of the notifier's own,
|
||||
@@ -1179,7 +1189,7 @@ full backups also archive the operator's `./certs` PEM files when present.
|
||||
### Database
|
||||
|
||||
The schema is an embedded baseline, `internal/store/migrations/0001_init.sql`,
|
||||
and the migrations after it (`0002` to `0012` today; the next is `0013`),
|
||||
and the migrations after it (`0002` to `0014` today; the next is `0015`),
|
||||
applied by `store.Open` in file order; `PRAGMA user_version` counts the files
|
||||
applied and
|
||||
`PRAGMA application_id` is `0x53503230` ("SP20"). There is no 1.x migration
|
||||
@@ -1198,7 +1208,11 @@ The tables that carry the panel's model:
|
||||
the user's own address, edited on Account and set by the global role on the
|
||||
user form; `all_domains` / `all_inbound_domains` widen a *domain* user to
|
||||
every domain of that direction, including ones added later (with a flag set
|
||||
the assignment rows are ignored).
|
||||
the assignment rows are ignored); `email_confirmed_at` is when `email` was
|
||||
confirmed, `''` while it is not (v13).
|
||||
- **`user_links`** — the one-time links that confirm an address and reset a
|
||||
password (v13): the token's SHA-256, `purpose`, the address it went to, its
|
||||
expiry; one live link per user and purpose.
|
||||
- **`user_domains`** and **`user_inbound_domains`** — the outbound and inbound
|
||||
domains assigned to a *domain* user. Separate tables: the same name on both
|
||||
is two assignments, and a sending domain gives no inbound access or the other
|
||||
|
||||
Reference in New Issue
Block a user