Neither review (read-only agents, Opus and Fable, over ae06d8b..815236a) has
a Critical or High finding. What they found and what was done:
- A stranger on port 25 could grow the table: every queue-id line of inbound
mail was stored and pruned an hour or more later, his own message-id among
them. The tailer now leaves out the lines of a queue id that
postfix/inbound/smtpd opened, until qmgr removes the message or another
listener opens one under the id; 4096 such ids are remembered.
- A commit, so a disk sync, per stored line on the connection the journal
milter shares, and a backlog written line by line. Lines are now written in
one transaction per pass over the log, 500 at most, before the read offset
is saved (InsertSendLogLines; follow() calls the keeper's flush).
- The prune asked the send log once per line and ran hourly. It asks once per
queue id and runs at start and with the six-hourly sweep; with port-25 mail
left out there is little without a row to wait for.
- The 4096-byte cut could split a character; it keeps characters whole.
- security.md did not say that a backup now carries these lines, and the plan
said the time window excludes a reused queue id where it only limits the
chance to the row's queue lifetime. Both are corrected; the collision is
recorded as an accepted risk with its trigger.
- Tests that did not prove their comments: the prune is now tested with the
sweep, a rejected row next to the prune, batching and the change from
catching up to following, port-25 mail left out and its id reused.
Left as found and written down in the plan: lines the tailer never reads
(a file rotated away while the panel was down), and a message in the queue
across the upgrade, which shows only its later lines.
On the test server, for this tree: go vet clean, go test ./... 22 packages
ok, make e2e ok.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Migration 0003_send_log_lines.sql, DDL only: queue id, the line's own time,
the line. No reference to send_log, because a message's first lines are
logged before the journal milter writes its row and one queue id is several
rows.
Store methods: InsertSendLogLine; InsertSendLogLineOnce for lines the tailer
reads twice after an unclean stop; SendLogLines, which gives a row the lines
of its queue id dated from an hour before the row to the queue lifetime after
it, so a later message under a reused id is not shown; PruneSendLogLines,
which deletes lines whose queue id has no send-log row - the whole of the
table's retention. A line is cut to 4096 bytes.
Nothing writes or reads the table yet. Checked on Windows: go vet and
go test ./internal/store pass, with the schema test pinning the new table,
its index and user_version 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>