Phanes ID · v0.5

The identity document for agents and robots

An issuer-sealed document bound to the agent's own key. A 160-byte Mark any device recognises in nanoseconds. A printed token that verifies in the browser the moment it is scanned. Certification tiers, insurance a venue can check, and a signed record of everything the agent did. Ed25519 and FIPS 204 ML-DSA-87 seals on every artefact.

SD-JWT document160-byte MarkEd25519 + ML-DSA-87IA software · IR robotsphanes.app/id

The document

One card. Everything a counterparty needs.

The data page is rendered by the registry from the sealed document. The holder sees every field; everyone else sees the issuer-sealed fields with disclosable claims marked as not disclosed. The QR is not a link to a record: it is the sealed token itself.

A Phanes ID data page: ink panel with the Phanes logo, the sealed token as a QR code and the ID number; paper panel with agent, controller, model, validity, subject, holder key and capabilities

The holder's own view of a software agent's ID, exactly as the registry rendered it during the recorded evidence run (document IA). Geist, the stone palette and the logo geometry are the phanes.app design tokens.

01Phanes ID number

Nine characters, A to Z and 0 to 9, derived from the issuer key, the holder key and fresh randomness.

02Document type

IA for a software agent, IR for a physical robot or device bound to attested hardware.

03Agent, controller, jurisdiction

The agent's name and type, the legal entity behind it, its ISO 3166-1 jurisdiction.

04Model

Provider and model id, marked attested when the provider signed for it.

05Holder key

The agent's own Ed25519 key. Every presentation is signed fresh with it; the document is bound to it (cnf).

06Capabilities and authority bounds

What the agent may do and integer limits on how far, sealed by the issuer.

07Validity, status references

Issue and expiry times, and the bit each verifier checks in the signed revocation and suspension lists.

08Selective disclosure

Disclosable claims travel as salted digests (SD-JWT). The holder reveals only what a venue needs.

What a scan does

The QR carries the token. The browser does the checking.

Every printed ID encodes https://phanes.app/id#<token>. The token is the 160-byte classical Mark in base64url. It sits in the address fragment, which the browser never sends to any server. The page reads it, verifies the issuer's seal against the key published on its own origin, checks the signed status lists, and shows every check with its reason.

Eight checks, in order

01

Token

160 bytes read from the address fragment: magic, layout and character set accepted.

02

Issuer

The key id inside the token matches the issuer key published on this origin.

03

Signature

Ed25519 over the sealed frame, verified with WebCrypto or the in-page reference implementation.

04

Validity

Issued before now (five minutes of skew allowed), not yet expired.

05

Revocation

The token's bit in the issuer-signed revocation list is clear.

06

Suspension

The token's bit in the issuer-signed suspension list is clear.

07

Hybrid seal

The printed token carries the Ed25519 seal; the ML-DSA-87 seal on the same ID is checked by the registry and phanes-core.

08

Registry

When the anchors name a registry, its live verdict on the hybrid Mark is shown alongside.

Verdicts are Valid, Not valid, Revoked, Suspended or Expired. Without the issuer anchors the page says Unverified; it never says Valid without the key.

phanes.app/id showing a Valid verdict for a token with all eight checks passed

A real token opened in Chromium during the recorded evidence run. The same page, served by any registry at its own /id, verifies that registry's tokens without reaching phanes.app.

The Mark

160 bytes. Recognised in nanoseconds, verified in microseconds.

The Mark is the machine form of the ID: a fixed layout an interface can recognise at the packet boundary with no parsing and no allocation, then verify with one strict Ed25519 check and two status-bit lookups. The hybrid form adds a FIPS 204 ML-DSA-87 signature over the same bytes. The Rust core ships as a crate, a C ABI for firmware, and a JSON binary for any language.

6.6ns
recognise a Mark
52µs
verify Ed25519
241µs
verify ML-DSA-87
311µs
verify the hybrid Mark

Medians from the recorded benchmark session (phanes-core, Intel Xeon 2.10 GHz, single core). Recognition of junk stops at the first byte in under a nanosecond.

OffsetBytesFieldMeaning
04magicPHNS
41version1 classical, 2 hybrid
51kind1 = identity
62flagshardware-bound, insured, certified
89numberPhanes ID number, ASCII
171levelcertification level 0 to 3
208issued_atmilliseconds, big-endian
288expires_atmilliseconds, big-endian
368issuer_key_idSHA-256 of the issuer key, first 8 bytes
4432holder_keythe agent's Ed25519 public key
764revocation_indexbit position in the revocation list
804suspension_indexbit position in the suspension list
8412status_list_hashSHA-256 of the list id, first 12 bytes
9664ed25519issuer signature over the framed body
1604627ml_dsa_87FIPS 204 signature, version 2 only

Rust crate phanes-core

recognize, verify and verify_hybrid on the raw bytes. No allocation on the recognition path.

C ABI for devices

