Watch
0
0
Fork
You've already forked hyperhive
0
Commit graph hyperhive/swarm-secret-client/src/policy.rs
Author SHA1 Message Date
atlas
2979fcf5d5 swarm-secret-client: give the store one namespace instead of one prefix
The crate had a single path convention and it was per-agent:
`swarm/agents/<agent>/matrix/<account>`. The secrets still to move into the
store do not fit it — one belongs to a hive, one to a swarm service, one to
the controller itself — so each would have picked its own shape, and each
would have been a separate grant to get wrong.

mara ruled the scheme on the epic: `swarm/<kind>/<name>/<secret>`, over
`agents`, `hives`, `services` and `controller`. This lands it.

`Kind` is an enum rather than free strings for one reason: the store's grant
is written in nix and cannot be reached from Rust, so a misspelled kind is a
403 at provision time and not a compile error. `Kind::ALL` lets a test
enumerate the set instead of restating it, which is what makes adding a kind
a deliberate edit rather than an accidental grant.

Note `Kind` sits beside `checked_segment`'s existing `kind` argument, which
means something else entirely — the label of the name being validated. They
are not the same concept and should not be merged.

Nothing about the rendered policy changes. `policy::render` still grants read
on the agent kind alone; the other kinds are absent on purpose, because what a
hive may read of its own kind is a boundary question and not a consequence of
the namespace growing. The controller's write grant likewise stays scoped to
`agents/` — it widens when a path outside it gains a writer, not when the
kinds are declared.

Verified: `cargo test -p swarm-secret-client` 23 passed, 0 failed. The two
tests pinning the rendered strings (`the_document_grants_read_over_the_whole_agent_prefix`
and matrix's path assertion) still assert the same literals they did before,
which is what shows this is a faithful port rather than a reshape. `nix fmt`
710 emitted, 10 formatted, 0 changed; the three scripts/check-*.sh lints pass
with the change staged. No reference to the removed `path::AGENT_PREFIX`
survives in the crate or in nix — checked with a scoped pattern, because the
unqualified name also belongs to hive-host-sock's container prefix and greps
for it are answering a different question.
2026-09-11 20:19:47 +02:00
atlas
3752482524 swarm: let every hive read every agent's credential, and say so
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.
2026-09-09 18:40:41 +02:00
atlas
7792b62609 swarm-secret-client: render a hive's read policy, and refuse names that inject
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.
2026-09-09 18:40:41 +02:00