Skip to main content

Signing Key Rotation

Introduction

Signing keys are not permanent. Every key has a finite useful life, after which it should be replaced with a fresh one so that any compromise of the old key has a bounded blast radius and so that cryptographic policy can evolve over time. Defakto rotates signing keys automatically on a schedule, and operators can also drive rotation manually when circumstances call for it.

This guide explains the model: what gets rotated, how automatic rotation works, and how the TAINTED state gives operators a way to signal that a keyset should be retired ahead of its natural expiry. For background on what signing keys are and where they live, see the Signing Key Management guide. For the operator commands that drive rotation manually, see Signing Key Rotation under spirlctl.

What Gets Rotated

The unit of rotation in Defakto is the keyset. A keyset bundles the X.509 signing key and the JWT signing key for a single trust domain deployment, and the two keys within a set are always rotated together. Treating the pair as one unit keeps the model simple: there is exactly one signing identity per deployment at any given time, regardless of which credential format a workload is requesting.

Rotation is scoped per deployment, not per trust domain. A trust domain can contain several deployments (for example, one per region or per environment), and each deployment maintains its own independent keyset history. Rotating the keyset in one deployment has no effect on the others.

Automatic Rotation

Defakto rotates each deployment's keyset on a schedule. Under the hood, the lifecycle is built on two states: PREPARED and ACTIVE. Exactly one keyset per deployment is ACTIVE at a time; it is the keyset signing all newly issued SVIDs. A PREPARED keyset is in the trust bundle, but not actively signing. It may be a freshly generated keyset waiting to be activated, or the formerly active keyset.

Trust Domain Server keyset rotation lifecycle

Starting 14 days before a keyset's 90-day expiry, the scheduler prepares a replacement. Seven days before expiry it activates the new keyset, and the old one steps back to PREPARED. Once it passes its expiry timestamp it is no longer valid for verifying SVIDs, but remains in the bundle until one day after hard expiry, at which point the scheduler removes it.

On-Demand Rotation

Operators can also drive rotation directly using the spirlctl trust-domain deployment keyset commands, which allows for a custom schedule. Use cases include compliance requirements, security incidents, and migrations and upgrades.

On-demand rotation also introduces the TAINTED state, which plays no role in automatic rotation. Tainting a keyset signals to the system that the keyset should be retired. The control plane publishes an updated trust bundle marking the keyset as tainted, and Defakto Agents proactively replace SVIDs it has signed rather than waiting for natural expiry. The agent behavior differs by credential type:

X.509 SVIDs: X.509 SVIDs are delivered to workloads over a persistent gRPC stream, and the agent caches the current SVID for each connected workload. Because the agent retains that cached copy and holds an open stream to the workload, it can proactively mint a replacement signed by the active keyset, and push it to the workload without any action required on the workload's side.

Two layers of backpressure prevent a "thundering herd" when a taint reaches a large fleet. First, each stream applies a random jitter delay of up to 60 seconds before attempting renewal, spreading the initial spike across the agent population. Second, a per-agent token bucket caps taint-triggered renewals at 20 per second (with a burst allowance of 20) across all workload streams on that agent, smoothing what remains into a steady trickle.

JWT SVIDs: JWT SVIDs follow a simpler request-response model. After a workload requests a token, the agent retrieves it from the server and returns it immediately. The agent retains no record of what was issued or to whom. Because the agent has no open stream or cached copy, it has no way to proactively renew outstanding JWT-SVIDs when a keyset is tainted. Those tokens remain valid for their full lifetime (24 hours by default). Before expiry the workload requests a fresh one signed by the active keyset.

As part of the on-demand workflow, the updated trust bundle is published to signers immediately rather than waiting for the next scheduled publish cycle. Agents maintain a persistent stream with the signer and receive bundle updates as they are pushed, so delivery is near-instantaneous. Propagation time is dominated by the per-stream jitter window: Each agent waits a random delay of up to 60 seconds before renewing, so full X.509 renewal across a fleet typically completes within minutes.

The spirlctl Signing Key Rotation reference covers the full set of commands, concurrency rules, and runbooks for the most common scenarios.