# Agent Passport — Protocol & MVP Spec v0.1

**Working name:** Agent Passport · **Category:** KYA (Know Your Agent) · **One-liner:** Let verified agents transact.

**The problem.** Platforms receive API traffic from AI agents that want to do real economic actions — refill an account, place an order, subscribe to a fund. Today they block it as fraud, because no compliance regime can answer: *who committed this action?* Is it a human or an agent; if an agent, whose; under what authority; with whose liability?

**The product.** A passport service that resolves any agent action to its **chain of agency**:

```
action ← agent (keypair, runtime metadata)
       ← mandate (scope, limits, expiry, revocable)
       ← principal (KYC'd human or entity)
       ← liability
```

KYC verified a point. The passport verifies the chain. Platforms integrate one middleware ("require KYA"); principals onboard once and issue scoped mandates to their agents; agents present signed assertions with every call. We are the neutral attestation layer — **records, not rails**: we never custody or move funds.

---

## 1. Actors and objects

| Object | ID prefix | What it is |
|---|---|---|
| Principal | `prn_` | KYC'd human or entity that bears liability. KYC via pluggable vendor interface (MVP: sandbox stub with the same interface shape as Persona/Stripe Identity). |
| Agent | `agt_` | A registered agent identity: Ed25519 keypair + self-declared runtime metadata (name, model, framework). Key held by the agent runtime; public key registered with us. |
| Mandate | `mnd_` | A signed, scoped, revocable grant from principal to agent. The heart of the system. |
| Platform | `plt_` | An integrator (brokerage, exchange, SaaS) with an API key, calling our Verify API. |
| Verification | `vrf_` | An immutable, signed audit record of one allow/deny decision, co-signable by the platform (counterparty co-signing from day one). |

## 2. Mandate schema

```json
{
  "id": "mnd_...",
  "principal": "prn_...",
  "agent": "agt_...",
  "scopes": [{
    "action": "account.refill",
    "max_amount_per_tx": 100000,
    "max_amount_per_period": 250000,
    "period": "week",
    "currency": "USD",
    "platforms": ["plt_..." ] ,
    "purpose": "trading-capital"
  }],
  "not_before": "...", "expires_at": "...",
  "status": "active | revoked",
  "issued_at": "...", "issuer_sig": "<JWS by passport issuance key>"
}
```

Amounts in minor units. MVP issuance is custodial (we sign mandates after the principal authorizes in the dashboard — the CA model); the documented upgrade path is principal-held keys / W3C Verifiable Credentials with identical semantics.

## 3. Presentation & verification flow

1. **Agent → Platform:** normal API call plus header `X-Passport: <JWS>`, where the JWS is signed by the **agent key** over `{ mandate_id, action, amount, currency, platform_id, nonce, ts }`. Nonce + timestamp prevent replay (±120s window, nonce single-use).
2. **Platform → Passport:** `POST /v1/verify` with the raw assertion + platform context. One round trip.
3. **Passport checks:** agent signature → mandate active/not expired/not revoked (live check — revocation is instant) → scope covers action+amount+platform → cumulative period counters (we maintain them) → principal KYC status → sanctions screen (stub interface in MVP) .
4. **Response:**

```json
{
  "decision": "allow | deny",
  "reason": "ok | mandate_revoked | per_tx_cap | period_cap | kyc_lapsed | unknown_agent | replay | ...",
  "chain": {
    "principal": { "type": "individual", "country": "US", "kyc": "verified", "accredited": true },
    "agent": { "id": "agt_...", "name": "treasury-bot", "runtime": "claude-code" },
    "mandate": { "id": "mnd_...", "remaining_this_period": 150000 }
  },
  "verification_id": "vrf_...",
  "receipt": "<JWS signed by passport>"
}
```

**Selective disclosure:** the platform sees attributes (KYC level, country, accreditation flag), never the principal's identity documents — unless the mandate explicitly grants disclosure or a lawful request compels it. Subject access, correction, and dispute flows are first-class (FCRA-shaped by design; regulation becomes moat).

5. **Co-signing:** platform may `POST /v1/verifications/{id}/countersign` after executing the action. A record signed by both sides is nearly incontestable — this is the Evidence-Layer L2 law, implemented from day zero.

## 4. API surface (MVP)

- `POST /v1/principals` + sandbox KYC flow (dashboard)
- `POST /v1/agents` (register public key) · `POST /v1/agents/{id}/rotate`
- `POST /v1/mandates` · `POST /v1/mandates/{id}/revoke`
- `POST /v1/verify` · `POST /v1/verifications/{id}/countersign` · `GET /v1/verifications` (audit export)
- Platform dashboard: API keys, decision log. Principal dashboard: agents, mandates, one-click revoke, activity.

## 5. SDKs

- **Platform middleware** (`@passport/verify`): Express/Hono/Next middleware — `requireKYA({ action: "account.refill" })` — ~10 lines to integrate, fails closed.
- **Agent SDK** (`@passport/agent`): holds the keypair, signs assertions, attaches headers. Plus an **MCP server** exposing `passport_present` / `passport_status` tools so Claude Code (and any MCP-capable runtime) can transact with a passport today.

## 6. The demo (this IS the pitch)

**"Northbank Sandbox"** — a fake brokerage with a real API, integrated via the middleware:

1. Principal signs up, passes sandbox KYC, registers agent `treasury-bot`, issues mandate: `account.refill` ≤ $1,000/tx, ≤ $2,500/week at Northbank.
2. Agent (via MCP in Claude Code, or the CLI) refills **$500 → ALLOW** — balance updates, receipt shows the full resolved chain.
3. Agent attempts **$2,000 → DENY (per_tx_cap)**.
4. Principal revokes the mandate in the dashboard; next attempt → **DENY (mandate_revoked)** within seconds.
5. Anonymous agent (no header) → **403 "KYA required"** with an onboarding link — the block-page that turns rejected traffic into leads.

## 7. Positioning & interop

- **Rails-neutral by law of the category:** interoperate with x402 / AP2 / ACP-style agentic-payment flows rather than compete — AP2's mandates and our mandates should map 1:1; we are the KYA/identity layer any rail can call. Token format tracks W3C VC/JWS so a standards migration is a serializer change, not a rewrite.
- **Where we hunt first:** platforms losing agent-originated volume they'd love to accept — trading/prop platforms, exchanges/on-ramps, ad-budget top-ups, API-credit refills, marketplaces. The refill/deposit primitive generalizes everywhere.
- **Regulatory posture:** attestation layer only. No custody, no money transmission, KYC through licensed vendors, subject rights built in. We sell *decisions and records*, not movement of value.
- **Evidence Layer fit:** this is L0 (identity spine). Verifications are L1–L2 records born attested and co-signed. Iris sessions and payment records signed by passported agents become evidence with a resolvable author — same company thesis, three shelves.

## 8. MVP scope fence

**In:** everything above with sandbox KYC + stub sanctions; SQLite via Drizzle; single-region deploy (Railway); demo brokerage; MCP server; CLI; audit export.
**Out (documented, not built):** real KYC vendors, principal-held keys/VCs, webhooks, multi-sig mandates, per-jurisdiction rulepacks, agent reputation scores (that's the L3–L5 climb).
