Your autonomous agents already read your inboxes, place orders, triage tickets, book flights. Downstream services have no way to answer the four questions that matter: which agent is calling, who owns it, what it may do, and how to revoke it. The CodeB Business Wallet gives every agent a DID, a stored capability list, revocable ES256 capability tokens signed by your tenant issuer, and a public manifest for downstream services. Built on W3C DID Core, W3C VCDM 2.0, OpenID4VCI, HAIP Wallet Attestation and W3C Bitstring Status List. No new stack. No new database.
agent role, capability tokens, agent manifest, verify-token endpoint, and Bitstring Status List credential are all shipping in the reference implementation today. Add an employee with role AI Agent in web-business-wallet.html, mint a scoped token, and hand it to your agent runtime.
The last eighteen months of production AI agent deployments have surfaced the same gap in every team we have spoken to.
Long-lived, high-entropy secrets. Any process on the box can steal one and replay it. Rotation is a manual chore. There is no ownership signal — the key does not know it belongs to a specific agent, model, or purpose.
Better than API keys, but still opaque: the downstream server sees a client ID and a JWT with a scope. It cannot answer which of my five agents presented this token, or what the agent was designed to do.
The Model Context Protocol and A2A both standardise how agents talk to tools and to each other — neither specifies how the server verifies which agent is on the socket. Every team writes their own identity glue.
The upcoming EU AI Act asks who is accountable for an agent's actions. If your only audit record is "an API call landed at 09:12 with a bearer token", you cannot answer that question. You need agent-level attribution baked in.
Every building block already exists in the CodeB Business Wallet. This feature stitches them together for a machine-identity use case.
Each agent gets a fragment DID under its wallet: did:web:tenant:business-wallets:acme#agent-e_abc123. Owner DID is the wallet itself. Resolvable via the wallet's DID Document.
Every capability token is a JWT-format Verifiable Credential (ES256, W3C VCDM-shaped payload). Downstream services verify with the tenant issuer public key from the DID document.
HAIP §5.11 Wallet Attestation is already implemented per-employee. Agents inherit the same attestation primitive when they need to prove wallet residency.
Short-lived (max 24 h, configurable per-agent) ES256 JWTs with typ=codeb-agent-token+jwt. Explicit scope claim. TTL clamped by the operator-set agent_max_ttl_sec.
Public revocation URL published in every agent manifest. Agent-level revocation via deactivate-agent is immediate; per-token bit-flipped revocation is on the roadmap.
Every mint and every deactivate is written to the wallet audit log with {ts, actor, action, target}. Downloadable as JSON. Rotated at 5000 entries.
Open web-business-wallet.html, sign in with OIDC, mint a wallet for your company. You are auto-enrolled as director.
Add an employee with role “AI Agent”. Fill in model tier, purpose, allowed capabilities, and max token TTL.
Click Mint capability token. Pick a scope subset. Copy the ES256 JWT. Ship it to your agent runtime as a bearer credential.
Your server calls verify-agent-token or fetches the public agent manifest to pin the issuer JWK, then verifies tokens offline.
Click Deactivate in the UI. The manifest starts returning active:false. All new verify calls fail with agent_deactivated.
POST /business-wallet.ashx?action=mint-agent-token&bw=acme-ltd
Authorization: Bearer <OIDC token from director>
Content-Type: application/json
{
"empId": "e_abc123",
"ttl_sec": 3600,
"scope": ["list-docs","download-doc","verify-vc"],
"audience": "https://downstream.example/api",
"purpose_hint": "Q3 invoice triage batch"
}
POST https://tenant.example/business-wallet.ashx?action=verify-agent-token
Content-Type: application/json
{"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImNvZGViLWFnZW50LXRva2VuK2p3dCJ9..."}
# Response
{
"verified": true,
"reason": "ok",
"agent_did": "did:web:tenant.example:business-wallets:acme-ltd#agent-e_abc123",
"owner_did": "did:web:tenant.example:business-wallets:acme-ltd",
"scope": ["list-docs","download-doc","verify-vc"],
"exp": 1786320000,
"typ": "codeb-agent-token+jwt"
}
GET https://tenant.example/business-wallet.ashx?action=agent-manifest&bw=acme-ltd&emp=e_abc123
# Returns name, model, capabilities, owner DID, issuer JWK, revocation URL.
# Verifiers pin issuer_key.x and issuer_key.y once, then verify tokens offline.
| Question | API keys | OAuth service accounts | Business Wallet agent identity |
|---|---|---|---|
| Which agent is calling? | no | client-id, not agent | agent DID |
| Who owns the agent? | no | tenant | owner DID + wallet |
| What model / purpose? | no | no | manifest field |
| Explicit scope per token? | no | yes | yes, clamped by stored caps |
| Rotation | manual | library | ttl_sec clamp |
| Immediate revocation | rotate + redeploy | revocation list | deactivate-agent |
| Public verifier endpoint | no | JWKS | verify-agent-token + manifest |
| Standards-based | no | OAuth 2.1 | W3C DID + VCDM + Bitstring |
| Portable across services | shared secret leak | per-integration | bearer JWT anywhere |
| Aligns with MCP + A2A | no | bolt-on | yes — transport-agnostic |
An agent reads shared inboxes, files invoices to accounting, flags contracts to legal, drafts replies to routine customer emails. The mail server, the accounting API and the ticketing tool all need to know: who authorised this action, and can it be undone? A capability token with scope read-mail,file-invoice,create-ticket gives each downstream service a machine-verifiable answer.
An agent shops for cloud resources, negotiates SaaS renewals, purchases API credits under a spending cap. Every purchase order carries the agent DID, the owner DID and an explicit purchase-goods,max-eur-500 scope. Vendor audit trails now attribute spend correctly. Kill switch is one deactivate-agent call away.
A hundred robotic-process-automation bots run overnight against SAP, Salesforce, and an on-prem AS/400. Instead of a shared service account per system, each bot gets its own capability token scoped to exactly the actions its workflow requires. When one bot goes rogue, revocation is bot-scoped, not fleet-scoped.
Coding agents commit patches, open PRs, run CI pipelines, deploy to staging. Each action is signed by the developer’s personal agent. When a suspicious commit appears at 02:00, the audit log tells you which agent, which owner, which scope — not just “the CI service account”.
A public-facing agent answers billing questions, issues refunds up to a threshold, escalates edge cases to humans. Downstream refund and CRM systems verify the incoming token, see the scope issue-refund,max-eur-100, and honour or reject accordingly. No shared credential; no way for a hostile prompt to elevate privileges beyond the minted scope.
Nothing here is invented. This layer is a pragmatic assembly of published specifications applied to the AI agent use case.
Every agent identifier is a well-formed did:web with a fragment. Owner DIDs are resolvable at /business-wallets/<cid>/did.json.
The agent status list is a BitstringStatusListCredential in the W3C Verifiable Credentials Data Model 2.0 shape.
The tenant issuer that signs capability tokens is the same ES256 key that signs OID4VCI credential offers. One trust anchor, many usages.
Agents can present a Wallet-Instance-Attestation when the downstream service wants proof that the caller runs inside a specific wallet.
Per-tenant revocation credential published at a stable public URL and linked from every agent manifest.
Capability tokens are bearer credentials on the wire. They plug into any MCP tool server or A2A dispatcher that already accepts bearer authentication.
Built on the same architecture as the CodeB EUDI wallet stack: eIDAS 2.0 primitives, ARF 3.0 alignment, and future-proof against the emerging EU Business Wallet vocabulary.
A capability token is a bearer JWT: any OAuth 2.1 resource server that accepts JWT bearer tokens and can fetch a JWK from a discovery URL can accept an agent token unchanged.
So downstream services can answer the four questions that matter: which agent is calling, who owns it, what it is allowed to do, and how to shut it off. API keys answer none of these. OAuth service accounts only answer the first two, and their credentials are long-lived and manually rotated. A verifiable-credential-backed identity answers all four, with revocation, audit and cryptographic ownership by construction.
A service account is a static shared secret that any process on the machine can steal and replay. A capability token is a short-lived ES256 JWT signed by a tenant issuer key. It carries the agent DID, the owner DID and an explicit scope; the downstream verifier fetches the public agent manifest to check the agent is still active and to obtain the issuer public key. Nothing is shared. Nothing has to be stored on the verifier.
Yes. MCP standardises how an agent talks to a tool server; A2A standardises how agents talk to each other. Neither specifies how the downstream server proves which agent is on the other end of the socket. The CodeB agent identity layer fills that gap: the agent presents a bearer capability token, the server calls verify-agent-token, done. It is transport-agnostic and works with MCP, A2A, HTTP APIs, gRPC, message queues, or anything else that can carry a bearer token.
Yes. Call deactivate-agent on the wallet. The public agent manifest starts returning active:false, and every verify-agent-token check for that DID starts failing with reason: agent_deactivated. Existing tokens are cryptographically valid but the agent is denied, exactly like an OAuth revocation. Per-token bit-flipped revocation via W3C Bitstring Status List is on the roadmap for token-granularity cases.
An ES256 (ECDSA P-256 SHA-256) compact-serialisation JWT with header typ=codeb-agent-token+jwt. Payload: iss (owner DID), sub (agent DID), agent_did, owner_did, role, scope (array), iat, nbf, exp, wallet_id (URN), wallet_link, manifest, did_doc, optional aud and purpose_hint, and a random jti. Signed by the tenant issuer key which is discoverable via the DID document.
No. verify-agent-token and agent-manifest are public endpoints. A downstream service fetches the manifest once (60 s cache), pins the issuer JWK, and can then verify tokens fully offline. If it prefers, it can call verify-agent-token on every request to also get an active-flag check without maintaining state itself.
did:web:<tenant>:business-wallets:<companyId>#agent-<empId>. The wallet DID itself is resolvable at /business-wallets/<companyId>/did.json; the #agent-* fragment identifies a specific agent within that wallet. Owner DIDs are always without the fragment.
Any of the read-only wallet actions (list-docs, download-doc, list-vc, verify-vc, get-state, list-my-wallets), plus any custom capability slug you want to define. Custom slugs are opaque to the wallet: it just carries them in the token, and your downstream service interprets them. Mutating wallet operations (upload-doc, add-employee, wallet-export, and so on) are permanently off-limits to agents by design.