Preview — not for production reliance
Documentation

Docs For organizations

Rotation

Rotate an active issuer's CA in two reviewed steps with an explicit overlap, renew before expiry, and respond to a compromise with an emergency rotation.

Rotation proposes a new version N+1 of an active, unexpired binding with POST /v1/issuer-bindings/{id}/rotate (owner or issuer administrator, human, step-up). The binding keeps serving version N while N+1 goes through the same validation as registration and its own full issuer-control ceremony: a new challenge, the federation endpoint proof and a challenge SVID from every x509-svid authority of the new bundle.

json
{
  "expected_version": 3,
  "bundle": "{\"keys\":[ ...old CA..., ...new CA... ]}",
  "overlap_seconds": 86400
}

expected_version must equal the binding's current version. bundle_endpoint is optional and defaults to the served version's endpoint.

Why there are two steps

Every assertion pins exactly one bundle digest, so there is no overlap between versions. The overlap lives inside the bundle, which is standard SPIFFE practice. The server compares the new bundle's x509-svid authorities (the CA certificates' public keys; jwt-svid keys never count) with the served version's and classifies the rotation:

Kind The new bundle Approvals What relying parties see
add Keeps every authority of the served version and adds new ones. Records overlap_until = activation + overlap_seconds. 1 Assertions name N+1. Its bundle holds the old and the new CA, so SVIDs from either validate.
remove A strict subset of the served authorities, keeping at least one. Allowed only after the overlap in force has ended, and approved by a reviewer who did not approve the add step it completes. 1 Only the remaining CAs validate.
renewal Byte-identical to the served bundle. Re-proves control and recomputes expires_at. 1 Same keys, a later expires_at.
emergency emergency: true with a reason. Replaces or drops authorities at once. 2 The new version at once.

So a normal CA rotation is: add the new CA alongside the old one, let SPIRE move to the new CA during the overlap, then remove the old CA.

Refusals are 422 semantic_check_failed, with the reason code in field_errors[0].message on the bundle field:

Reason Meaning
no_shared_authority Nothing in common with the served version. Add the new CA alongside the old one first, or declare an emergency.
replace_requires_two_steps The proposal adds and removes authorities at once.
overlap_not_elapsed A remove step before the served version's overlap_until.

The overlap

overlap_seconds is required for every kind and ranges from 330 seconds to 7 days.

  • In production an add step needs at least 86400 seconds (24 hours); a shorter value is refused with 400 on overlap_seconds. Staging and development keep the 330-second minimum.
  • 330 seconds is the assertion lifetime (300 s) plus the verifier's clock skew (30 s). By the time the old CA can be removed, every assertion that named a bundle without the new CA has expired.
  • The overlap is sticky. A renewal carries it forward, and a second add step can extend it but never shorten it. A remove step is allowed only after the longest overlap since the last remove step has ended, and it clears the overlap.

Renewal

A binding is endorsed until its expires_at (at most one year). Renew before then by rotating to the byte-identical bundle: the renewal re-proves issuer control and recomputes expires_at. A binding that is not renewed in time becomes expired, a terminal state with a public binding.expired event, and must be registered again. An expired binding cannot be rotated (409).

Emergency rotation

If a CA is compromised, send emergency: true with a reason (1 to 2000 characters, newlines allowed, no other control characters). The new bundle may replace or drop authorities at once, and it always needs two reviewer approvals. reason is refused without emergency: true.

An emergency rotation changes what the directory endorses from the moment it activates. Assertions issued before that can be accepted for up to 330 seconds, and SVIDs your issuer already minted stay valid until they expire: revoke or shorten them on the SPIRE side.

Limits and replacements

  • A proposal that has not activated yet can be replaced by another rotate call.
  • At most 20 versions that never became active per binding in a rolling 90 days. Activated rotations and renewals do not count.
  • Superseded versions stay fetchable at their immutable bundle_url, but they are no longer what the directory endorses.