phanes_recognize and phanes_verify_mark_pq from include/phanes_core.h; the C++ example is compiled and run in the test suite.

Python and JSON

aeos.mark.PhanesCore binds the library through ctypes, falls back to the JSON-stdio binary, then to a pure-Python reference with the same verdict.

Security posture

Two signatures on everything. Nothing served off the record.

Built for the environments that will ask the hardest questions. Every issuer artefact is sealed twice, every request the registry serves is provable afterwards, and the page a scan opens cannot be quietly changed.

Hybrid seals

Ed25519 · ML-DSA-87 (FIPS 204)

Every issuer artefact carries both signatures over the same bytes: the document, the Mark, the status lists, certifications and policies. A verifier can require the lattice signature and refuse classical-only tokens.

Holder binding

cnf · key-binding JWT · single-use nonces

The document is bound to the agent's key. Every presentation is signed for one audience and one nonce; replay fails.

Status lists

W3C Bitstring Status List · issuer-signed

Revocation and suspension are bits in signed, cacheable lists. A list that fails verification is not loaded and verification fails closed.

Registry audit trail

hash chain · Merkle tree · signed tree heads

Every request the registry serves is an entry with an inclusion proof against an operator-signed head. Nothing is served off the record.

The scan page

hash-pinned CSP · same-origin anchors

phanes.app/id runs one inline script pinned by SHA-256 and loads issuer keys only from its own origin; a link cannot substitute foreign anchors.

Abuse controls

per-caller token bucket · security headers

Rate limits keyed by signing key or address, 429 with Retry-After, nosniff, no-referrer, no framing, HSTS on TLS.

Physical agents

Robots carry the same document, bound to attested hardware.

A robot, vehicle, drone or embedded device applies with a device profile and a manufacturer attestation over its measured firmware and serial. The issuer verifies the attestation against the manufacturers it trusts, issues an IR document, and sets the hardware-bound flag in the Mark so a gate can tell a device from a process before any cryptography runs.

Device profile

Kind, manufacturer, model, a digest of the serial, the hardware root of trust (TPM 2.0, secure element, HSM, TEE) and the firmware digest.

Manufacturer attestation

A signed statement over the unit's key fingerprint, serial digest and firmware digest. A mismatch on any of them does not bind.

Robotics insurance

Physical damage and bodily injury lines, available only to hardware-bound IR documents at Verified or above.

A Phanes ID data page for a warehouse robot: document IR, device line with manufacturer, model, TPM 2.0 root of trust and serial, hardware-bound chip

A warehouse unit's ID from the evidence run: the manufacturer attested the measured firmware, the registry bound the device, the card shows the root of trust and the Hardware-bound chip.

Certification

Four tiers, every input echoed back.

The tier is computed by deterministic rules over verifiable inputs and issued as a signed credential a counterparty verifies offline. The credential lists every check it used, so a verifier sees why the tier was granted, not only that it was. The badge is communicated in HTTP headers and MCP metadata.

LevelTierAdds
0RegisteredA valid ID, not revoked or suspended.
1VerifiedController KYB attestation and model-provider attestation, both verified by the issuer at issuance.
2CertifiedInsurance in force with a per-claim limit at or above the agent's transaction bound, and an operator-anchored activity record.
3Institutional90 days of history, 500 events, adverse outcomes under 1%, a tenth of events witnessed by venues, no paid claims in a year.
Phanes Certified badge: logo, gold seal, ID number, validity and insured

The badge for a Certified agent from the evidence run. It is rendered from the credential, never typed: the number, the expiry and the insured state are read from the signed payload.

Sealed like the document

The certification board signs with Ed25519 and ML-DSA-87. A verifier can demand a minimum level and proof of insurance in one call.

Insurance

Four packages. Premiums from the record, claims from the proof.

Quotes are computed deterministically from the ID, its tier and its activity history, with every rating factor returned. A claim names an event in the agent's activity record; the registry verifies its inclusion proof against a signed tree head before rules adjudicate it. Limits below are per claim and aggregate, in USD.

Starter

Registered

Individual builders and early-stage agents

Errors and omissions$25,000 per claim$50,000 aggregate
Unauthorized action$10,000 per claim$25,000 aggregate

Deductible $250

Professional

Verified

Production agents transacting with third parties

Errors and omissions$250,000$500,000
Unauthorized action$100,000$250,000
Non-delivery$100,000$250,000
Data breach$100,000$200,000

Deductible $1,000 · $2,500 data breach

Institutional

Certified

Financial institutions, government, large enterprise

Errors and omissions$2,000,000$5,000,000
Unauthorized action$1,000,000$2,000,000
Non-delivery$500,000$1,000,000
Fraud$500,000$1,000,000
Data breach$1,000,000$2,000,000
Erroneous output$500,000$1,000,000

Deductible $5,000 · $10,000 on the two largest lines

Robotics

Verified · hardware-bound

Physical robots, vehicles, drones and embedded agents

