Identities for entities did.bot
agent llm did
didbot plan dev-setup.md
11 kB


dev-setup #

local-dev is the loop once you are in it: five terminals and a swarm. This is getting there. The gap is not the scripts, it is everything around them — a certificate, a name, an edit to the user's harness configuration, and a restart before any of it takes effect.

The shape is a command that asks rather than assumes. It touches a person's settings file and possibly their trust store, so it says what it will do, does the parts it may, and stops with a specific instruction for the parts it may not.

What the shape turned out to be #

Three tables in ~/.config/didbot/stack.toml, read by didbot-stack: a service is one process, a profile names one service per role, and a binding maps a directory to a profile.

A whole stack is the wrong unit. The composition that comes up first is two record hosts over one personal data server — so that rebuilding one does not interrupt the other — and five-processes-or-nothing cannot say that. Naming services per role makes sharing what happens when two profiles name the same one.

The directory is the key because it is the only identifier every consumer already has: a hook payload carries it, a script has one, and the harness keys its own per-directory configuration on the same absolute path. Resolution is longest-prefix, and a directory nobody bound falls back to default, which is what lets a worktree created five minutes ago work with nobody registering it.

A machine with no configuration file behaves exactly as one whose file names today's ports, and a test reads scripts/dev-*.sh and fails if the two ever disagree.

Where accounts come from, and the bootstrap #

Hooks are captured at session start, so a session that runs the setup command is never itself hooked — and neither are its subagents, which inherit the session's captured configuration rather than re-reading disk. A directory is therefore set up from outside it, before a session starts there, which is what --dir is for.

Recovery is a different problem from bootstrap, and a more frequent one: a server that was down when the session opened, or one restarted without --data. SessionStart cannot be replayed, so PreToolUse provisions as well, and the first record of a session with no usable account mints one. The identity still comes from the harness payload and never from the model, which is why this is a hook rather than something the record host offers: the Model Context Protocol carries no verified caller identity, so a tool that let a session register itself would let a model claim to be any session.

Setup #

  • `mkcert` is declined. It installs a certificate authority into a person's
    trust store, which is the most invasive thing this command could do, and
    it destroys the value of the test it exists for: a third-party client
    either holds that authority, in which case its trust path is not the one
    it uses in production, or it does not, in which case it fails for a
    reason that teaches nothing. No public authority would issue
    `*.agents.localhost` either, so the certificate's shape differs from
    production regardless.
    
    A public wildcard-to-loopback certificate with a published key needs no
    trust store change and works in any client, but it forces somebody else's
    hostname suffix — so the zone differs from production, so the `did:web`
    values differ, and the DID is the field everything downstream keys on.
    
    That leaves the delegated development subzone, the only one where local
    is identical to production, and it needs a DNS credential on a developer
    laptop that [deployment](../docs/deployment.md) says should not sit
    beside the signing keys. It waits for a zone.
    
    Nothing is blocked on this. TLS is load-bearing for
    [pds-xrpc](pds-xrpc.md), which is `blocked`, and for [oauth](oauth.md).
    Plain HTTP over `*.localhost` stays the default, because it works
    offline, in seconds, and with no secret.
    

The parts that are not the server #

Working in it #

Done #