Every agent decision now names an accountable human — signed, recorded, and chain-committed
2026-09-15
LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: [ "POST /v1/agent/bootstrap (owner_id)", "POST /v1/agent/profiles (owner validation)", "PATCH /v1/agent/profiles/{agentId} (owner validation)", "GET /v1/governance/lineage/{request_id} (principal block)", "GET /v1/governance/artifacts/{request_id} (accountability block)", ] sdk_methods_updated: ["agentProfiles.bootstrap() owner_id", "agent_profiles.bootstrap(owner_id=) sync + async"] mcp_tools_updated: ["br_bootstrap_agent (owner_id)"] ---
What We Built
The agent profile store has carried an owner_id since the ARM work, and both agent auth paths already hoisted it onto the request context. Nothing downstream ever read it. The signed trust envelope, the hash-chained audit row, and the governance artifact all said _which agent_ acted and none of them said _which human_ is answerable for it. An auditor holding an artifact could not answer the first question a Hush-educated buyer now asks: "who owns this agent?"
This change binds the owner into every evidence surface through one resolver, resolveRequestAccountability(c), so the surfaces can never disagree:
- Trust envelope —
br_principal.owner_id+owner_source, signed with the rest. - Audit row —
completion_audit.principal_owner_idon every allow, deny, error,
cache-hit and guardian-denial write (migration v69, non-hashed like grant_context).
- Lineage decision —
decision.accountabilityinside the bound lineage row, covered
by lineage_digest → provenanceDigest → the chain. A post-hoc edit of the owner is chain-detectable. This is the copy the artifact prefers.
- Governance artifact — a signed
accountabilityblock with an explicit
evidence: committed | recorded flag, and a new EU AI Act Art. 14 control.
Two opt-in knobs make it a policy, not just a record. owner_id on bootstrap defaults to the _user_ behind the bootstrapping key (never the key id — a credential is not a person). BR_AGENT_OWNER_REQUIRED=1 refuses to create an ownerless agent (400) and refuses agent-authenticated requests from an ownerless profile (403 owner_required with a recovery hint), leaving only the self-service paths an agent needs to get fixed.
Why It Matters
Identity vendors are teaching buyers that agent governance means "every agent has an accountable owner." They can prove it in a directory. BrainstormRouter proves it on the decision itself: the owner is signed into the grant the request ran under, and chain-committed next to the model, the policy verdict and the cost. That is evidence a directory cannot produce, and it is the exact artifact an EU AI Act Art. 14 review asks for.
How It Works
// One resolver, every surface.
const a = resolveRequestAccountability(c);
// agent caller → { agent_id, owner_id: profile.ownerId, owner_source: "agent_profile" }
// api-key caller → { agent_id: null, owner_id: apiKey.userId, owner_source: "api_key_user" }
// unknown → { agent_id, owner_id: null, owner_source: null } // never guessed
The router receives it as lineageAccountability and stamps it into the decision payload; the completions handler passes principalOwnerId to every chained audit write; synthesizeEnvelope signs it. On read, assembleLineageWhy exposes a principal block and deriveAccountability picks the committed copy first, the recorded column second, and omits the key when neither exists so pre-binding artifacts keep their exact signed bytes.
The chain payload versions (v1/v2) are untouched. The owner rides the lineage digest, which v2 already commits, so no verify-side dispatch changed and no published test vector moved.
The Numbers
| Surface | Before | After |
|---|---|---|
| Envelope names an owner | never | always (or honest null) |
| Audit rows with owner | 0 | every write site (20) |
| Artifact accountability | absent | committed / recorded, signed |
| Framework controls per plain completion | 1 (SOC2 CC7.2) | 2 (+ EU AI Act Art.14 when owned) |
| Bootstrap owner default | none (ownerless) | bootstrapping key's user |
Competitive Edge
Hush, Astrix and the NHI vendors bind an owner to an identity in their directory. BrainstormRouter binds the owner to the _decision_: the signed grant, the chained audit row and the artifact the auditor holds. No gateway that sits beside the model call can produce that; no directory that sits beside the agent can either.
Lockstep Checklist
- [x] API Routes: bootstrap
owner_id; profile create/patch owner validation; lineageprincipal; artifactaccountability. - [x] TS SDK:
AgentBootstrapParams.owner_id(packages/sdk-ts/src/types.ts). - [x] Python SDK:
bootstrap(owner_id=)sync + async (packages/sdk-py/.../agent_profiles.py). - [x] MCP Schemas:
br_bootstrap_agentacceptsowner_id;agents.jsonregenerated. - [x] Docs:
docs/api-reference/agents.mdx,docs/api-reference/governance.mdx. - [x] Migration: v69
completion_audit.principal_owner_id(idempotent, additive). - [ ] Master Record: capability record entry for owner binding — follow-up.
- [ ] Production verification: deploy, bootstrap an owned agent, fetch its artifact and confirm
accountability.evidence === "committed".