Errors and omissions$250,000$500,000
Unauthorized action$250,000$500,000
Physical damage$1,000,000$2,000,000
Bodily injury$2,000,000$4,000,000

Deductible $2,500 · $5,000 · $10,000 by line

Activity record

What the agent did, signed by the agent, anchored by the registry.

Two-phase append

01

The registry prepares the exact event body: sequence, previous hash, actor fingerprint, timestamp

02

The agent signs it with the holder key; a venue may add a witness signature inside its visa

03

The registry commits it as a leaf of an RFC 6962 Merkle tree and signs the tree head

04

Anyone verifies inclusion and consistency proofs against the signed head

Who, what, when, where, how, why

Each event carries the tool invoked, the venue, the jurisdiction, the method and model, and the outcome.

Transcripts and claims

Operator-signed transcripts summarise the log; insurance claims and certification read from it, never from prose.

record.sh
# hash a real file, record the invocation with its digest, size and exit code
H=$(sha256sum aeos/phanes_id.py | cut -d' ' -f1)
phanes record --server https://registry.example --key agent.key IA1234567 \
  --type tool.invoked \
  --what '{"tool":"sha256sum","path":"aeos/phanes_id.py","sha256":"'$H'","exit_code":0}' \
  --where '{"venue_did":"did:phanes:venue:desk","jurisdiction":"USA"}' \
  --how '{"method":"bash","model_id":"claude-fable-5-1"}'

# the signed summary, tree head and an inclusion proof for event 3
phanes transcript --server https://registry.example IA1234567
phanes audit --server https://registry.example --inclusion 3

Proven in the open

Recorded, not described.

Every command of the evidence run was executed under a pseudo-terminal recorder and kept with its raw typescript and SHA-256: the suites, a walkthrough against a live registry, a 112-agent campaign with an adversary running 43 attacks, the browser verifier driven in Chromium, and two documents built only from those files. Every session is on the evidence page.

112
agents and robots completing every step, 112 certified and 112 insured
336 / 336
peer verifications of hybrid Marks, ML-DSA-87 required
43 / 43
adversary attacks defended
80
recorded terminal sessions, every exit code 0

For builders

An ID in five commands. Verification in one.

The whole flow is the phanes command line, the Python client, the REST surface under /v1, or fourteen MCP tools inside an agent runtime. Keys are generated on the agent and never travel; every request is signed locally.

terminal
pip install 'phanes[server,render,pq]'

phanes keygen --out agent.key
phanes apply --server https://registry.example --key agent.key \
  --name "Ledger Agent" --controller-did did:phanes:org:acme \
  --controller-legal-name "Acme Inc" --jurisdiction USA \
  --model-provider Anthropic --model-id claude-fable-5-1 \
  --capability transact --bound max_transaction_value=2500000 \
  --out agent.id.json
phanes token   --server https://registry.example IA1234567   # https://phanes.app/id#...
phanes certify --server https://registry.example IA1234567
phanes insure  --server https://registry.example --key agent.key IA1234567 --package professional

# anyone: cache the anchors once, verify offline through the Rust core
phanes anchors --server https://registry.example --out anchors.json
phanes verify-mark --file mark.bin --anchors anchors.json --require-pq
client.py
from aeos.client import PhanesClient, OfflineVerifier
from aeos.phanes_id import HolderKeys

keys = HolderKeys.generate()                       # stays on the agent
c = PhanesClient("https://registry.example", holder=keys)
doc, extras = c.apply(
    agent_name="Ledger Agent", controller_did="did:phanes:org:acme",
    controller_legal_name="Acme Inc", controller_jurisdiction="USA",
    model_provider="Anthropic", model_id="claude-fable-5-1",
    capabilities=["transact"], authority_bounds={"max_transaction_value": 2_500_000},
)
print(c.token_url(doc.id_number))                  # what the QR carries

v = OfflineVerifier.from_registry(c)               # anchors + status lists, cached once
assert v.verify(c.mark(doc.id_number))["ok"]       # Ed25519 and ML-DSA-87, offline
MCP tools for agent runtimes
phanes_id_applyphanes_id_showphanes_id_presentphanes_verify_challengephanes_id_verifyphanes_activity_recordphanes_activity_transcriptphanes_insurance_packagesphanes_insurance_quotephanes_insurance_bindphanes_insurance_claimphanes_mark_getphanes_mark_verifyphanes_certification_evaluate

Venues and gateways

Verify a presented Mark or an SD-JWT presentation in one call; require a tier, insurance and a fresh nonce.

Self-hosted registries

Run phanes serve with your own Ed25519 and ML-DSA-87 keys, register the attesters you trust, verify your own tokens at your own /id.

Air-gapped verification

phanes verify-mark --classical --anchors and the C ABI need no network at all; nothing reaches phanes.app.

Read the full guide, then run it yourself

The documentation covers the document, the Mark, the printed token and the browser verifier, robots, packages, the audit trail and every route and tool. The repository carries the code, the tests and the recorded evidence.