A Practical Layout for Agent Identities: Me, Agents, Roles and Projects
Table of Contents
The standards work on agent identity is about what goes over the wire: an identifier, a credential bound to it, a token that says who an agent is acting for. None of it says where one person keeps the credentials that their agents hold today, most of which are plain API keys. This is a proposal for that layout, written against a file-per-entry password store such as pass, and a note of where it meets the standards and where it falls short of them.
It is a layout and a set of rules, not a protocol. Nothing here has been built.
1. The layout
Five kinds of owner hold credentials, and the first segment says which. Every path has the same shape after the owner:
path = owner "/" realm "/" handle "/" kind [ "/" name ] owner = "me" | "agent/" name | "role/" name | "proj/" project "/" env | "site/" place
| Owner | Path prefix | Holds |
|---|---|---|
| the person | me/ |
the person's own accounts: logins, second factors, recovery codes, tokens |
| a named agent | agent/<name>/ |
an agent with accounts of its own, when a service issues tokens only to accounts |
| a role | role/<name>/ |
an agent that is a job, not a persona: tokens issued for that job and nothing else |
| a project | proj/<project>/<env>/ |
what identifies an application or deployment to a provider, per environment |
| a place | site/<place>/ |
what admits anyone standing there: a network, a door, a host's admin |
An example tree, with invented names:
me/ example-forge.com/alice/login example-forge.com/alice/otp example-forge.com/alice/token/laptop role/ builder/example-forge.com/alice/token/builder-laptop builder/llm-gateway.internal/_/token/builder-laptop builder/broker.internal/builder/token/builder-laptop reviewer/example-forge.com/alice/token/reviewer-laptop observer/metrics.internal/_/token/observer-laptop proj/ workshop/dev/graph-db.internal/app/login workshop/prod/graph-db.internal/app/login workshop/prod/example-idp.com/workshop-prod/token/app workshop/shared/dns.example/_/token/ci site/ home/net.guest/_/login
The segments have fixed meanings.
- Realm is where the credential is honoured: the provider's registrable
domain, in lower case, with no trailing dot (
example-forge.com, notExampleForge). Without one it ishost.<name>ornet.<name>. A tool that derives values from the path seesExample.comandexample.comas two entries, though DNS sees one host. - Handle is what the realm calls the account, verbatim;
_when the realm has no accounts. A role's token is usually issued on someone's account (aliceabove), and the handle says whose. - Kind is closed:
login(typed to begin a session),otp(an authenticator secret),recovery(codes that stand in for a lost factor),token(issued by the provider, revocable there one at a time),key(material nobody can reissue),pin. No segment may be one of these words anywhere else in the path, which is what lets a path parse without knowing its owner. - Name follows
tokenand is the holder: the machine or service that presents it (laptop,ci,builder-laptop). Two holders get two tokens. What the token may do is not in the path. - Environment belongs to a project and is closed:
local,dev,test,staging,prod,shared. A project uses the ones it has; a library may have onlytestandshared. A role reaches an environment through the scope of the token it holds, not through its path.
An entry is the secret on line 1, then name: value fields from a closed
list. The ones that carry the layout's weight:
scope- what the provider says the token permits, in its own words. This is where the privilege level lives.
effect- the worst the bearer can do in front of other people:
private(nobody sees it, or it can be undone),staged(it prepares something a person approves),outward(it acts under a name, at once). A token that can publish isoutwardeven if it never has. issued,expires- the current value's issue date and end date, or
never. A derived secret also carries its generation; bumping the generation movesissued. used-by- what presents it, in a sentence.
A project's registration with a sign-in provider is a token under the
project (the provider issues the client secret); its client id and redirect
address are fields, and differ per environment. An account the person signs
into through another provider holds nothing of its own: it has a line in the
record (below) pointing at the account it hangs from, and no entry.
2. The rules
- The person mints, a role consumes. Every token under
role/was issued by an account the person controls, and can be revoked from there. - No
loginunder a role. A role holds what it was issued, and nothing that can mint more. If a service issues tokens only to accounts, that is a named agent underagent/, with its own login, second factor and recovery codes, and every cost that comes with them. Prefer a role. - Readers per owner. Each owner has its own set of keys that can decrypt
it:
role/builder/to the builder's key and the person's,me/to the person's alone. A process acting as an owner is given a key for that owner and no other, so that it cannot readme/is a fact about keys, not about where it was told to look. Recipients per directory, separate stores, or a broker holding the only key all satisfy this. It hides contents, not names:passleaves paths in clear, so any reader can list the whole tree. One key for everything, decrypted by an unlocked agent on the person's own machine, satisfies none of it; a setup like that should say so. - A role is a ceiling. The role's directory is the most an agent in that role may hold. A task takes less: where the issuer can mint a shorter-lived or narrower token from the stored one, the agent uses that.
- No two owners share an entry. If two need the same access, the provider issues two credentials. Moving an entry from one owner to another is not a rename; it is a revoke and an issue.
- The path is the identifier.
role/buildernames the same thing in the store, in the broker's access list, in a telemetry attribute and in an audit log.
Four roles are enough to exercise this: a planner that reads everything and
writes nothing, a builder that writes in dev, a reviewer that reads and
comments, an observer that reads telemetry only. The surveys of multi-agent
systems keep finding the same splits: a coordinator that decomposes work,
planner against executor, coder against reviewer.
3. Where it meets the standards
Most of the documents below are individual drafts or community-group output, not standards. The mapping is mine.
| Work | What it defines | This layout |
|---|---|---|
| IETF WIMSE; SPIFFE | a workload identifier: a URI unique within a trust domain, spiffe://<trust-domain>/<path> |
the path prefix is that path: spiffe://example.org/role/builder. Same shape, no issuer behind it |
draft-klrc-aiagent-auth, now draft-ietf-wimse-aims |
one identifier per agent; primary credentials bound to it and provisioned at runtime; secondary ones obtained by exchange | everything in the store is at best secondary. The draft calls static API keys an antipattern, and the store is where they live |
draft-ni-wimse-ai-agent-identity |
binding an agent to an accountable user or organisation | rule 1, and rule 3: the person is always among an owner's readers |
| RFC 8693 token exchange | sub for the subject, act for the actor, may_act for who may become one |
me is sub, role/<name> is act. The store is a local may_act; the token records who did |
draft-araut-oauth-transaction-tokens-for-agents |
the RFC 8693 act claim for the agent, sub for the principal, and an agentic_ctx claim updated at each agent hop |
the owner and the project environment are what would go in that context |
| W3C DID 1.1 (a Candidate Recommendation since March 2026), Verifiable Credentials; the Agent Identity Registry Protocol Community Group (April 2026) | resolving an agent identifier across organisations; a credential format; revocation | no overlap. Nothing here resolves outside one person's store |
| OpenID Foundation, Identity Management for Agentic AI | names user impersonation and missing on-behalf-of delegation as the gaps | rule 2 is the local form of "do not impersonate": a role cannot hold the person's login |
| NIST NCCoE concept paper (draft, February 2026) | telling agent identities from human ones, and tying an agent's actions to its own identity | one owner per role, and rule 5: no two owners share an entry |
| NIST SP 800-57 Part 1 Rev. 5 | cryptoperiods for keys | the rotation section below; it counts in years, for keys, and says nothing about bearer tokens |
The honest reading of the table: the layout borrows the standards' names and none of their guarantees. An entry here is a bearer secret in a file. It is not bound to the workload that uses it, it does not expire unless the issuer makes it, and nothing attests which process read it.
4. Rotation
- Age without decrypting. The store is a git repository. The last commit
that touched an entry's file is when it was last replaced, so a report of
credential age never opens a secret. Changing a directory's recipients
re-encrypts and recommits every file in it, so the report has to skip
pass's re-encryption commits. - Policy by path and effect. One file at the root maps path patterns and
effects to intervals:
outwardtokens sooner thanprivateones, tokens underproj/*/prod/sooner than underproj/*/dev/, aloginonly on a leak. A token whose end date isneveris the first thing to fix. No standard sets an interval for API keys or tokens. AWS flags access keys older than 90 days; GitHub caps fine-grained tokens at 366 days by default. NIST SP 800-57 gives cryptoperiods in years, for keys. For passwords NIST SP 800-63B says the opposite: no periodic change, change on evidence of compromise. - Rotated means the old one is dead. A new value in the store is half of it. The rotation is done when every consumer has switched and the issuer refuses the old value.
- Derived or stored. A secret the person chooses (a broker password, a database password, a local shared key) can be derived from a root secret and the entry's path, with a counter as its generation, the way a stateless password manager derives one per site. A secret the issuer chooses cannot, and stays in the store; an OAuth client secret is the common case, since the provider issues it. Deriving buys reproducibility and nothing on disk. It does not buy forward secrecy: the root secret gives every generation, and an old generation stays derivable for ever, so only the issuer refusing it ends it.
- Rotation only goes forward. In the workshop fixture, asking the derivation tool for generation 1 saved that counter and silently rolled the entry back. A store that derives needs a rotate operation that refuses to lower the counter, separate from reading an old value.
- Scope by prefix. A mock incident on the fixture, "one project's secrets were exposed", rotated everything under that project's path, seven entries, and left the neighbouring project's five alone. The path is the blast radius.
6. Promotion: the same paths off the laptop
The layout is written for one laptop. Agents also run elsewhere: in Amazon Bedrock AgentCore, in Google Cloud's Agent Runtime, on a GitHub Actions runner, in a container or a jail. The aim is to move a role into one of those without two failures: the laptop's store stops being the record of what exists, or a copy of a secret lands somewhere that nobody rotates.
A credential reaches a runtime in one of three ways, in order of preference.
- Federate. The runtime gives the workload an identity of its own and issues it short-lived credentials. Nothing is copied. The store holds the trust instead: which runtime identity may act as which role. That is configuration, not a secret.
- Issue another. The runtime is a new holder, so it gets its own token,
named for it (
token/builder-agentcorebesidetoken/builder-laptop), issued on the same account and revocable on its own (rule 5). Only where the issuer gives out one secret per account, which is the case for some API keys and most database passwords, is the value copied; the record then says the entry has two holders, and rotation has to reach both. - Never. Nothing under
me/is promoted, and nologinunder a role (rule 2). A runtime that asks for one is asking for the person.
| Runtime | Identity it gives the workload | What the path becomes | Federated | Held there |
|---|---|---|---|---|
| the laptop | none; the owner's readers | the identifier itself | -- | nothing; the store is read in place |
| GitHub Actions runner | an OIDC token per job, if the workflow has id-token: write; the subject names repository and environment, repo:example-org/workshop:environment:dev |
the GitHub environment carries a project's <env>; the cloud role the token may assume is named after the owner, role-builder |
AWS and Google Cloud accept the token, so no cloud credential is stored in GitHub | environment secrets, released to a job only after the environment's rules, such as required reviewers, pass |
| Amazon Bedrock AgentCore | a workload identity, arn:aws:bedrock-agentcore:<region>:<account>:workload-identity/directory/default/workload-identity/<name> |
<name> is the owner flattened: role-builder |
AWS resources through IAM; outside services through OAuth credential providers, client credentials or authorization code | API keys and OAuth client credentials in the token vault, which releases them only to the agent and user that obtained them |
| Google Cloud Agent Runtime | a per-agent identity based on SPIFFE, principal://agents.global.org-<org>.system.id.goog/resources/aiplatform/projects/<n>/locations/<loc>/reasoningEngines/<id>, with tokens bound to a certificate and usable only from the runtime |
Google assigns <id>, so the path cannot be the identifier; the record maps owner to principal |
Google Cloud resources through IAM grants to the principal | not covered by the agent identity docs |
Two things follow from the table. First, promotion is where the standards' guarantees turn up: each runtime binds its identity to the workload, which the laptop never does (see the standards). The bearer-secret gap closes for federated credentials and stays open for everything held. Second, rule 6 bends. GitHub and AgentCore let the owner survive as a name; Google does not, and GitHub's subject for repositories created after 15 July 2026 carries owner and repository IDs, so a rename does not move the trust. Where the runtime picks the name, the record maps one to the other.
7. The record
The store needs one file in the clear that answers three questions without decrypting anything: what is held, what ends when, and what it could do. Entry names are visible on disk already, so dates and effects beside them leak little more.
Each entry has a line with a state: held (in the store), wanted (not yet
issued), elsewhere (exists, but not here), dropped (revoked, with the
reason). For a held entry the end date and the effect are copied from the
entry's fields, and the copy is derived or checked by a tool, never typed: a
record kept by hand drifts from what it describes, and a wrong line is
believed.
Promotion adds two things. An elsewhere line says where. And a role that is
federated has a line of its own, keyed by owner, because nothing is filed:
role/builder/llm-gateway.internal/_/token/builder-laptop held 2027-01-10 private role/builder/llm-gateway.internal/_/token/builder-agentcore elsewhere 2027-01-10 private agentcore:role-builder api-key provider llm-gateway role/builder federated github:example-org/workshop env=dev -> cloud role role-builder role/reviewer federated gcp:reasoningEngines/1234 (assigned) proj/workshop/prod/graph-db.internal/app/login held never private also: github:example-org/workshop env=prod secret GRAPH_DB_APP
That turns "don't lose anything locally" into checks a harness can run:
- Every holder is recorded. List each runtime's vault; a name with no line is drift or a leak.
- Losing a runtime loses nothing. Federated roles held nothing there, and what was issued there is revoked there without touching the laptop's.
- Rotation reaches every holder. An entry with
also:is rotated when the store, every copy and the issuer agree. - An incident is a prefix. The prefix that selects a project's entries also selects their lines, so scope by path covers the runtimes.
- Environments do not cross. An entry under
proj/<p>/devnever appears in a runtime'sprodenvironment. - The role stays a ceiling. A runtime gets at most the role's entries (rule 4), never a union of roles.
8. A harness
The layout makes claims that can fail, so it can be tested.
- Stand up the local shared systems only: a model gateway, a metrics server, a broker, a collector. Hosted services stay out of an automated run.
- Mint one credential per role and system into a throwaway store with throwaway keys. A real store is never opened.
- Run a table of probes: role, system, action, and whether it should be allowed. The denials are the point. The reviewer publishing to the builder's topic must fail. A role decrypting another role's directory must fail.
- Replace one role's credential, then assert the old one is refused and the new one works.
- Check that every probe shows up in the collector under the right role.
The collector takes
service.nameon the sender's word, so this tests the client, not the store; a client certificate per role is what would tie the two. - For each promoted role, list the runtime's vault and compare it with the record. A local stand-in for a runtime, a container with its own secrets directory, is enough to test the comparison.
The result is a grid of role by system: credential age, when it is due, and the last probe.
9. Gaps
- Bearer secrets. Nothing binds an entry to the process that uses it. The standards' answer is a credential issued at runtime to an attested workload; this is a layout for the world before that arrives.
- One person. There is no trust domain beyond a single store, no resolution, no way for another party to verify a role.
- Named agents cost what people cost. An earlier version listed agents with
accounts of their own as a gap. Building them gives the answer,
agent/<name>/, and its price: a mailbox to verify to, a second factor with no hand to hold it (it ends up as a hardware key a person owns, or a secret in the same store as the password), recovery codes, and usually a sign-in through another provider, which makes that provider's account the real credential. Whoever reads the mailbox can reset the rest. Make one only when a service issues tokens to nothing but an account. A role can still carry a persona, a name and a description, as metadata that never appears in a path. - Hardware second factors. There is nothing to file, so the record does not say which keys are registered where. That is what a second custodian would most need.
- What it means to others.
effectis typed at issue time. Nothing checks it against the provider, and a wrong line is believed. - The store's own isolation. A test copy of the store has to be told apart from the real one, per checkout. A shell started in one checkout keeps that checkout's settings when it moves into a worktree, which is the sub-agent case, so the check has to be made where the write happens, not where the shell started.
- Roles against tasks. The guidance on least privilege for agents argues that a static role is too broad, because an agent picks its tools at runtime (Okta's view; see Sources). Rule 4 concedes the point without solving it.
- Delegation depth. An agent that starts another agent is not modelled.
Nested
actclaims record who acted at each hop, not what each hop was allowed, and only the outermost actor counts for access control.draft-liu-oauth-chain-delegationadds a per-hop record for that. - Derive, don't store, is a different family entirely. The whole layout
assumes a stored secret with a record derived from it. The Spectre/Master
Password algorithm (spectre.app;
exercised locally via
babashka-workshop-spectre) instead recomputes a site's password from a master name and passphrase every time, storing only non-secret derivation parameters (a counter, an output template, a variant). Nothing here says whetherkind=/=effect=/=statestill make sense for an entry that is never at rest – or whether a derived secret belongs in this store at all, versus being out of scope for it the way hardware factors already are.
10. Sources
- draft-klrc-aiagent-auth-00 – identifier, primary and secondary credentials, static keys; replaced by draft-ietf-wimse-aims, same text
- RFC 8693: OAuth 2.0 Token Exchange –
act,may_act - draft-ietf-wimse-identifier-03, SPIFFE-ID – the identifier shape
- draft-ni-wimse-ai-agent-identity-03 – binding an agent to an accountable party
- draft-araut-oauth-transaction-tokens-for-agents-02 –
actandagentic_ctx - draft-liu-oauth-chain-delegation-00 – per-hop constraints in a delegation chain
- CSA: IETF's Race to Standardize AI Agent Identity – where the IETF work sits
- W3C Agent Identity Registry Protocol Community Group
- AIP: Agent Identity Protocol for Verifiable Delegation Across MCP and A2A – DID 1.1 status and the cost of resolution
- Identity Management for Agentic AI – the OpenID Foundation whitepaper
- NCCoE: Accelerating the Adoption of Software and AI Agent Identity and Authorization – concept paper, draft, February 2026
- NIST SP 800-57 Part 1 Rev. 5 – cryptoperiods
- NIST SP 800-63B-4 – no periodic password changes
- AWS Security Hub IAM.3, GitHub fine-grained token policy – vendor rotation defaults
- AI Identity: Standards, Gaps, and Research Directions for AI Agents
- Okta: least privilege for AI agents – roles against task scope; one vendor's view
- NHI Management Group on the OWASP NHI Top 10 – rotation scope; secondary source
- A Survey of Agentic Reasoning for Large Language Models, Large Language Model-based Data Science Agent: A Survey – recurring roles
- AgentCore Identity features, AgentCore Identity terminology – workload identity ARN, token vault, credential providers
- Google Cloud: Use Agent Identity with Agent Runtime – SPIFFE-based principal, certificate-bound tokens
- GitHub Actions OIDC reference, GitHub environments – subject format,
id-token: write, environment secrets - Hugging Face access tokens, LiteLLM budgets and rate limits, Grafana service accounts
11. Related
- Identity Store Specification: A Stack of Drafts – this layout as a sealed specification, v1 to v7
- Identity Store Enterprise Ontology – the same v1 formalized in OWL/RDF and SHACL, with a worked LiteLLM virtual-key example
- Naming for Isolation – how names become paths, branches and ports, and which escapes collide
- Agent Identity and Attestation – who acted and who claims what
- JSON Web Tokens – the token substrate
- Agent Permission Guardrails – what an agent may do once it holds a credential
- Dashboarding Agent Sessions from Hook Events – the other half of the grid