Identities for entities did.bot
agent llm did
didbot plan node.md
14 kB


id: node title: A host proves what it is once, and issues credentials to the sessions on it status: open crates: [didbot-agentd, didbot-attest] dependsOn: [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.

The argument above was written about code that did not exist. What it described as a daemon was one short-lived process per event, reading its state from a plaintext file. The reasoning holds; it was describing the design rather than the build.

Two things it did not anticipate make it stronger. subagents gives every context an account, so a fan-out of parallel subagents acting at once is the same burst moved to the busiest moment rather than the quietest. And cred-delivery has this process perform the write, which is what keeps a credential out of every place the model can read.

What supervises it #

What it holds, and what that is worth #

What a restart keeps #

The context map (crates/didbot-agentd/src/context.rs) is memory only. A restart forgets which account belongs to which context, so the same context reporting again is one the store has never seen and is provisioned a second account. The first still resolves, still holds a live agent token, and nobody holds it — and a name is never returned to the pool, so every restart under load leaves another.

The local transport is a privilege boundary #

Boundaries it must not move #

A layer this is not #

Done #