Preview — not for production reliance
Documentation

Docs Start here

Concepts

SPIFFE, SPIRE and SVIDs; trust domains and bindings; the evidence model and the three checks; delegations; and the signed assertion.

The big picture

Who uses the directory An organization claims its domain and registers its SPIRE issuer with the directory, sending only the public bundle. Reviewers verify the organization's identity and authority. The SPIRE server issues an SVID to an AI agent, which connects to a relying party over mTLS. The relying party resolves the agent's trust domain at the directory, receives a signed assertion and the approved bundle, and makes its own allow or deny decision. YOUR ORGANIZATION Organization admins in the console SPIRE server holds CA keys, issues SVIDs AI agent workload holds an X.509-SVID ③ issues SVID Reviewers independent checks ② verify identity + authority Agentic Root Trust svid.io Verified organizations Domain claims + evidence Issuer bindings + bundles Signed assertions (ES256) Public change feed ① claim, register bundle (public keys) Relying party API, service or agent ⑦ allow or deny: its own decision ⑤ resolve ⑥ assertion + bundle ④ mTLS connection with the X.509-SVID The directory never issues credentials and never holds private keys.
Who does what: organizations register, reviewers verify, relying parties resolve and decide.

SPIFFE, SPIRE and SVIDs

SPIFFE is the standard for workload identity. A workload's identity is a SPIFFE ID such as spiffe://agents.payments.example.com/agent/checkout: a trust domain (agents.payments.example.com) and a path (/agent/checkout). The workload proves that identity with an SVID (SPIFFE Verifiable Identity Document), which is an X.509 certificate or a JWT.

SPIRE is the reference implementation. A SPIRE server holds the signing authorities of one trust domain, attests workloads and issues them short-lived SVIDs. Anyone who holds the trust domain's bundle (its public authorities) can validate those SVIDs.

What SPIFFE does not say is which organization operates a trust domain. A DNS-shaped name proves nothing about DNS ownership, and receiving a bundle from a peer proves nothing about who the peer is. Agentic Root Trust fills that gap: it records which verified organization authorized a trust domain, and publishes the bundle that organization approved.

The directory supports the spiffe-x509 profile: X.509-SVID verification over mTLS. JWT-SVID and WIMSE (Workload Identity Token and Workload Identity Certificate) adapters are later, separately specified profiles.

Trust domains

A trust domain in the directory is always exact: lowercase [a-z0-9._-], at most 255 characters, no empty labels, not an IP address, and never written with a spiffe:// prefix. There is no wildcard or parent-domain matching, so resolving agents.example.com says nothing about eu.agents.example.com.

A trust domain is tied to a verified domain claim in one of two ways:

Mapping Rule
dns (default) The trust domain is the claim's domain or a DNS name under it, such as agents.payments.example.com under payments.example.com.
explicit A single-label trust domain (no dots), mapped to the claim by a reviewer.

Public suffixes and special-use names (.example, .test and similar) are refused. Only one organization holds a live binding for an exact trust domain at a time.

Bindings

A binding is the directory's record that an organization authorized an issuer. It ties together:

  • the organization and the verified claim that backs it;
  • the exact trust domain and the profile (spiffe-x509);
  • the workload scope: the SPIFFE ID path prefixes the issuer may speak for;
  • a numbered, immutable version carrying the federation bundle endpoint and the exact bundle bytes and their SHA-256 digest.
What a binding ties together An organization with a stable ID holds a verified claim on payments.example.com. The claim backs an issuer binding for the exact trust domain agents.payments.example.com, with profile spiffe-x509 and a workload scope. The binding has numbered immutable versions, each with its bundle bytes, SHA-256 digest and federation endpoint; rotation adds a new version. Outside the directory, your SPIRE server keeps the private CA keys, issues short-lived SVIDs to workloads, and publishes only the public bundle. ORGANIZATION Example Payments org_7k3m9q2x… verified identity; pin this ID VERIFIED CLAIM payments.example.com three checks passed authority: authorize_issuer ISSUER BINDING agents.payments.example.com profile spiffe-x509 scope /agent/checkout, /agent/refunds Version 1 (superseded) VERSION 2 bundle bytes + SHA-256 digest federation endpoint immutable OUTSIDE THE DIRECTORY: YOURS SPIRE server private CA keys stay here Workloads short-lived SVIDs public bundle only The directory records who authorized the issuer, for which exact trust domain and workload paths, and which bundle they approved. Key changes are always a new, reviewed version; old versions stay fetchable at their own URL.
From a verified organization to the exact bundle a relying party validates SVIDs against.

