Preview — not for production reliance
Documentation

Docs For organizations

Registering an issuer

Submit your SPIFFE trust bundle, prove control of the issuer with the challenge ceremony, and get two independent approvals.

Registering an issuer tells the directory that your organization authorizes one SPIFFE trust domain, with one bundle, for a set of workload paths. It takes four steps: prepare the bundle, register, prove issuer control, and wait for two approvals.

Before you start you need a verified claim whose authority approval includes authorize_issuer, the organization_owner or issuer_administrator role (a domain administrator cannot do this), and a fresh passkey step-up. Every call on this page is human-only.

Registering an issuer, end to end Four stages. Register: submit the exact bundle, trust domain and workload scope; the binding is a draft with version 1 proposed. Challenge: the directory returns a 256-bit nonce, valid 24 hours with 5 attempts. Prove control: publish a key with kid art-challenge-nonce at the federation endpoint, mint a challenge SVID from every CA in the bundle, and sign a statement with each jwt-svid key if there are any; no private key ever leaves your SPIRE server. Review: two different reviewers, neither the requester, approve the exact bundle digest, and the binding becomes active. 1 Register exact bundle text, trust domain, scope draft version 1 proposed 2 Challenge 256-bit nonce, shown once valid 24 h, 5 failed attempts 3 Prove control • endpoint publishes key art-challenge-<nonce> • a challenge SVID from every CA in the bundle • jwt-svid keys sign a statement 4 Review two reviewers, not the requester, approve the exact bundle digest → active pending_review Your SPIRE server CA private keys never leave it DNS control does not prove you run the issuer. The ceremony proves you operate its federation endpoint and hold every CA in the bundle, without exporting any private key.
The four steps on this page. Steps 1 to 3 are yours; step 4 is two independent reviewers.

1. Prepare the bundle

The bundle is a SPIFFE trust bundle in the SPIFFE federation format: a JWK Set. With SPIRE, start from:

bash
spire-server bundle show -format spiffe

The directory stores and hashes the exact text you submit, so it validates that text strictly:

  • Top level: only keys (1 to 32 entries), spiffe_sequence and spiffe_refresh_hint (integers of 0 or more). At most 64 KiB, UTF-8, no byte-order mark.
  • Public members only. A key may carry kty, use, kid, crv, x, y, n, e, x5c and alg. Any private member (d, p, q, dp, dq, qi, k, oth) is refused with private_key_material.
  • x509-svid keys carry x5c with exactly one certificate per authority: the CA certificate itself, not a chain (x5c_must_be_single_authority). It must be a CA certificate, and its public key must match the JWK (x5c_key_mismatch).
  • jwt-svid keys need a kid and must not carry x5c.
  • The spiffe-x509 profile needs at least one x509-svid key.
  • Key IDs starting with art-challenge- are reserved for the ceremony.

If your bundle contains jwt-svid keys, the ceremony will ask for a signed statement from each of them (step 3). SPIRE's JWT signing keys cannot sign arbitrary statements, so if you only need the spiffe-x509 profile, submit the x509-svid authorities alone, as the reference demo does.

2. Register

In the console choose Register an issuer from the verified claim, or call POST /v1/issuer-bindings:

json
{
  "claim_id": "clm_4b7e1h5k8n2q6t9w3z0c4f7j1m",
  "trust_domain": "agents.payments.example.com",
  "profile": "spiffe-x509",
  "bundle_endpoint": "https://spiffe.payments.example.com/bundle",
  "bundle": "{\"keys\":[{\"use\":\"x509-svid\",\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"...\",\"y\":\"...\",\"x5c\":[\"MIIB...\"]}]}",
  "workload_scope": ["/agent/checkout", "/agent/refunds"]
}
Field Rules
trust_domain Exact, lowercase, no spiffe://. With mapping: "dns" (default) it is the claim's domain or a name under it; mapping: "explicit" allows a single-label name mapped by a reviewer. Wildcards, public suffixes and special-use names are refused.
bundle_endpoint Your SPIFFE federation bundle endpoint: https, port 443, a DNS host (no IP literal) at or under the verified domain, up to 2048 characters. The most specific verified domain covering that host must be yours.
bundle The bundle JSON as a string, exactly as prepared.
workload_scope 1 to 64 path prefixes, matched by whole segment.
environment production (default) or test. Production deployments accept only production.

