---
title: 'ADR 0006: Passport format'
description: A signed, versioned snapshot for presenting identity to third parties — and deliberately never the authorization decision.
label: '0006 Passport format'
---

## Context

An agent sometimes needs to present identity to something that is not Veefore:
a partner's agent, a third-party system, an auditor. "Trust me, I am
`finance-agent` from Acme" is not a claim anyone can verify.

A signed document solves that. The temptation, once you have one, is to use it
for authorization too — it already lists capabilities, it is signed, and
checking it is a single signature verification with no database read.

That temptation is the decision.

## Decision

A **signed, versioned snapshot** of an agent, Ed25519 over canonical JSON with
the platform key.

```json
{
  "passport_version": 4,
  "agent": { "id": "agt_01JAV…", "name": "finance-agent", "status": "ACTIVE",
             "verification_status": "VERIFIED" },
  "organization": { "id": "org_01JAV…", "name": "Acme Payments" },
  "environment": { "id": "env_01JAV…", "type": "PRODUCTION" },
  "capabilities": ["refund.create", "order.read"],
  "protocols": ["HTTP", "MCP"],
  "trust": { "identity_verified": true, "organization_verified": true,
             "security_attestation_valid": true, "protocol_compliance_valid": false },
  "key": { "id": "key_8f2c…", "algorithm": "Ed25519", "public_key": "MCowBQYDK2Vw…" },
  "issued_at": "2026-10-03T09:00:00.000Z",
  "expires_at": "2026-11-02T09:00:00.000Z",
  "signature": "…"
}
```

**And the gateway never authorizes from it.**

On every protected action the gateway re-resolves live state: the agent's current
lifecycle status, its current capability grants, the current delegation chain,
the currently active policy version, and the risk and trust state as of now.

**Versioned, not mutated.** Issuing supersedes the previous version, so there is
a history of what the agent looked like at each point.

**30-day default lifetime** (`PASSPORT_TTL_SECONDS`).

**Three verification endpoints**, for three different situations:

| | Answers |
| --- | --- |
| `POST /v1/passports/{agentId}/verify` | Is this passport we issued still accurate? Checks the signature **and** live state |
| `POST /v1/passports/verify-external` | Is this document someone handed me authentic? Signature only |
| `GET /v1/passports/{agentId}/export` | Give me the passport plus the verification key, for offline checking |

**No secrets.** The public key is in it; the private half is not and never was.

## Alternatives

**Authorize from the passport.** One signature verification, no database read,
and it would make the gateway faster.

It would also mean revocation does not take effect until the passport expires —
up to 30 days. Everything else in Veefore is built so that containment is
immediate: pause, isolate, revoke, disable, block. A passport-based
authorization path would be the one place where "we stopped it" meant "we
stopped issuing new ones".

Rejected, and it is the whole point of this record.

**Short-lived passports, authorized from, reissued frequently.** A 60-second
passport would bound the staleness. It also reintroduces an issuance round trip
on the hot path, so you have replaced a database read with a signing operation
and gained nothing except a 60-second revocation delay.

**A W3C Verifiable Credential.** Standards-aligned, with existing tooling. Also
a substantially larger document, a JSON-LD processing requirement, and a
canonicalisation story that is harder to implement identically in two languages
— which matters, because cross-language signature agreement is tested.

The passport is a narrow, specific document. The standard is built for a general
problem.

**An X.509 certificate.** Universally verifiable. Awkward for structured
application claims — capabilities, trust evidence, the environment type — which
end up in extensions nobody reads.

**Mutable, with no versioning.** Simpler: one row per agent, updated in place.
Loses the ability to say what the agent looked like when a six-month-old action
was taken, which is exactly what an investigation needs.

## Consequences

**The distinction has to be taught.** "Use the passport for *who*, use the
gateway for *whether*" is stated on
[the passport page](/concepts/passport), in
[the boundary](/secure/boundary), and in the
[FAQ](/faq). People reasonably assume a signed document listing capabilities is
an authorization token.

**A2A is the one place it is load-bearing.** Registering a connection means
registering the remote party's expected key; verification checks their passport
against it. A connection stays `PENDING` and unusable until it verifies.
[ADR 0010](/architecture/adr/0010-a2a-architecture)

**Verifying an external passport does not tell you the agent is in good
standing.** Only the issuer knows that. Documented, and the reason trust
evidence exists alongside it.

**Rotating the platform signing key invalidates every issued passport.** A
planned migration: issue new passports for active agents, let partners pick up
the new published key, retire the old one. Not a config change.

**Reissuance has to be triggered by material changes** — a new capability, a
lifecycle transition, new trust evidence. A stale passport verifies
cryptographically and describes something that is no longer true, which is why
`POST /v1/passports/{agentId}/verify` compares against live state rather than
only checking the signature.

**Canonicalisation must match byte for byte across languages.** Sorted keys, no
insignificant whitespace, `undefined` omitted. Verified by
`npm run smoke:sdk-python`, which asserts the Python and TypeScript
implementations produce identical bytes.

## Related

- [Agent Passport](/concepts/passport)
- [The boundary](/secure/boundary)
- [A2A](/build/a2a)
- [Identity model](/architecture/adr/0005-identity-model)
