A hive reads its agents' credentials with its own certificate, and nothing
said which paths that certificate may read, so the read half of a delivery
answered 403.
The grant is wide on purpose. An agent's path does not name the hive
hosting it -- agents move -- so a per-hive grant has to be an enumeration
the controller re-emits whenever the roster changes, and an enumeration
that can drift or land out of order advertises a boundary it does not
hold. A wide grant that says what it is beats a narrow one that only looks
narrow. mara's call, on the PR: rather a too-lax scope than one that
pretends to be strict.
What that buys, beyond honesty: the document is identical for every hive
and depends on nothing, so it is written once at startup beside the rest of
a hive's provisioning instead of on every declaration. No derived state, no
re-emission, and the ordering hazard that came with one stops existing.
What still holds is read-only. A hive cannot write an agent's credential,
so it cannot hand itself an agent's identity, and the grant reaches nothing
in the store outside the agent-credential prefix.
The fact is documented where someone meets the boundary rather than only in
this message, and the two ways to narrow it later -- scope per hive, or
give agents their own store identity -- are tracked.
The controller writes an agent's credential; the hive reads it back with its
own token. Nothing says which paths that token may read, so the read half of
a delivery has no identity at all and answers 403.
This is the pure half of the fix: the policy text is a function of a hive name
and its agent set, so the shape can be asserted with no store to talk to.
One stanza per hosted agent rather than a prefix grant. An agent's credential
path does not name the hive hosting it -- deliberately, since agents migrate --
so "this hive's agents" has no prefix expression and must be enumerated. The
grant is read-only: the controller mints these and never reads one back.
The paths come from MOUNT and AGENT_PREFIX rather than literals, so the policy
cannot drift from the module that builds the paths it grants.
checked_segment runs before any name reaches HCL. That is policy injection
rather than path traversal -- a name can close a stanza and open a wider one --
so the test carries a real injection string, paired with the reachable-charset
control that stops it passing by refusing everything.
An empty agent set renders an empty policy, which grants nothing.