What an agent passport is
An agent passport is a signed JSON document that identifies an AI agent, declares its operator, and binds it to a governance posture (risk class, capabilities, delegation chain). It is presented as an Authorization header credential when the agent calls an Aegis-verified endpoint.
Aegis-protected endpoints (e.g. /api/protect) check the passport at every request: signature valid, registry status active, expiry in-bounds, governance payload coherent. If any check fails, the request is rejected with a 401 and a correlation_id for self-diagnosis.
Schema versioning at a glance
| Version | Adds | Status |
|---|---|---|
| v1.0–v1.4 | Core identity, permanence_class, risk_classification, registry signature | Pre-Phase-2 baseline |
| v1.5 | governance_payload block (machine-readable governance posture) | Live |
| v1.6 | capability_attestation (attester-signed capability claims, decoupled from operator) | Live |
| v1.7 | (interim — internal staging) | — |
| v1.8 | governance_payload_signature (independent governance signing key, separate from passport sig) | Live, MANDATORY mode toggle in observation window |
A v1.8 passport is backwards-compatible with v1.5/v1.6 verifiers — they read the fields they understand and ignore the rest. Forward compatibility is provided through the schema_version field on the passport root.
Top-level passport fields
Every passport version carries the same identity backbone:
{
"schema_version": "v1.8",
"agent_id": "agent_alpha_001",
"operator_id": "op_examplecorp",
"issued_at": "2026-05-07T22:11:23Z",
"expires_at": "2026-08-05T22:11:23Z",
"permanence_class": "permanent",
"risk_classification": "high",
"governance_payload": { ... },
"capability_attestation": { ... },
"delegation_chain": [ ... ],
"governance_payload_signature": "ed25519:...",
"passport_signature_hex": "ed25519:..."
}
Field semantics
schema_version— version stringv1.5/v1.6/v1.8. Verifiers prefix-match onv1.— addingv1.9will not break existing deploys.agent_id— globally-unique opaque identifier. Immutable forpermanence_class=permanentagents (enforced by registry triggers).operator_id— opaque identifier for the legal entity operating the agent. Immutable for permanent-class.issued_at/expires_at— RFC 3339 timestamps.expires_atis enforced by parser; expired passports reject at/api/protectboundary.permanence_class—transient(short-lived, no registry write),escrowed(registry-tracked, revocable),permanent(write-once, federation-anchored). Class downgrades are blocked by SQLite trigger.risk_classification—minimal/limited/high/critical. Drives advisory-vs-strict gating policy.governance_payload— see v1.5.capability_attestation— see v1.6.delegation_chain— see below.governance_payload_signature— see v1.8.passport_signature_hex— Ed25519 signature over the canonical JSON of the whole passport (excluding the signature field itself).
v1.5 — governance_payload
Introduced in v1.5. Machine-readable declaration of the agent's governance posture, signed independently of the passport in v1.8.
"governance_payload": {
"framework_alignment": ["EU_AI_ACT", "NIST_AI_RMF", "OWASP_LLM_TOP10"],
"policy_constraints": {
"data_residency": "EU",
"pii_scope": "tier_2_pseudonymous",
"human_in_the_loop": true
},
"audit_endpoint_url": "https://operator.example/aegis/audit",
"incident_response_url": "https://operator.example/aegis/incident"
}
The governance_payload block is read by Aegis verifiers to drive policy decisions (e.g. whether a high risk agent in EU residency may invoke a non-EU service). Verifiers do not interpret the field semantically — they enforce that what the operator signed matches what they declared at registry time.
v1.6 — capability_attestation
Introduced in v1.6. Attester-signed claims about the agent's capabilities, decoupled from the operator's signature. This is the foundation for third-party attestation — an attester (security audit firm, compliance vendor) attests that an agent has been red-teamed / pen-tested / certified, independently of the operator's own claims.
"capability_attestation": {
"attester_id": "audit_firm_alpha",
"attester_pubkey_url": "https://attester.example/keys/2026.pem",
"attestation_payload": {
"capabilities": ["code_review", "data_summarization"],
"red_team_scope": "OWASP_LLM_TOP10_2025",
"certification_level": "tier_2"
},
"attested_at": "2026-04-01T00:00:00Z",
"attestation_signature_hex": "ed25519:..."
}
Verification flow
- Verifier fetches
attester_pubkey_url(subject to SSRF mitigation). - Verifier reconstructs the canonical bytes via
canonicalize_passport_attestation(passport)— signs(attestation_payload + attested_at + agent_id), notoperator_id. Reason: the attester attests independently of any specific operator; in Phase 3 federation, attesters serve multiple operators. - Verifier checks Ed25519 signature against fetched pubkey.
- On failure →
verifier_failevent withreason_code=CAPABILITY_ATTESTATION_INVALID.
delegation_chain
When an agent is acting on behalf of another agent (multi-agent workflows, sub-agent invocation), the passport carries an ordered list of delegation hops:
"delegation_chain": [
{
"from_agent_id": "agent_alpha_001",
"to_agent_id": "agent_beta_002",
"scope": ["code_review"],
"delegated_at": "2026-05-07T22:00:00Z",
"expires_at": "2026-05-07T23:00:00Z",
"delegation_signature_hex": "ed25519:..."
}
]
Each hop is signed by the prior agent in the chain. Verifiers walk the chain and confirm:
- Each hop's signature verifies against the prior agent's pubkey.
- No hop has expired at the moment of the request.
- Scope at hop N is a subset of scope at hop N–1 (no privilege escalation).
- Total chain depth ≤ configured maximum (defends against runaway delegation).
A failure at any hop produces verifier_fail with reason_code=DELEGATION_CHAIN_INVALID and the failing hop index in the audit detail.
jurisdictional_extensions (regulatory packs)
When an operator deploys agents under a specific regulatory regime (Singapore MGAIF, South Africa POPIA, etc.), the passport carries jurisdiction-scoped fields under governance_payload.jurisdictional_extensions.<JURISDICTION>. The verifier loads the matching .rego policy pack and checks the namespaced fields against it.
"governance_payload": {
"framework_alignment": ["SG_MGAIF", "ZA_POPIA"],
"jurisdictional_extensions": {
"SG": {
"model_lifecycle": {
"data_provenance_uri": "https://operator.example/datasets/v3.lineage",
"model_version": "model_v2.4.1"
},
"stakeholder_communication": {
"explanation_url": "https://operator.example/aegis/decisions/explainability"
},
"human_involvement_tier": "tier_2_decision_augmentation",
"incident_logging": {
"audit_pack_lineage_root": "sha256:..."
}
},
"ZA": {
"compliance_contacts": {
"information_officer_id": "officer_alpha"
},
"processing_purposes": ["credit_decisioning"],
"special_category_processing": false,
"cross_border_transfer": {
"destination_country": "EU",
"adequacy_basis": "EU_GDPR_ADEQUACY_DECISION"
}
}
}
}
Activation
Each pack activates on a predicate at verify time:
| Pack | Predicate | Reason-code prefix |
|---|---|---|
| Singapore MGAIF | regulatory_jurisdictions contains "SG" AND risk_classification in {high, critical} | SG_MGAIF_* |
| South Africa POPIA | regulatory_jurisdictions contains "ZA" | ZA_POPIA_* |
regulatory_jurisdictions lives at passport.credentials.deployment_context.regulatory_jurisdictions. The baseline universal policy pack runs first; jurisdictional packs run second and emit additional deny reasons under their namespaced reason-code prefix.
Why namespaced
Putting jurisdiction-specific fields under jurisdictional_extensions.<CODE>.* keeps the v1.5/v1.6/v1.8 baseline schema clean and lets new jurisdictions land additively without re-cutting the schema version. EU AI Act, NIST AI RMF, ISO/IEC 42001 baselines remain at governance_payload top level (framework_alignment + policy_constraints). Country-specific extensions extend rather than mutate the contract.
special_category_lawful_basis (POPIA Section 36 conditional)
When a ZA-jurisdiction passport sets special_category_processing: true, the special_category_lawful_basis field becomes required. POPIA Section 27 prohibits processing of special personal information except under listed grounds; Section 36 establishes the conditions for invoking those grounds. The field carries the specific lawful-basis identifier the operator is relying on.
| Value | POPIA basis |
|---|---|
section_27_consent | Explicit consent of the data subject (s27(1)(a)). |
section_27_legal_obligation | Necessary for carrying out obligations under labour law (s27(1)(b)). |
section_27_legal_claim | Establishment, exercise, or defence of a legal right or obligation (s27(1)(c)). |
section_27_public_interest | Necessary in the public interest (s27(1)(d)). |
section_27_research_history_statistics | Historical, statistical, or research purposes with safeguards (s27(1)(e)). |
section_27_made_public_by_subject | Information deliberately made public by the data subject (s27(1)(f)). |
section_28_religious_belief | Processing of religious or philosophical beliefs by religious organisation members. |
section_30_trade_union | Processing of trade-union membership. |
section_31_political_persuasion | Processing of political persuasion. |
section_32_health_or_sex_life | Processing of health or sex life information by listed responsible parties. |
section_33_criminal_behaviour | Processing of criminal behaviour by competent bodies. |
section_34_biometric | Processing of biometric information. |
When special_category_processing is false, special_category_lawful_basis may be omitted or set to null. When special_category_processing is true and the field is absent or null, the verifier emits ZA_POPIA_S36_LAWFUL_BASIS_MISSING_FOR_SPECIAL_CATEGORY and the request is denied (advisory in observation window, strict post-Day-16).
The enum is closed — values outside this list are rejected with ZA_POPIA_S36_LAWFUL_BASIS_UNRECOGNISED so operators don't silently invent unsupported bases.
Self-diagnosis
Pack denials surface in the standard verifier-fail flow with their namespaced reason_code (e.g. SG_MGAIF_HUMAN_INVOLVEMENT_TIER_INSUFFICIENT, ZA_POPIA_S36_LAWFUL_BASIS_MISSING_FOR_SPECIAL_CATEGORY). Operators self-diagnose via /registry/anomaly/my_events.
v1.8 — governance_payload_signature
Introduced in v1.8. The governance payload is signed with an independent governance signing key (separate from the passport-level signing key). Reason: governance attestation is a different trust surface from operator identity — operators may want to rotate governance keys (e.g. after a compliance audit refresh) without rotating the passport-level identity key.
The governance signing key's public half is fetched from the operator's governance_signing_pubkey_url at verify time, behind the same SSRF-mitigation layer.
Canonical bytes contract
The signed bytes are not the whole passport — they are specifically:
canonicalize_passport_governance(passport) = canonical_json_bytes({
"governance_payload": passport["governance_payload"],
"agent_id": passport["agent_id"],
"operator_id": passport["operator_id"],
})
Sign-side and verify-side must produce identical canonical bytes for the same input — the canonical-JSON contract is sort_keys=True, separators=(',', ':'), allow_nan=False, UTF-8.
MANDATORY mode
verifier_fail event but the request is allowed through. After the Day-16 strict-flip cutover, governance signature failures become mandatory rejections (401 at the boundary).
The flip decision is gated by the flip_recommended flag on /registry/anomaly/flip_gate?hours=168. Operators who want early warning can subscribe to the /registry/anomaly/my_events self-diagnosis stream during the advisory window.
Verification surface
Client request
↓ Authorization: Bearer <passport>
[Aegis verifier]
↓ checks (sequence, fail-fast):
├─ schema_version recognized
├─ passport_signature_hex valid (Ed25519, against operator_id pubkey)
├─ expires_at in bounds
├─ registry status (agent_id active, not revoked/suspended)
├─ governance_payload_signature valid (v1.8, advisory in observation window)
├─ capability_attestation signature valid (v1.6, when present)
├─ delegation_chain coherent (when present)
└─ policy decision (risk_classification × endpoint sensitivity)
↓ pass → request forwarded
↓ fail → 401 + correlation_id (look up via /registry/anomaly/my_events)
Self-diagnosis
If you receive a 401 from an Aegis-protected endpoint, the response body carries a correlation_id. Use:
- verifier_fail correlation_id — diagnose a specific failure
- my_events self-diagnosis — browse all recent failures for your operator
Cross-references
- /api/reference — full API endpoint catalog (auth schemes, response shapes)
- /architecture — 15-layer security stack + active hardening surfaces
- /docs/troubleshooting/ — customer self-diagnosis flows
Schema evolution
This page reflects schema as of the 2026-05-08 observation window kickoff. Future versions land here as they ship; older versions remain documented for backwards-compatible verification. Changes are coordinated with operators via the /registry/anomaly/my_events advisory stream before any MANDATORY-mode flip.