A binding moves through draft → pending_review → active, and from there to suspended, expired or withdrawn. withdrawn and expired are terminal. Key material is never overwritten: a changed bundle is always a new version, and superseded versions stay fetchable at their own URL. An active binding is endorsed until its expires_at, at most one year, capped by everything it depends on (the representative authority, the claim, the CA certificates' validity, the issuer-control evidence and any delegation).

Workload scope

The scope is a list of 1 to 64 path prefixes. Each starts with /, has at least one segment, and each segment is [A-Za-z0-9._-]+ with no . or .. segments and no percent-encoding. Matching is by whole segment: /payments matches /payments/api but never /payments-evil.

The three checks

A claim on a domain is verified only when all three checks pass. None can be skipped, and each is recorded and shown separately.

The three checks behind a verified claim A claim passes three checks in order. One, domain control: a single-use token in a DNS TXT record at _workload-directory.domain, seen by both Cloudflare and Google DNS and rechecked every 90 days; the claim is pending_domain. Two, organization identity: a reviewer checks legal name, jurisdiction and registration; pending_identity. Three, representative authority: confirmed through an independently sourced contact, approval expires within a year; pending_authority. Then the claim is verified and can authorize issuers. At any step a claim can need more information, be rejected or be disputed. 1 Domain control single-use token in DNS TXT _workload-directory.<domain> seen by Cloudflare + Google rechecked every 90 days 2 Organization identity a reviewer checks legal name, jurisdiction, registration a DV certificate alone is never enough 3 Representative authority confirmed through an independent contact names the permitted actions; expires within a year Verified can authorize issuers pending_domain pending_identity pending_authority verified At any step a claim can become needs_information, rejected or disputed. Conflicting claims go to review, never first come, first served.
The three checks, in order, and the claim state while each is open.
  1. Domain control. An authenticated account publishes a single-use token in a DNS TXT record at _workload-directory.<domain>. Both Cloudflare and Google public DNS must see it. This proves control of the name now; it is re-checked every 90 days.
  2. Organization identity. A reviewer checks the organization's evidence against its legal name, jurisdiction and registration. A DV certificate alone cannot complete this check.
  3. Representative authority. A reviewer confirms, through an independently sourced contact at the organization, that the claimant may act for it. Company email, uploaded documents and DNS access are never enough on their own. The approval names the permitted actions (authorize_issuer, delegate, manage_domain) and expires within a year.

A claim moves pending_domain → pending_identity → pending_authority → verified. It can also be needs_information, rejected, disputed, expired, suspended or withdrawn. Conflicting claims go to review; they are never settled by whoever claimed first.

The evidence model

The directory borrows the TLS validation levels so that relying parties compare like with like, and keeps every other kind of evidence separate.

Evidence What it supports
DV Domain control was validated for certificate issuance
OV DV plus vetted organization information
EV OV plus prescribed organization-existence and applicant-authority checks
Directory DNS challenge The account can satisfy the current domain-control challenge
Representative review The claimant may represent the named organization for the approved actions
Issuer authorization The verified representative explicitly approved this issuer, bundle and scope

Certificates the directory observes are classified only after path, hostname and validity checks against a pinned, versioned trust store; organization fields alone never make a certificate OV or EV, and unknown policies stay unknown. A TLS observation is a historical record of what a host served, never a verdict. A CDN-served DV certificate does not downgrade separately reviewed identity.

There is no numerical trust score. DV, OV and EV describe evidence, not legitimacy or agent safety: a fully verified organization can still run a careless or compromised agent.

Binding lifecycle A binding starts as draft, moves to pending_review once issuer control is proven, and becomes active after two reviewer approvals. An active binding can be suspended and resumed. From active or suspended it can be withdrawn or expire; withdrawn and expired are terminal. Key changes never edit a version: a rotation adds a new version that is reviewed the same way. draft pending_review active suspended withdrawn terminal expired terminal control proven two approvals suspend resume withdraw expires_at withdraw Keys never change in place: a rotation adds a new version, reviewed the same way. Old versions stay fetchable. A draft or pending binding can also be withdrawn before it is ever published.
Binding states. Withdrawn and expired are terminal; rotation never edits a version.

Delegations

A parent organization can delegate a strict subdomain of its verified claim to another organization, with a limited set of actions (authorize_issuer, manage_domain), a workload scope inside its own, and an expiry. Control of a child grants nothing over the parent or its siblings, re-delegation is refused, and cycles are rejected. A delegation is active only after the recipient's owner accepts it and a reviewer approves it. See Withdrawal and delegations.

The assertion

What happens when an agent connects Sequence: the agent opens an mTLS connection and presents its X.509-SVID. The relying party calls GET /v1/resolve for the trust domain; the directory answers status active with a signed assertion valid for at most 300 seconds. The relying party fetches the immutable bundle at bundle_url, then verifies the signature against its pinned keys, the issuer, audience and times, the pinned organization ID, the workload scope, the bundle digest and the SVID chain. Finally it applies its own policy and allows or denies the request. AI agent SPIFFE workload Relying party your API or service Directory svid.io ① mTLS handshake, presents its X.509-SVID spiffe://agents.payments.example.com/… ② GET /v1/resolve?trust_domain=… ③ status: active + signed assertion ES256, valid at most 300 s ④ GET bundle_url ⑤ bundle bytes (immutable, cacheable) ⑥ The relying party checks • signature against pinned JWKS • issuer, audience, times, schema • organization_id you pinned • SVID path inside workload_scope • SHA-256 of bundle = bundle_sha256 • SVID chains to that bundle ⑦ your own policy: allow or deny Fail closed: stop at the first failed check.
One agent connection, end to end. Steps 2 to 6 are the verification algorithm.

GET /v1/resolve?trust_domain= returns a compact JWS. It is a directory assertion, not an SVID or a WIMSE credential. The protected header is exactly:

json
{"alg":"ES256","typ":"art-resolution+jwt;v=1","kid":"<RFC 7638 thumbprint of the signing key>"}

The payload of the example in the API description decodes to:

json
{
  "iss": "https://svid.io",
  "aud": "workload-directory-resolver-v1",
  "iat": 1791201600,
  "nbf": 1791201600,
  "exp": 1791201900,
  "schema_version": 1,
  "binding_id": "bnd_9r2t5w8z1b4d7f0h3k6n9q2s5v",
  "binding_version": 1,
  "status": "active",
  "organization_id": "org_7k3m9q2x8d5f1h4j6n0p2r4t8v",
  "organization_name": "Example Payments, Inc.",
  "verified_domain": "payments.example.com",
  "trust_domain": "agents.payments.example.com",
  "profile": "spiffe-x509",
  "workload_scope": ["/agent/checkout", "/agent/refunds"],
  "bundle_url": "https://svid.io/v1/issuer-bindings/bnd_9r2t5w8z1b4d7f0h3k6n9q2s5v/versions/1/bundle",
  "bundle_sha256": "q7Yd3nXk0aV2pLw9sT4uHc8BzE1mR6fJ5gN0oIxK2tA",
  "evidence_summary": {
    "domain_control": "verified",
    "organization_identity": "reviewed",
    "representative_authority": "verified"
  },
  "policy_version": "directory-policy-v1"
}

Points that matter when you verify it:

  • iat is the time of the authoritative database read, nbf always equals iat, and exp is at most iat + 300. The lifetime is shortened further to the earliest expiry of the binding, the claim, the 90-day domain-control window and every piece of required evidence.
  • The verifier allows 30 seconds of clock skew, so an assertion read just before a withdrawal can be accepted for up to 330 seconds after the withdrawal commits.
  • bundle_url always has the form <iss>/v1/issuer-bindings/<binding_id>/versions/<binding_version>/bundle, and bundle_sha256 is the unpadded base64url SHA-256 of the exact bytes served there.
  • organization_id is the stable identifier to pin. Never trust organization_name or any label the agent presents.
  • The signed bytes are canonical JSON (no whitespace, no duplicate keys) and at most 8192 bytes. Unknown fields fail verification.

The verification guide walks through every check in order.