Treeship
Concepts

Agent Identity

An Agent Identity Certificate is the TLS certificate of an AI agent — a deterministic, signed bundle that says who an agent represents and what it's allowed to do. This page covers the format, how to issue one, and how it binds to session receipts.

A Session Receipt proves what happened. An Agent Identity Certificate proves who is allowed to make that happen, and within what envelope. Together they form the trust fabric: one click from "this agent did X" to "this agent is allowed to do X" — both verifiable on a clean machine with no network call.

The TLS-for-AI-agents analogy, carefully

The web has SSL/TLS certificates: a CA-signed bundle that says "this server represents acme.com, here is its public key, here is the validity window." That's why a browser can show a green lock without the user having to call Acme's IT desk.

AI agents need an equivalent. There's currently no broadly adopted, interoperable way for one agent to prove to another (or to a human reviewer) who it represents, what it's allowed to do, and how it gets paid. Vertically integrated stacks solve this for their own ecosystem; protocol attempts like x402 solve the payment side; MCP solves the application-layer wiring. None of them produce a single signed artifact a third party can verify offline.

The Agent Identity Certificate is Treeship's answer. It is:

  • A single signed bundle under one ed25519 key, deterministic to verify
  • Layered: identity / capabilities / declaration, each independently inspectable
  • Independent of any hosting decision — works for cloud agents, local agents, OSS contributors, enterprise deployments
  • Bound to receipts via cross-verification, so the cert isn't just a claim — it's a contract that gets checked against actual behavior

It is not a CA-issued public-trust certificate. There is no centralized CA in Treeship; identity bottoms out in the ed25519 keypair generated at treeship init. Domain ownership and other trust-amplifying signals are layered on top through separate, optional verification flows.

The three layers in one bundle

Every Agent Identity Certificate is a deterministic JSON document with three signed sections plus a signature block. The format is defined in packages/core/src/agent/ and emitted by treeship agent register.

Layer 1 — Identity (who they represent)

{
  "agent_name": "deploy-bot",
  "ship_id": "ship_6232d021805b8fb5",
  "public_key": "ed25519:gN4kT9pRq2sVxL5wJ8bF6cYzM3aD1eH7iU0oP4nQ2vW8",
  "issuer": "ship://ship_6232d021805b8fb5",
  "issued_at": "2026-04-26T17:00:00Z",
  "valid_until": "2026-07-25T17:00:00Z",
  "model": "claude-opus-4-7",
  "description": "Ships acme staging from agent-driven sessions."
}

Mandatory fields: agent_name, ship_id, public_key, issuer, issued_at, valid_until. Optional: model, description.

ship_id is the cryptographic identity of the issuing machine — it ties this certificate to a specific keystore. Self-issuance is the default (issuer == "ship://" + ship_id); future Hub-mediated issuance can use a different issuer URI.

Layer 2 — Capabilities (what surface they expose)

{
  "tools": [
    { "name": "read_file",  "description": "Read a file from the working tree." },
    { "name": "write_file", "description": "Create or modify a file." },
    { "name": "bash",       "description": "Run a shell command." }
  ],
  "api_endpoints": [],
  "mcp_servers": [
    { "name": "treeship", "command": "npx -y @treeship/mcp" }
  ]
}

Capabilities are the surface — what the agent can call. Optional descriptions help reviewers understand each tool's purpose without grepping the agent's source.

Layer 3 — Declaration (the contract)

{
  "bounded_actions": ["read_file", "write_file", "bash", "git_diff", "fly_deploy"],
  "forbidden":       ["git_force_push", "prod_deploy", "delete_branch_main"],
  "escalation_required": ["prod_db_migrate", "billing_change", "rotate_secrets"]
}

Declaration is the contract — what the agent is allowed to call, what it must never call, and what requires human approval. This is the layer that gets cross-verified against actual session activity.

bounded_actions is usually a subset of capabilities.tools (you might have a tool installed but not authorized for a given session). forbidden and escalation_required exist on the certificate so a verifier can check independently — even an agent that ignores its own contract gets caught at receipt-review time.

Signature

{
  "algorithm": "ed25519",
  "key_id": "key_2026q1_acme_deploybot",
  "public_key": "ed25519:gN4kT9pRq2sVxL5wJ8bF6cYzM3aD1eH7iU0oP4nQ2vW8",
  "signature": "mZkN7tH3xR8cQ4sA1pV5fB2eY6wJ9oU0iL3dG7hT5nM2vX8bC4kP1qS6rW9aE3yF",
  "signed_fields": "identity+capabilities+declaration"
}

Ed25519 signature over the canonical JSON of identity + capabilities + declaration. Deterministic — the same inputs always produce the same signature, which is itself a safety property (see the DSSE explainer for why randomized signatures are dangerous in agent infrastructure).

Issuing a certificate

treeship agent register \
  --name acme-deploy-bot \
  --description "Ships acme staging from agent-driven sessions" \
  --tools read_file,write_file,bash,git_diff,fly_deploy \
  --forbidden git_force_push,prod_deploy \
  --escalation prod_db_migrate,billing_change \
  --model claude-opus-4-7 \
  --valid-days 90

