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.
1. Prepare the bundle
The bundle is a SPIFFE trust bundle in the SPIFFE federation format: a JWK Set. With SPIRE, start from:
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_sequenceandspiffe_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,x5candalg. Any private member (d,p,q,dp,dq,qi,k,oth) is refused withprivate_key_material. - x509-svid keys carry
x5cwith 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
kidand must not carryx5c. - The
spiffe-x509profile 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:
{
"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>:
{"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:
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:
{"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:
{"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.