Fix seven M3a spec review findings in policy and the audit chain

Policy: a deny grant must apply at every taint; a result's label is
combined over all matching grants and the longest matched path wins
within a mode; a grant of / is invalid.

Audit: a recovered line need not fail to parse; the writer never goes
back to an earlier day's file; --accept-break verifies the whole log
and the break record's fields are all checked, with a seq counted from
lines; the Approval record carries the re-decision's grant and state;
calls with no Result are reported.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-18 22:13:28 -07:00
co-authored by Claude Fable 5.1
parent 180ef28833
commit 3492734434
3 changed files with 154 additions and 41 deletions
+21 -7
View File
@@ -33,8 +33,10 @@ bxctl grants check
It prints each problem as `<file>:<line>: <problem>`. Common ones: a misspelled field (unknown
fields are errors); `secret` set (not supported until M4); `patterns` set (not supported); `hosts`
on a file tool or `paths` on `http_fetch`; a relative path or one with `..`, `.` or `//`; a host
with uppercase letters, a port, or an IP address; a file name with characters outside `[a-z0-9-]`.
on a file tool or `paths` on `http_fetch`; a relative path or one with `..`, `.` or `//`; the path
`/` (grant the directories you mean); a `deny` grant whose `max_taint` is not `"secret"` (it would
stop denying once the session read a secret); a host with uppercase letters, a port, or an IP
address; a file name with characters outside `[a-z0-9-]`.
**Fix.** Edit the file, or move it out of `$GRANTS` while you work on it:
@@ -74,13 +76,19 @@ directory 0700). Do not edit, move or delete audit files to make space: that bre
## audit-chain-broken
**What you see.** `brokerd` refuses to start and prints `<file>:<line>: <what>`, then this entry.
Or `bxctl audit verify` prints the same and exits 1.
Or `bxctl audit verify` prints the same and exits 1. If the break is in an older file, only
`bxctl audit verify` reports it: at an ordinary start `brokerd` checks the latest file alone.
**Why.** A record does not parse, its `seq` is not the next number, its `prev` is not the hash of
the line before, or a file does not continue from the one before it. Something changed the log
after it was written: an edit, a deleted or reordered line, a file restored from a backup, a
partial copy. `brokerd` will not add records after a history it cannot vouch for.
There is one innocent cause. After a crash `brokerd` recovers a torn last line by itself
([audit-recovered](#audit-recovered)), but if it is killed a second time while writing that
recovery record, the log is left with a line that nothing describes. The failure is then within
the last three lines of the latest file, and the last line is cut short.
**Confirm.**
```sh
@@ -98,11 +106,14 @@ cp -a "$BOXMAKER_HOME/audit" ~/audit-copy-$(date +%F)
brokerd serve --config <path> --accept-break
```
`brokerd` appends an `accepted_break` record naming the file and line, and continues from there.
Nothing is repaired or deleted. The break stays visible in every later verification.
With this flag `brokerd` checks the whole log, not only the latest file, and appends an
`accepted_break` record naming the file and line of the first failure. It continues from there.
Nothing is repaired or deleted. The break stays visible in every later verification. One accepted
break covers everything between the failure and the break record, including any further damage
there, so look at that whole stretch before accepting it.
**Check.** `bxctl audit verify` prints `audit: ok` and lists the accepted break with its file and
line. A second failure later in the log needs its own accepted break.
line. Damage that happens after the break record needs its own accepted break.
## audit-recovered
@@ -112,7 +123,10 @@ It starts normally.
**Why.** The last record was being written when `brokerd` or the machine stopped, so the file ended
without a newline. That is expected after a crash, not an error. `brokerd` kept the partial line,
ended it, and wrote a `recovery` record holding its length and hash, chained from the last complete
record. The partial record was never acted on: records are written before the action they allow.
record. The partial line may even look like a whole record; it is not one, and the recovery record
takes its `seq`. It was never acted on: a record allows something only once it is on disk with its
newline. If the partial line was the `result` of a call, the call did run and its result was
never delivered; `bxctl audit verify` lists that call as unfinished.
**Confirm.** `bxctl audit verify` lists the recovery with its file and line.