hub v0.103.0 — a host can read the packages we kept for it (R-311)

ListSupersededEscrow had zero production callers for nineteen days. It is the only
reader of a retained identity_blob, so the retention shipped in v0.93.0 was
material the product could not reach - proven on the fixture 2026-08-12, where a
code that opens a retained package was answered as a code that opened nothing.

New GET /api/v1/hosts/<id>/escrow/retained: self-scoped exactly as the current-row
GET, same recovery-mode gate, same audit event written BEFORE the bytes leave,
capped at 16.

Rows with a NULL identity_blob are WITHHELD and returned as unopenable_count.
They retain the PBS key, not the repository password, so they can never open what
the caller is asking about; serving them would have the agent try packages that
cannot succeed and would let the screen claim an earlier package is openable on
exactly the boxes the original defect hurt. The count is returned because their
existence is load-bearing and underivable.

The trade, stated rather than waved through: the hub still cannot read any of it -
sealed bytes in, sealed bytes out, no decrypt path, no recovery code ever held.
What widens is volume, bounded by self-scope, the recovery-mode gate and the cap.

The response is a NAMED TYPE, not a map, so the wire-contract gate can resolve it;
the wire is declared as a fourth ROOT and the gate now checks 182 tags rather than
174. A positive control shows that check is name-presence, not decodability -
filed as R-315 rather than reported as coverage.

Six tests through the real endpoint; four red-proofs asserted applied.
This commit is contained in:
2026-08-12 18:43:07 +02:00
parent 1d4985d87c
commit 6362bb6cb6
7 changed files with 443 additions and 16 deletions
+32
View File
@@ -1,3 +1,35 @@
## v0.103.0 — a host can read the packages we kept for it (2026-08-12, R-311)
**`ListSupersededEscrow` had zero production callers for nineteen days.** It is the only reader of a
retained `identity_blob`, and nothing called it — so the retention shipped in v0.93.0 was material the
product could not reach. Proven on the fixture 2026-08-12: a recovery code that opens a retained
package (unsealed by hand, and it restored planted files byte-identical from a store the box itself
could no longer open) was answered by the product as a code that opened nothing.
**New: `GET /api/v1/hosts/<id>/escrow/retained`** — the retained siblings of the existing
`/escrow` row, self-scoped to the asking host exactly as that one is, behind the same §8.3
recovery-mode gate, and audited with the same `escrow_blob_served` event BEFORE the bytes leave.
**What it will not serve.** Rows whose `identity_blob` is NULL — every pre-v0.93.0 row — are withheld
and returned as `unopenable_count` instead. They retain the PBS datastore key and not the repository
password, so they can never open what the caller is asking about; serving them would have the agent
try packages that cannot succeed and would let the recovery screen claim an earlier package is
openable on exactly the boxes the original defect hurt. The count is returned because their EXISTENCE
is load-bearing and underivable: on such a box a correct old code opens nothing, and the honest reason
is a defect of ours rather than the customer's typing.
**The trade, stated rather than waved through.** The hub still cannot read any of it — sealed bytes in,
sealed bytes out, no decrypt path, no recovery code ever held. What widens is VOLUME: a host key that
could fetch one opaque package can now fetch N. Bounded by the same self-scope, the same recovery-mode
gate, and an explicit `retainedEscrowServeCap` (16) so a long supersession history cannot become an
unbounded read.
Pinned by six tests driven through the real endpoint, with four red-proofs each asserted to have
applied: serving the CURRENT row instead (count 2→1), dropping the unopenable guard (count 1→2,
unopenable 1→0), dropping self-scope (403→200), and collapsing the route suffix (count 1→0).
---
## v0.102.0 — the hub refuses to vouch a version that cannot be installed (2026-08-09, R-273)
**The guard owed since 2026-08-09 morning.** Agent v0.128.0 had been published as a package and never