id: node title: A host proves what it is once, and issues credentials to the sessions on it status: open crates: [didbot-attest, didbot-hook, didbot-hookd] dependsOn: [credentials, attestation] exitCriterion: > A machine holding a node credential issues a session credential to a hook that asks for one, a machine without one is refused, and many hooks starting at once produce one provisioning request per session rather than one per hook. #
node #
credentials states the shape and names this as its first item: the shape mirrors SPIFFE — a node attests once, a local component issues credentials, and the workload talks to the server itself. This epic is that local component, split out because it is a process with a lifetime, an install story and a privilege boundary of its own, and those do not fit inside a bullet.
Why a daemon rather than the hook #
Both were on the table. The hook is the simpler answer and it does not survive contact with a busy machine.
A hook is a short-lived process spawned per tool call. Putting the node credential in it means every one of those processes reads the credential off disk, so the blast radius of anything that can observe a process is the whole node identity rather than one session. And a machine running several sessions and their subagents fires many hooks at once, each asking the server for an account, so provisioning arrives as a stampede with nowhere to be coalesced.
A long-lived component fixes both: one process holds the credential, and it is the natural place to serialise and rate-limit what reaches the server.
The cost is honest and worth writing down: it is another thing to install, supervise and version on every agent host, and deploy already notes that agent hosts are their own deployment. dev-setup's service/profile/binding model is where it has to fit.
What it holds, and what that is worth #
The local transport is a privilege boundary #
Boundaries it must not move #
A layer this is not #
Done #
Nothing closed yet.