Chio/Docs

EconomyReputation & Identity

Agent Passports

Reference for Agent Passport schemas, credential projections, OpenID flows, and offline verification.


did:chio Identifiers

Every Agent Passport subject is a did:chio identifier:

text
did:chio:{64-lowercase-hex-Ed25519-public-key}

The method-specific identifier is the lowercase hex form of an Ed25519 public key. Because the identifier embeds the key, a verifier can check signatures without a registry lookup, DID resolver, or network call. Parsing rules in chio-did:

  • The string must start with did:chio:.
  • The suffix must be exactly 64 hexadecimal characters.
  • The decoded public key must be Ed25519. P-256 and other algorithms are rejected with DidError::UnsupportedKeyAlgorithm.

Resolution returns a W3C-compliant DID document with the embedded Ed25519 verification method (in multibase encoding) plus optional service endpoints:

chio-did/src/lib.rs
pub const RECEIPT_LOG_SERVICE_TYPE: &str = "ChioReceiptLogService";
pub const PASSPORT_STATUS_SERVICE_TYPE: &str = "ChioPassportStatusService";

Service endpoints are attached by the resolving environment, not the identifier itself. Two operators resolving the same did:chio can hand back different service URLs while sharing the same verification method.


Passport Schema

The native Agent Passport (schema tag chio.agent-passport.v1) is an unsigned bundle of independently verifiable credentials:

chio-credentials/src/passport.rs
pub struct AgentPassport {
    pub schema: String,                                         // chio.agent-passport.v1
    pub subject: String,                                        // did:chio of the agent
    pub credentials: Vec<ReputationCredential>,
    pub merkle_roots: Vec<String>,                              // receipt-log checkpoint roots
    pub enterprise_identity_provenance: Vec<EnterpriseIdentityProvenance>,
    pub issued_at: String,                                      // RFC 3339
    pub valid_until: String,                                    // RFC 3339
    pub trust_tier: Option<TrustTier>,                          // optional, recent addition
}

The Agent Passport itself carries no signature. Signature verification applies to the credentials inside it: each ReputationCredential is independently signed by an issuer, and verification walks the bundle credential by credential.

The four field groups serve different verification needs:

FieldPurpose
subjectBinds the passport to a single Ed25519 keypair via did:chio
credentialsIssuer-signed reputation attestations carrying scorecards
merkle_rootsReceipt-log checkpoint roots so a verifier can spot-check receipt evidence
enterprise_identity_provenanceFederation evidence: which IdP authenticated the principal that issued each credential

Passport v2 also carries financial credentials

The schema above is the identity passport. A parallel passport v2 bundle (chio.agent-passport.v2) adds a family of signed, individually verifiable, selectively disclosable financial credentials (chio.fincred.*) that carry an agent's economic record across organizations, documented at Portable Financial Credentials. They are distinct from the identity passport described here.

Verifiable Credential Projections

Chio supports three wire formats for a passport. The native format is the source of truth; the other two are projections derived from it for interop with W3C Verifiable Credentials tooling.

FormatWhere it livesUse case
Native CHIO JSONchio-credentials/src/passport.rsSource of truth. All other formats are derived from this.
SD-JWT VCportable_sd_jwt.rsIETF SD-JWT-VC. Holders selectively disclose individual claims.
JWT VC JSONportable_jwt_vc.rsW3C VC 2.0 in JWT-encoded JSON for VP-style flows.

The SD-JWT VC projection partitions claims into two groups: always-disclosed claims that travel in cleartext, and selectively disclosable claims that the holder reveals on demand.