This produces a .agent package in the current directory:

acme-deploy-bot.agent/
├── identity.json       # the identity layer, pretty-printed
├── capabilities.json   # the capabilities layer, pretty-printed
├── declaration.json    # the declaration layer, pretty-printed
├── certificate.json    # the full bundle (all three + signature)
└── certificate.html    # a self-contained shareable card

You can publish the .agent directory to a Hub (so others can fetch the certificate by slug at treeship.dev/agent/<slug>) or distribute it directly. Either way, the trust check is local: anyone with treeship verify --certificate certificate.json can confirm the signature.

Where does the public key come from?

The keypair was generated at treeship init. There is no separate CA enrollment step — your machine is its own root of trust. This is intentional: it keeps Treeship installable in five seconds with no upstream dependency. Domain ownership and other trust amplifiers (below) are layered on top, not gating.

Trust amplifiers (optional)

A bare certificate proves an ed25519 signature. That's enough for offline verification, but a third party reading the cert may want stronger signals. Treeship's identity backend exposes optional verification flows:

AmplifierWhat it provesHow
Domain ownership"This agent represents acme.com"DNS TXT record challenge at _treeship-challenge.acme.com, verified server-side via /identity/{slug}/verify-domain
Voice fingerprint"Communications signed under this cert match the agent's stable style fingerprint"Style-based fingerprinting via /identity/{slug}/voice-fingerprint and /identity/{slug}/verify-voice
Tool manifest verification"The tools this cert declares are the tools the agent actually exposes"Manifest probe via /identity/{slug}/verify-tools

Each amplifier produces an independent attestation that the agent card surfaces as a separate badge. Stronger trust without coupling everything into one mechanism — a single failed amplifier doesn't invalidate the cert; it just narrows what the cert claims.

Binding to session receipts

The cert is half a contract. The other half is the Session Receipt — what the agent actually did. Cross-verification binds the two:

treeship verify --certificate acme-deploy-bot.agent/certificate.json \
  ./.treeship/sessions/ssn_a1b2c3d4.treeship

This runs cross_verify_receipt_and_certificate (in packages/core/src/verify/) and answers four questions:

  1. ship_id_match — does the receipt's ship_id equal the cert's identity.ship_id?
  2. within_validity_window — was the session inside [issued_at, valid_until]?
  3. unauthorized_calls — count of tool calls outside bounded_actions (must be zero for a clean pass)
  4. never_called — declared tools the agent didn't end up using (informational, useful for tightening the next cert)

The function takes now as an explicit argument so the check is deterministic and testable: tests pass a fixed value, the CLI passes SystemTime::now(), the WASM build passes a JS-supplied timestamp. See Cross-verification for the full check matrix.

The agent card at treeship.dev/agent/<slug> runs this on every recent receipt and surfaces the result as a ✓ no unauthorized calls badge per row.

Initial scope: how bounded_actions gets determined

Three sources, in order of trust:

  1. Operator-declared at registration. The --tools / --forbidden / --escalation flags. The agent's owner declares the contract upfront and signs it. This is the canonical source.
  2. Project-declared via treeship declare. Each project can carry its own .treeship/declaration.json that lists which tools agents are authorized to use in this project. The session receipt records both the agent cert's declaration AND the project declaration; cross-verify treats unauthorized = "called but in neither list."
  3. Discoverable from the integration. For MCP-routed agents, the bridge can introspect the tool list the agent connected with. This is the "no-config" path: a fresh treeship add cursor registers a default certificate from Cursor's actual MCP capabilities. (Building.)

The two-axis gate (agent declaration AND project declaration) lets the same agent operate under different policies in different projects without re-issuing certs.

What an agent's URL looks like

Once a certificate is published to a Hub, anyone can view it at:

https://www.treeship.dev/agent/<slug>

Where <slug> is the canonical sanitization of agent_name (lowercase, spaces → dashes, alphanumeric + dash + underscore only). The page renders identity, capabilities, declaration, recent verified receipts, and the treeship verify --certificate snippet for the offline check.

This is the URL operators put in their README, in their docs, on their GitHub profile. It's the public face of the agent's identity — the equivalent of a TLS cert viewer for an HTTPS site.

What this is not

  • Not a CA-rooted PKI. There's no globally-trusted root authority. Trust bottoms out in the ship's ed25519 keypair plus optional amplifiers (domain, voice, tool manifest).
  • Not a hosting decision. The certificate works for cloud agents, local agents, OSS contributors, enterprise deployments. The only requirement is that the issuing machine has a Treeship keystore.
  • Not a payment identity. x402 / lobster.cash flows are wrapped by treeship wrap and recorded into receipts; the identity of who is allowed to spend is a separate concern from who is allowed to act. Future certificates may include a payment layer; today they don't.
  • Not immutable. Certs expire (default 90 days). Re-issue with the same name to publish a new version. The Merkle history of past receipts under the old cert remains verifiable forever — the cert just stops being usable for new attestations.

Where to go next