--- id: credentials title: A session gets a credential without a wrapper process status: open crates: [didbot-attest] dependsOn: [oauth, attestation] exitCriterion: > An ordinary session, configured only through settings.json, receives a credential scoped to its own agent account and writes with it. --- # credentials The system installs as configuration the harness already reads, and may not require a wrapper. That costs the kernel-level session marker a wrapper would give, and moves the anchor up to the node. The shape mirrors SPIFFE: a node attests once, a local component issues credentials, and the workload talks to the server itself. Nothing brokers a write, and nothing assumes co-location. - [ ] **The node component: device identity and credential issuance.** Split out into [node](node.md), because it is a process with a lifetime, an install story and a privilege boundary of its own. - [ ] **Nothing secret is delivered through the harness at all.** The one hook mechanism that reaches later tool calls is session-scoped and its contents are readable by any shell command the model asks for, so what a client needs is a socket path it can compute rather than a credential it is handed. [cred-delivery](cred-delivery.md) holds the threat model that decides this, and the daemon holds the credential. - [ ] **A subagent is a context and is issued for like one.** [subagents](subagents.md) settled that: the custody boundary is the context rather than the session. What it costs here is that issuance cannot ride session-scoped environment, so it happens over the daemon's socket — see [cred-delivery](cred-delivery.md). - [ ] **An agent needs no credential to authorize an app.** Its write credential is not also its authorization credential: the confirm call in [oauth](oauth.md) is stamped by the hook, so nothing has to sit in a model's context for a sign-in to work. What the app ends up holding afterwards is a different question, and [cred-delivery](cred-delivery.md) answers it rather than leaving it implied. - [ ] **Say what is in the trusted set, and check it.** The hook binary supplies an identity the model cannot forge, and the server binary holds the keys. Both are trusted and neither is verified: nothing pins the hook a harness runs or says how a deployment knows its server binary is the one it built. - [ ] **Per-agent unix users or containers**, for deployments that want the boundary lower. A deployment choice, not a requirement. - [ ] **Something with no harness still needs a credential.** Everything above is session-scoped by construction, and a CI runner or a daemon has no hook to deliver anything or to stamp the confirm call in [oauth](oauth.md). See [account-types](account-types.md). ## Done Nothing closed yet. Adjacent, and worth knowing about here: a harness client carries a write credential (`didbot_pds::credential`, minted at provisioning) by stamping it onto the tool call, not through `CLAUDE_ENV_FILE`. That solves `plan/auth-types.md`'s narrower problem — a credential has to exist and reach the write path at all, for the repo write surface to require one without breaking every write this deployment already makes — without solving this epic's actual shape: a credential a hook stamps into one tool call is bound to that call, not delivered to the session for every later tool execution the way this epic's `CLAUDE_ENV_FILE` item asks for. It also does not touch node identity, the trusted-set question, or either of the per-agent boundary items below. Whether the stamped credential is the same thing this epic should end up issuing, or a stopgap this epic's delivery mechanism eventually replaces, is not decided here.