Always disclosedWhy
issThe credential issuer (the issuing operator)
subHolder thumbprint (binds to the holder's key)
vctVerifiable Credential type (the chio-passport SD-JWT VC type)
cnfConfirmation key (holder JWK for DPoP-style binding)
chio_passport_idStable passport identifier
chio_subject_diddid:chio of the agent the passport binds to
chio_credential_countNumber of credentials inside the bundled passport
Selectively disclosableWhy
chio_issuer_didsList of issuer did:chios for the credentials in the bundle
chio_merkle_rootsReceipt-log checkpoint roots referenced by the credentials
chio_enterprise_identity_provenanceFederation evidence (IdP, principal, tenant, groups, roles)

Selective disclosure is a presentation concern

Selectively disclosable claims travel as salted hashes in the compact SD-JWT. The holder reveals individual disclosures during presentation, so a verifier can be told which issuer signed the bundle without learning the federation provenance, or vice versa.

OID4VCI and OID4VP Protocols

The SD-JWT VC projection supports two OpenID-family protocols: OID4VCI for issuance and a limited OID4VP path for presentation. Both operate over the same projected credential, so a holder issued through OID4VCI can present through OID4VP without a second encoding step.

OID4VCI Issuance

An Agent Passport is issuable over OpenID for Verifiable Credential Issuance. The credential configuration id is chio_agent_passport, the credential format is chio-agent-passport+json, and issuer metadata is served at /.well-known/openid-credential-issuer. The pre-authorized-code grant drives the offer, token, and credential exchange, exposed through the chio passport issuance metadata|offer|token|credential CLI verbs.

chio-credentials/src/oid4vci.rs
pub const OID4VCI_PRE_AUTHORIZED_GRANT_TYPE: &str =
    "urn:ietf:params:oauth:grant-type:pre-authorized_code";
pub const CHIO_PASSPORT_OID4VCI_CREDENTIAL_CONFIGURATION_ID: &str = "chio_agent_passport";
pub const CHIO_PASSPORT_OID4VCI_FORMAT: &str = "chio-agent-passport+json";
pub const OID4VCI_ISSUER_METADATA_PATH: &str = "/.well-known/openid-credential-issuer";

OID4VP Presentation

Presentation supports a limited OID4VP verifier flow over the projected application/dc+sd-jwt Agent Passport presentation endpoint. It does not implement generic OID4VP. A request carries a DCQL query for exactly one credential, and the verifier rejects any credential whose format is not application/dc+sd-jwt or whose type is not the chio-passport SD-JWT VC type. Verifier metadata is served at /.well-known/chio-oid4vp-verifier, and the chio passport oid4vp verbs drive the exchange.

chio-credentials/src/oid4vp.rs
impl Oid4vpDcqlQuery {
    pub fn validate(&self) -> Result<(), CredentialError> {
        if self.credentials.len() != 1 {
            return Err(CredentialError::InvalidOid4vpRequest(
                "Chio OID4VP currently supports exactly one requested credential".to_string(),
            ));
        }
        self.credentials[0].validate()
    }
}

HTTP Flow

Both protocols use a fixed external HTTP flow that a raw client can drive without the Chio CLI: issuance covers the first four calls, presentation the rest. A holder issued a credential in steps one through four presents it back through either the OID4VP lane (steps five through seven) or the Chio-native challenge lane (steps eight and nine).

StepEndpointPurpose
1GET /.well-known/openid-credential-issuerFetch issuer metadata.
2GET /.well-known/jwks.json, GET /.well-known/chio-passport-sd-jwt-vc or .../chio-passport-jwt-vc-jsonOptional. Portable issuer key material and projected-profile type metadata. Omitted from issuer metadata and served as 404 when no portable signing key is configured.
3POST /v1/passport/issuance/tokenRedeem a pre-authorized code for an access token.
4POST /v1/passport/issuance/credentialRedeem a native AgentPassport or a projected application/dc+sd-jwt or jwt_vc_json credential.
5GET /.well-known/chio-oid4vp-verifierFetch verifier metadata.
6GET /v1/public/passport/oid4vp/requests/{request_id}Fetch the signed OID4VP request object, or resolve the same transaction through the HTTPS cross-device launch URL the verifier returns.
7POST /v1/public/passport/oid4vp/direct-postSubmit the signed direct_post.jwt holder response.
8GET /v1/public/passport/challenges/{challenge_id}Fetch a stored Chio-native verifier challenge.
9POST /v1/public/passport/challenges/verifySubmit the signed Chio-native holder response.

The OID4VP request object pins client_id_scheme=redirect_uri, response_type=vp_token, and response_mode=direct_post.jwt. Admin operations (issuance offers, challenge creation, verifier-policy CRUD, lifecycle publish and revoke) stay on separate routes so public transport never widens verifier admin authority.


Multi-Issuer Composition

An Agent Passport can carry credentials from any number of issuers. Two organizations can each independently sign a credential for the same agent, and the agent bundles both into a single Agent Passport without either issuer needing to coordinate with the other.

This works because the Agent Passport is a container: each credential carries its own Ed25519 signature, and verification walks the credentials individually. The PassportVerification result returned by verify_agent_passport exposes both lists:

chio-credentials/src/passport.rs
pub struct PassportVerification {
    pub passport_id: String,
    pub subject: String,
    pub issuer: Option<String>,      // single-issuer back-compat field
    pub issuers: Vec<String>,        // unique issuer did:chios
    pub issuer_count: usize,
    pub credential_count: usize,
    pub merkle_root_count: usize,
    pub enterprise_identity_provenance: Vec<EnterpriseIdentityProvenance>,
    pub passport_lifecycle: Option<PassportLifecycleResolution>,
    pub verified_at: u64,
    pub valid_until: String,
}

A verifier policy (see PassportVerifierPolicy in chio-credentials/src/passport.rs) constrains each credential independently. It can pin issuers to an issuer_allowlist, set a min_receipt_count or min_composite_score per credential, and require an active Agent Passport lifecycle before accepting the Agent Passport.


Offline Verification

The portable kernel core ships an offline-capable Agent Passport verifier at chio-kernel-core/src/passport_verify.rs. It is no_std + alloc, so the same verifier compiles into native sidecars, browser WASM, mobile runtimes, and edge proxies.

The portable verifier consumes a thin PortablePassportEnvelope (schema tag chio.portable-agent-passport.v1) that wraps canonical-JSON bytes of any passport projection:

chio-kernel-core/src/passport_verify.rs
pub struct PortablePassportBody {
    pub schema: String,                  // chio.portable-agent-passport.v1
    pub subject: String,
    pub issuer: PublicKey,
    pub issued_at: u64,
    pub expires_at: u64,
    pub payload_canonical_bytes: Vec<u8>, // hex-encoded on the wire
}

pub struct PortablePassportEnvelope {
    pub body: PortablePassportBody,
    pub signature: Signature,
}

verify_passport performs four checks:

  • The bytes parse as a PortablePassportEnvelope and the schema tag matches.
  • The issuer key is in the relying party's authority key set.
  • The Ed25519 signature is valid over the canonical JSON of the body.
  • The clock value sits in [issued_at, expires_at).

On success it returns a VerifiedPassport with the subject, issuer, validity bounds, evaluation time, and the canonical payload bytes. There is no revocation lookup, no issuer-chain validation, and no payload decoding in the portable path. Those richer checks remain in the native chio-credentials / chio-kernel code; the portable core is the verification component that browsers and edge adapters can run with the same Ed25519 path as the sidecar.


Holder Binding

A passport binds to runtime key material through the holder's confirmation key. In the SD-JWT VC projection the cnf claim carries a JWK for the holder's Ed25519 key (the same key embedded in chio_subject_did). The holder's thumbprint is the SD-JWT's sub, so the issuer signature attests that this passport may only be presented by the holder of the bound key.

At runtime, the same key signs DPoP proofs on the request side. A relying party that accepts a passport plus a DPoP proof has cross- bound evidence: the passport says "this scorecard belongs to the holder of did:chio:7b...", and the DPoP proof says "this request was signed by the same key".


Worked Example: Two-Issuer Passport

Agent did:chio:7b... has been operating across two orgs: Operator A (security tools) and Operator B (data tools). Each observes its own slice of receipts and signs an issuer-specific scorecard.

two-issuer-passport.json
{
  "schema": "chio.agent-passport.v1",
  "subject": "did:chio:7b0f6f63...",
  "credentials": [
    {
      "issuer": "did:chio:a1b2...",
      "credentialSubject": {
        "id": "did:chio:7b0f6f63...",
        "metrics": { "reliability": { "score": { "state": "known", "value": 0.97 } } }
      },
      "evidence": { "receipt_count": 5400, "checkpoint_roots": ["sha256:..."] },
      "proof": { "type": "Ed25519Signature2020", "proofValue": "..." }
    },
    {
      "issuer": "did:chio:c3d4...",
      "credentialSubject": {
        "id": "did:chio:7b0f6f63...",
        "metrics": { "reliability": { "score": { "state": "known", "value": 0.93 } } }
      },
      "evidence": { "receipt_count": 1820, "checkpoint_roots": ["sha256:..."] },
      "proof": { "type": "Ed25519Signature2020", "proofValue": "..." }
    }
  ],
  "merkle_roots": ["sha256:roota...", "sha256:rootb..."],
  "enterprise_identity_provenance": [
    { "provider_id": "operator-a-okta", "provider_kind": "okta", "principal": "agent-7b@org-a", ... },
    { "provider_id": "operator-b-azure", "provider_kind": "azure-ad", "principal": "svc-7b@org-b", ... }
  ],
  "issued_at": "2026-04-20T00:00:00Z",
  "valid_until": "2026-07-20T00:00:00Z"
}

A relying party Org C verifies the passport offline:

  • Parse the passport and confirm subject == did:chio:7b0f6f63....
  • For each credential, verify the Ed25519 proof against the issuer's embedded key (issuer is itself a did:chio, so the public key is in the identifier).
  • Confirm the credential subject equals the passport subject.
  • Apply Org C's PassportVerifierPolicy: for example, require both Operator A and Operator B in the allowlist, require min_receipt_count = 1000 per credential, and require active passport lifecycle.

Org C now has cross-issuer evidence about the agent without ever contacting Operator A or Operator B. The verification result lists both issuers and both provenance records, which Org C's downstream policy can attenuate independently.


How This Differs from the How-To Guide

The page at /docs/guides/agent-passport is task-oriented: it walks through creating a passport, presenting it, and writing a verifier policy. This page is a reference: it documents the schema, the projections, the selective-disclosure partitioning, the multi-issuer composition rules, and the portable-kernel offline verification path. Cross-link both directions when working through an integration.