June 3, 2026 · Changkun Ou
Agent harness design: the identity fabric
June 3, 2026
This is a companion to Agent harness design: trade-off analysis, which frames the agent platform as a small set of separable primitives connected by contracts. That overview names four primitives the ecosystem agrees on: sandbox, persistence, identity, and the harness itself. This post is the deep dive on the third, and the one the other three lean on hardest: the identity fabric.
The contract is one sentence, and it runs underneath every other primitive.
Resolve this caller into
(user_id, tenant_id, agent_id, workload_id, scopes, attributes), and answer "may this principal take this action on this resource?" on every operation.
That tuple is what the persistence fabric stamps on every session, the cost pipeline bills against, the audit log attributes to, and the policy engine reasons over. The identity fabric is the substrate the others consume. It is not itself a consumer of them. Almost every cross-primitive failure that the overview catalogs, the cross-tenant retrieval leak, the unbillable LLM call, the forensically useless audit entry, the runaway agent on a revoked token, is an identity-fabric edge that was drawn imprecisely or not at all. Naming the primitive is the first step to drawing it cleanly.
The contract
Two slices live behind that one sentence, and a system that builds only one of them is broken in a predictable way.
The authorization slice answers who is calling and what may they wield right now: it authenticates the principal, issues and exchanges tokens, and scopes them. The substrate slice answers who owns what and who may see it: tenancy, resource-identity association, isolation, sharing, lifecycle cascades, and the policy engine that evaluates resource access.
Build only the authorization slice and you get over-scoped tokens, ambient authority, and audit trails that cannot say which tenant an action belonged to. Build only the substrate slice and you can model "Acme's legal team owns this session" but you cannot express "Bob, acting on behalf of Alice, against a resource Alice's tenant shared with his." Both are required, and they are required together: token scoping is how the authorization slice enforces the ownership the substrate slice declares.
The administrative surface around the contract is small: register principals, issue and revoke grants, define the tenancy shape, author the access policy, and rotate the signing keys. Everything past that boundary, what code runs, what the harness sends, what the agent is allowed to do, belongs elsewhere.
The seam this primitive does not cross
The boundary that needs the sharpest line is the one against governance, because a sibling post argues for a separate governance fabric and the two are constantly mistaken for each other.
Identity owns authentication, principal resolution, token issuance and exchange (OAuth scopes, short-lived capability tokens, on-behalf-of flows, macaroons and biscuit-style attenuable tokens), resource-access authorization, and tenancy. It does not hold an agent's prompt, its tool grants, its budget, or its approval steps. The agent-definition document, the catalog of which tools an agent may call, the spend ceiling, and the human-in-the-loop gate all belong to governance.
The distinction that keeps the two from collapsing is declares versus enforces. Governance declares an attenuation: this agent may only read, only this repository, only under five thousand dollars a month. Identity enforces it, by minting a token scoped to exactly that and refusing to mint anything broader. Capability attenuation is governance's vocabulary; token scoping is identity's mechanism for making it true on the wire. When a system buries the spend ceiling in the same place it holds the OAuth client secret, it has fused two primitives that fail differently and should be owned by two different teams.
The cleanest test for any concern: if revoking it changes who can reach a resource, it is identity. If revoking it changes what an authorized principal is permitted to attempt, it is governance. A token's scope claim sits on the seam precisely because identity writes it to encode what governance decided.
Three identities, reconciled on every request
The harness has three principals to reconcile on every call: the user whose intent is served, the agent acting on the user's behalf, and the workload (the pod or process making the actual call).
graph TB
classDef user fill:#d6eaf8,stroke:#2980b9
classDef agent fill:#fdebd0,stroke:#e67e22
classDef workload fill:#d5f5e3,stroke:#27ae60
U[User identity<br/>human, SSO, MFA]
A[Agent identity<br/>name, version, capabilities]
W[Workload identity<br/>pod, process, SPIFFE ID]
U -->|"on-behalf-of"| A
A -->|"runs as"| W
W -->|"calls"| EXT[External systems]
class U user
class A agent
class W workload
These are orthogonal axes, not a hierarchy that can be collapsed. The user is a human authenticated through SSO and MFA, with consent that can be withdrawn. The agent is a named, versioned principal with its own capability surface, which is what makes "the agent did X, not the user" expressible at all. The workload is the runtime, and it answers a question the other two cannot: is this actually a legitimate harness replica, or something else in the cluster that obtained a token? A system that has only user identity cannot tell a compromised pod from a real one. A system that has only workload identity cannot attribute an action to the human who authorized it. The reason to carry all three is that each closes an attack the others leave open, and each carries an attribution the others cannot.
The authorization slice: four ways to scope what travels
The single design decision with the most blast-radius leverage is how authority is packaged for the call. Four options, in increasing order of plumbing and decreasing order of blast radius.
Broad bearer tokens. The user authorizes the agent once with coarse OAuth scopes (repo, email:send), and the agent reuses that token for every action across the session's life. It is the default because it is one consent dialog and one credential. Its cost is that the blast radius equals the union of everything the scope permits, for as long as the token lives. GitHub's fine-grained personal access tokens [1] and Google's narrowed consent screens exist precisely to claw back from this pattern. The deeper problem is temporal: a broad token is a standing grant, and a standing grant is reachable by anything that can reach the agent, including prompt injection. The token does not know whether the agent decided to act or was tricked into it.
Short-lived capability tokens. Each tool call mints a token carrying exactly the permissions that call needs, with a TTL measured in minutes. This is the inversion of the standing grant: authority exists only at the moment of use and for the action at hand, so the window in which a leaked or injected call can do damage shrinks from a session to a single operation. The price is a token broker in the trust plane and downstream services that accept narrowly scoped tokens. Biscuit [2] and Macaroons [3] are the formal expression: tokens that can be attenuated by the holder, so a service can hand a strictly weaker token to the next hop without calling back to the issuer. In practice most systems approximate this with short-lived OAuth JWTs carrying a tight aud and scope. The reason to pay the plumbing cost is irreversibility: when the agent's allowed actions are high-privilege or cannot be undone, the difference between a one-minute window and a one-session window is the difference between a contained incident and a breach.
On-behalf-of (OBO). When downstream authorization depends on which user is behind the agent, the user's authority has to propagate end to end, not be replaced by the agent's. OBO attests both identities in one token: the downstream service sees that the agent is acting, and that it is acting for this specific user, and can apply the user's own data-access rules. The canonical case is a user-scoped query against a data warehouse, where the warehouse's row-level security must see the human, not the service account. RFC 8693 [4] formalizes the token exchange; Microsoft's Entra OBO is the common implementation. The standards-track answer specific to agents is IETF draft-oauth-ai-agents-on-behalf-of-user-00 [5], which encodes user plus agent plus client identity in a single delegated token, the formal version of "the agent is a first-class identity, not a stand-in for the user." Okta for AI Agents [6] and its Cross-App Access primitive ship this as a product rather than a draft. The failure OBO prevents is silent privilege substitution: without it, the only way to let an agent query user data is to give the agent the user's full authority, which it then holds for everything else too.
Workload identity (SPIFFE/SPIRE). The pod proves what it is to the trust plane cryptographically, with no shared secret to leak [7]. This is orthogonal to all three of the above: it does not say who the user is or what the agent may do, it says "this caller is a genuine instance of this workload." In a multi-replica, multi-service harness running on Kubernetes, it is what lets the token broker refuse to mint a user token for a process that merely sits in the cluster. The reason it is non-negotiable at scale is that every other credential in the system is bootstrapped from something, and SPIFFE makes that something an attested identity rather than a static secret copied into an environment variable.
The selection rule: broad scopes are defensible only for single-user, low-privilege deployments. Capability tokens earn their plumbing when actions are high-privilege or irreversible. OBO is load-bearing whenever downstream authorization is user-dependent. SPIFFE is standard for anything multi-replica.
The substrate slice: tenancy as the coarsest ownership axis
Tenancy is the widest-grain ownership decision, and it is the one that calcifies hardest if made implicitly. Every resource in the persistence fabric, every session, workspace, memory index, artifact, audit entry, and cost record, either carries a tenant attribute or is silently trapped in a single tenant. The choices:
- Flat user, no tenants. Each user is an island. Trivial to isolate, impossible to share. It breaks the first time a colleague needs the session or a billing admin needs to roll up cost.
- User plus team. Resources owned by a user or a team; access resolved through team membership. Adequate for most early-stage B2B.
- User plus org plus project. The enterprise default (the shape Stripe, Linear, and GitHub orgs use): per-project isolation inside a shared org, org-level billing on top.
- Hierarchical ABAC. No fixed levels at all. Tenancy emerges from attribute rules ("caller has
dept=legaland resource hasclassification=privileged"). AWS IAM tags and condition keys [8], GCP IAM's resource hierarchy [9], and Cedar [10] support this natively. Maximum flexibility, steep authoring and audit cost.
The governing trade-off is that isolation granularity runs directly against sharing flexibility. Flat-user is trivially isolated and cannot share. A three-level hierarchy is legible by inspection but calcifies the moment the real org is a matrix rather than a tree. ABAC fits any topology and makes "who can see what?" undecidable by reading the data: only the policy engine knows. The non-obvious consequence is for audit and erasure, covered below, both of which are graph traversals over the ownership model, and a model that "only the policy engine knows" is a model you cannot traverse offline. Pick the simplest tenancy shape that fits the org and leaves the ownership graph walkable.
Resource-identity association and isolation tiers
Given a tenancy shape, each resource class still has to decide how identity attaches to it, and these decisions are made independently per class.
- Owner-only. One owner, no one else. Drives most single-user session stores.
- Owner plus ACL. Owner plus an explicit list of users or groups with roles. The Google Docs sharing model.
- Attribute-based. No list; access computed from caller attributes against resource tags. Scales past ACLs for large orgs, opaque without tooling.
- Per-tenant logical partitioning. Shared schema, a
tenant_idcolumn (or a Pinecone-style namespace, or a pgvector row filter), every query rewritten to filter on tenant. Cheap, and one missingWHEREclause from a breach. - Per-tenant physical isolation. Separate database, schema, or cluster per tenant. Strongest isolation, highest operational cost, hardest to roll up cross-tenant analytics.
Isolation, separately, lives on a spectrum whose defining property is blast radius on a bug:
| Tier | Mechanism | Blast radius on bug | Cost | Fit |
|---|---|---|---|---|
| Logical | Shared DB plus tenant column plus enforced filter | One missing WHERE leaks everything |
Lowest | Dev, internal tools, low-stakes SaaS |
| Namespace | Separate schemas or namespaces per tenant | Misrouted query hits one wrong tenant | Low | Most B2B SaaS |
| Physical | Separate DBs, clusters, or accounts per tenant | Bug stays inside one tenant | High | Regulated (HIPAA, PCI), large enterprise, sovereign cloud |
The reasoning that makes this a per-resource decision rather than a platform-wide one: the tiers price isolation against blast radius, and blast radius is not uniform across resource classes. A leaked audit row is a confidentiality incident; a leaked cross-tenant memory retrieval is an active data-breach class failure because it surfaces one tenant's content inside another tenant's live session. So a mature platform mixes tiers deliberately, audit logs physically sharded for regulatory reasons, sessions logically partitioned for cost, high-sensitivity memory physically isolated, with each choice justified by the blast radius of that class. A common production shape: sessions and workspaces as owner-plus-ACL, memory indexes as per-tenant namespaces, audit as a shared physical store with tenant-tagged rows, artifacts in one object store with per-object ACLs enforced at the gateway. The trap that runs underneath all of it is implicit tenancy from request headers: if tenant_id is read from an HTTP header with no cryptographic binding (a JWT claim, an mTLS client cert), a single spoofed header reaches any tenant, and the most carefully chosen physical isolation tier is bypassed at the front door.
Sharing semantics, where the leaks hide
Real products share, and sharing is where the substrate slice earns its complexity. The questions are easy to state and easy to get subtly wrong.
A cross-user session share (Alice shows Bob a read-only live view, or hands him collaborative edit, or full takeover) is an ACL entry or an attribute grant, but it forces the harness to accept multiple concurrent viewers without privilege escalation. A cross-tenant catalog (a marketplace of agents or prompts readable by all, writable by a platform tenant) is a universal read grant on a platform-owned resource. An invite/accept model holds the grant pending acceptance, trading one more state machine for protection against accidental exposure.
The two that bite hardest are delegation chains and revocation. In a delegation chain, Alice grants Bob access to a session for a week, and Bob invokes an automated agent on it: whose identity does the agent run as? If the chain is collapsed into a single effective identity, attribution and cascade revocation are both lost, you can no longer say the agent acted for Bob acting under Alice's grant, and revoking Alice's grant no longer stops the agent. Revocation semantics then ask what a revoked grant does to in-flight work: hard-kill the session synchronously, let it continue read-only, or let it finish its current turn. This is a genuine design choice between synchronous hard-kill and token-TTL expiry, and it has a latency, examined next.
The reason sharing is treacherous is that it crosses every resource class at once, and getting it right for one class is not getting it right. A platform that revokes Bob from a session but not from the memory index behind it produces exactly "I revoked Bob, and his session can still query my memory", a cross-tenant leak that, worse than a silent one, leaves a paper trail proving the platform knew Bob was revoked.
Lifecycle cascades, where the happy path lies
Identity is not static, and the cascades matter far more than the steady state. Each of these is a graph traversal over the ownership model, and each is where an implicit tenancy decision made months earlier comes due.
User deletion to resource cascade. Deleting a user must reach their sessions, workspaces, memory entries, artifacts, cost records, and audit entries. But it is modulated by retention: audit entries may be legally required to persist under tamper-evident chaining, so erasure there becomes a tombstone plus a signed proof of deletion rather than a true delete. The cascade is only as complete as the ownership graph; if some artifact inherited tenancy implicitly and never got an owner, it survives the deletion as an orphan.
Tenant migration and re-parenting. Acme is acquired by Globex, and every Acme resource must re-parent under Globex's tenant. It is rare and it is load-bearing when it happens, and it is nearly impossible to retrofit if tenancy was ever implicit, because re-parenting is exactly the operation that implicit tenancy cannot express.
Re-authentication after revocation. Once a token is revoked, in-flight operations must halt at the next boundary or re-authenticate. The hard case is the long-running scheduled agent: it has no user at the keyboard to re-consent, so the boundary at which it halts has to be designed in.
Agent decommission. A retired agent version must stop minting new tokens through its workload identity, yet sessions that still reference it must continue to resolve it, because the audit trail names that version and a name that no longer resolves is a hole in the record.
Right-to-erasure scope. GDPR erasure is a traversal from a user to every resource the fabric attributed to them. With a canonical ownership graph it is a batch job; without one it is a best-effort search that cannot prove completeness, and "we believe we deleted everything" is not a defensible answer to a regulator.
The revocation cases share a property worth stating directly: revocation latency is unavoidable with bearer tokens. A user withdrawing consent does not reach into already-issued tokens; the session runs on revoked authority until the token expires. This is the strongest argument for short TTLs from the substrate side, not the authorization side, the TTL is the ceiling on how long a revoked grant stays exploitable, so the more sensitive the tenant, the shorter the token has to live. Long-TTL tokens and a right to immediate revocation are mutually exclusive, and a system has to pick which one it is honoring.
Identity is the audit and cost primitive, not a contributor to them
It is tempting to file audit and cost under observability and billing. They belong here, because they are the identity fabric's highest-volume consumers and they consume nothing but its output.
Every audit event carries (user_id, tenant_id, agent_id, workload_id). An audit entry that records "session X did Y" without that verified tuple is forensically useless: it cannot answer the only questions an investigation asks, which are who and for which tenant. Right-to-erasure, in turn, is a traversal over the same ownership graph the fabric maintains, so audit and erasure are two reads of one structure. The tension between them, audit demands the record persist, erasure demands it vanish, is resolved by the tombstone-plus-proof pattern: purge the payload, keep the tamper-evident chain intact.
Every cost record carries tenant_id, and a record without it cannot be billed: it lands in one undifferentiated platform invoice with no way to argue spend back to the tenant that caused it. This is why per-tenant budget enforcement is downstream of identity and not the other way around. A budget cap that says "this tenant may spend five thousand dollars a month" is meaningless unless every LLM call carries a verified tenant at the moment of the call, before the spend happens. The cost-control primitive can only enforce a cap as precise as the attribution identity hands it, which is why the runaway-spend incidents the overview catalogs, the forty-seven-thousand-dollar agent loop [11] and the Claude Code recursion that burned 1.67 billion tokens in five hours [12], are not purely cost-system failures. Budgets existed in both. They fired on spend that the system could attribute only after the money was gone.
The policy engine is the shipping surface
The component that actually answers "may this caller take this action on this resource?" is a policy engine, and it is the most visible artifact of the identity fabric. The options:
- OPA / Rego [13]. Kubernetes-native, general-purpose, data-driven, widely deployed, with a real learning curve on the Rego language.
- AWS Cedar [10]. Typed and analyzable, which means you can ask formal questions about a policy ("can any principal ever reach this resource?") rather than only testing it. Cedar-Agent adds streaming policy updates for agent use cases.
- Oso [14]. Authorization as a library with an opinionated relationship model, positioned as a ReBAC alternative for app-level authorization.
- Permit.io [15]. Authorization as a service that composes OPA, Cedar, or Zanzibar-style backends behind one API.
The distinction that has to be held: this engine evaluates resource access by identities, which is identity's job. It is not the engine that evaluates action risk by agents ("may the agent send this email?"), which is governance's. A production system runs both. Sometimes they are collapsed into one Rego bundle for operational simplicity; more often they are deliberately separated so the identity team owns "who can reach what" and the agent-safety team owns "what is too dangerous to do," because those two policy surfaces change for different reasons and on different cadences. Collapsing them saves a process boundary and costs you the ability to reason about either one in isolation.
Failure modes
The failure modes of the identity fabric are the cross-primitive failures the overview keeps surfacing, seen from the substrate that should have prevented them.
Ambient authority. A single long-lived token sits in the environment and every tool call uses it, so prompt injection can trigger anything the token permits. The canonical real-world case is Invariant Labs' May 2025 disclosure of the GitHub MCP "one-PAT-for-all-repos" pattern [16]: a reasonable-looking personal access token, broad enough to span repositories, became a cross-repository exfiltration primitive when an attacker planted instructions in one repository's content that an indirect prompt injection then used to reach the others the same token covered. The precise shape matters: this was scope creep across repositories under one user's token, not a leak across tenants. It is the cleanest demonstration that a standing grant does not distinguish an action the agent chose from one it was tricked into, which is exactly the property short-lived capability tokens remove.
Consent fatigue. Re-prompting for consent on every action trains users to grant the broadest scope available just to silence the dialog, which manufactures the ambient authority above. The structural fix is to make the agent a first-class identity with its own durable, auditable grant rather than a proxy that re-borrows the user's authority per action, the direction Salesforce's Agentforce identity model [17] and Auth0's agent-identity work [18] both take.
Agent-to-agent delegation. When agent A calls agent B, whose identity does B see? If A forwards the user's token, B now holds the user's full authority for its entire lifetime, with none of the user's intent attached. Peer delegation across platforms (A2A [19] and similar protocols) makes this acute, because B may be on infrastructure A does not control. The correct shape is a delegated, attenuated token that names the chain (user, then A, then B) and narrows scope at each hop, which is the substrate slice and OBO doing exactly what they exist for.
Cross-tenant retrieval bleed-over. Vector memory, RAG indexes, and embedding stores are the highest-risk class, because a top-k query against a shared index without an enforced tenant_id filter silently returns another tenant's data into a live session. Pinecone namespaces [20] and pgvector row-level security [21] are the standard mitigations, but the deeper rule is partition-at-write-time over filter-at-read-time: a filter applied at read time is one SQL bug from a leak, while an index that is physically per-tenant cannot leak even if the query is wrong.
Silent tenant coupling. A resource that inherits tenancy implicitly, from the caller's default tenant at creation, becomes unshareable and un-re-parentable without a migration. The cascade and re-parenting failures above are this failure, deferred. The discipline is to make tenancy explicit at the moment of resource creation, never inferred later, because every implicit assignment is a future migration that may turn out to be impossible.
ACL and ABAC drift. Re-orgs change membership; resources keep their old ACLs and tags. Access slowly stops matching reality in both directions, granting people who left and denying people who arrived. The only answer is periodic reconciliation of the ownership graph against the current org, which is, again, a traversal you can only run if the graph is walkable.
Where this leaves the human
The thread through why Latere exists is that the most important intelligence in an autonomous system is the one you cannot see: the person who set the direction and drew the boundaries. The identity fabric is where the most consequential of those boundaries are written down and made enforceable. Who the agent is acting for, which tenant is accountable, what it may reach, and how fast a revocation takes effect are human decisions, and they are exactly the decisions a system erases when it drops a single broad token into an environment variable and lets the trust decision be made once and never revisited.
Keeping identity a first-class fabric keeps those decisions visible and revocable. A standing grant becomes a short-lived, attenuated one, so withdrawing authority is a real act with a bounded latency rather than a hope that nothing went wrong before the token expired. An action carries the human it was authorized by, all the way to the external system, so attribution survives delegation instead of dissolving into a service account. The agent still runs at full speed. The authority over who it speaks for, and what it may touch, stays with a person, which is the same bet the rest of the platform makes, applied to the substrate every other primitive stands on.
Latere runs identity as internal substrate every product consumes rather than a separately marketed product; the hub places it on the map and names the seams, a unified identity-attributed audit stream among them, that are still open.
References
[1] GitHub. "Fine-grained personal access tokens." GitHub Docs
[2] C. Brunel et al. "Biscuit: Decentralized Authorization with Attenuable Tokens." biscuitsec.org
[3] A. Birgisson et al. "Macaroons: Cookies with Contextual Caveats for Decentralized Authorization in the Cloud." NDSS 2014. Google Research
[4] IETF RFC 8693. "OAuth 2.0 Token Exchange." RFC Editor
[5] IETF. "OAuth 2.0 On-Behalf-Of User for AI Agents." draft-oauth-ai-agents-on-behalf-of-user-00, 2025. IETF Datatracker
[6] Okta. "Okta for AI Agents, Early Access." September 2025. Okta
[7] SPIFFE/SPIRE. "Secure Production Identity Framework for Everyone." spiffe.io
[8] AWS. "IAM Identity Federation and Attribute-Based Access Control." AWS Docs
[9] Google Cloud. "IAM Overview, Resource Hierarchy and Conditions." Google Cloud Docs
[10] AWS. "Cedar Policy Language." cedarpolicy.com
[11] DEV Community. "The $47,000 Agent Loop." 2025. dev.to
[12] anthropics/claude-code. "Massive token consumption: 1.67B tokens in 5 hours." Issue #4095, 2025. GitHub
[13] CNCF. "Open Policy Agent (OPA)." openpolicyagent.org
[14] Oso. "Authorization as a Library, Relationship-Based Access Control." osohq.com
[15] Permit.io. "Authorization-as-a-Service for Fine-Grained Access Control." permit.io
[16] Invariant Labs. "GitHub MCP: Ambient Authority and Cross-Repo Token Scope Creep." May 2025. invariantlabs.ai
[17] Salesforce. "Agentforce Trust Layer and Agent Identity." 2025. Salesforce
[18] Auth0. "Identity for AI Agents." 2026. Auth0
[19] Google, Microsoft et al. "Agent-to-Agent (A2A) Protocol." 2025. a2aprotocol.org
[20] Pinecone. "Managed Vector Database for AI." pinecone.io
[21] pgvector. "Open-source Vector Similarity Search for Postgres." github.com/pgvector/pgvector