The binding is created as draft with version 1 proposed, and the response includes the ceremony instructions. A trust domain that already has a live binding answers 409 with the deliberately uninformative "This trust domain is not available." Registration is never refused because another organization used the same key; overlaps are recorded for the reviewers instead. An organization can register at most 20 issuers in a rolling 30 days.

3. Prove issuer control

DNS control does not prove you control a workload issuer. The ceremony proves you operate the issuer's management surface and hold every CA in the bundle, without exporting any CA private key.

Start with POST /v1/issuer-bindings/{id}/control-challenge. It returns a 256-bit nonce, shown once, valid for 24 hours with 5 failed attempts allowed. Every proof below uses the same nonce. GET on the same path shows the challenge's status but never the nonce again.

Submit each proof with POST /v1/issuer-bindings/{id}/control-proof (step-up).

The federation endpoint

At your bundle_endpoint, publish the bundle you submitted plus one extra public key whose kid is art-challenge-<nonce>:

json
{"use":"jwt-svid","kid":"art-challenge-<nonce>","kty":"EC","crv":"P-256","x":"...","y":"..."}

Any P-256 public key will do; only the kid matters. Leave spiffe_refresh_hint unchanged. Then submit {"method":"federation_endpoint"}. The directory fetches the endpoint through its isolated scanner (https on port 443 only, at most 3 same-host redirects, 64 KiB) and compares the published keys with the submitted bundle plus the challenge key.

A challenge SVID from every CA

For every x509-svid authority in the bundle, have the SPIRE server that holds it mint an ordinary X.509-SVID for the challenge ID:

bash
spire-server x509 mint -spiffeID spiffe://agents.payments.example.com/art-challenge/<nonce> -write ./art-challenge

Submit the certificate chain from ./art-challenge/svid.pem, leaf first, one chain per CA:

json
{"method":"x509_svid","svids":["-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n"]}

Never submit key.pem. spire-server x509 mint signs with the server's active CA only, so a bundle that lists a second, prepared authority cannot be proven until that authority signs.

Signed statements for jwt-svid keys

Only if the bundle has jwt-svid keys: for each one, sign a compact JWS whose header kid is that key's kid (ES256, ES384, RS256 or PS256) over:

json
{"binding_id":"bnd_...","version":1,"bundle_sha256":"<digest>","nonce":"<nonce>","iat":1791201600}

and submit {"method":"signed_statement","statements":["<jws>", ...]}.

A failing proof answers 422 semantic_check_failed and uses one of the 5 attempts. When every required proof has passed, the directory records issuer_control evidence, consumes the nonce and moves the binding to pending_review.

4. Two-person activation

Reviewers approve the exact bundle digest of the version under review. A production issuer needs two different reviewers, neither of them the requester. A test binding in a non-production deployment needs one. The second approval activates the binding: it starts resolving, and a public binding.activated event appears in the change feed.

An active binding is endorsed until its expires_at: at most one year, and never later than your representative authority, the claim, the CA certificates' validity, the issuer-control evidence or any delegation it relies on. Renew it before then with a renewal rotation.

After activation

The directory never changes keys on its own. If your federation endpoint later publishes a different key set, the binding is flagged endpoint_drift: true and operators are alerted, but resolution keeps serving the approved bundle. Keys change only through a reviewed rotation.

To replace a proposed bundle before activation, or after a rejection, propose a new version of the draft with POST /v1/issuer-bindings/{id}/versions.