Files
beardthelion e6cfb588c2 fix(review): bound token key, rule bytes, and the discovery load
An external review found three costs that outlive the request or the boot
that paid for them.

The scan token was sealed with a key minted per boot, so a restart or a
rolling deploy made every outstanding continuation undecryptable and the
handler reads an unopenable token as absent. A caller walking a large
inventory could be sent back to the front indefinitely. The key is now
derived from the node's Ed25519 seed with HKDF, so it is the same after a
reload, under a distinct salt and info string so it is not the signing
key and cannot be turned against signatures. Rotation is a version bump in
the info string. An earlier comment rejected derivation on the grounds
that surviving restarts bought nothing; that is exactly what it buys, and
the comment now says so.

The rule byte ceiling was summed after the page had already been fetched,
so it truncated the request without bounding the work: the allocation and
the transfer had happened. The budget now bounds the query itself. The cut
lands on a repo boundary, which is not cosmetic: a mid-repo cut leaves a
partial rule set, and a partial rule set at the gate is indistinguishable
from a repo with no rules, so it would fail open and serve an object those
rules withheld. Only fully loaded repos are gated. The query always admits
the first rule-carrying repo of a page, so the bound is the budget plus
one repo rather than the budget; without that a single oversized repo
would put the cut at the cursor and every retry would reproduce the same
page.

The old check carried a not-exhausted condition so a short final page
could not turn a complete scan into a permanent shed. That condition is
gone because the taint no longer keys on page length; it keys on the query
having left repos unloaded. A short final page that fits produces no cut
and still 404s, and one that is cut is honestly incomplete and resumable.

Discovery paged the whole repo table and stat'd every warm repo to pick a
sixteen candidate window. Its comment called that acceptable because the
cost was paid once, which was true when the sweep ran once per boot. This
round made the sweep periodic, so an upgraded node with one unrepairable
row paid a full table scan on every run, forever. The load now stops once
the window is full. It collects one candidate past the cap so that running
out still distinguishes a warm set that fits from one that does not, which
is what the reset arm needs now that the total count is no longer known.
Ordering is unchanged, so an attacker who mints repos still cannot move
the boundary.

Verified by mutation: a per-boot key strands a continuation across a
restart, using the signing seed directly defeats the domain separation,
summing bytes after the fetch materializes the whole page, paging to
exhaustion buys pages the window cannot use, and reading the fit under cap
from the window length loses the reset.
2026-08-12 23:38:15 -05:00
..