Skip to content

Rotate the signing key

Signing-key rotation opens an overlap window during which both the retiring and the incoming key verify, then retires the old key. Rotation is driven through the plexctl signing rotate and plexctl signing close subcommands implemented in ../../../cmd/plexctl/commands/signing.go, which delegate to the signer's OpenRotation and CloseRotation gRPC RPCs per Domain scope.

Prerequisites

  • Operator access to the signer deployment.
  • The target scope (platform or domain:<uuid>) and the new key ID.
  • Access to a plexctl binary configured to reach the signer (--signer-endpoint or the PLEXSPHERE_SIGNER_ENDPOINT env var).

Steps

Open the rotation

shell
plexctl signing rotate --scope domain:<uuid> --new-key-id <key-id>
# SCOPE      OLD_KEY_ID       NEW_KEY_ID       OPENED_AT              CLOSES_AT
# domain:…   domain-2026-04   domain-2026-05   2026-05-01T12:00:00Z   2026-05-01T13:00:00Z

The command invokes the OpenRotation RPC and prints the resulting transition. The signer now advertises both public keys, and CLOSES_AT = OPENED_AT + overlap_window (configured at signer bring-up through --overlap-window).

rotate exits 0 on success, 1 when a rotation is already open for the scope, and 2 when a flag value is rejected (malformed scope or key ID, or a scope the deployment profile refuses). The full gRPC-status to exit-code table is in the plexctl signing reference.

Sign through the new key

During the overlap every fresh Sign resolves to the new key while existing signatures still verify against the old one. The overlap window is persisted, so restarting the signer mid-overlap resumes the open rotation without re-emitting the rotation event.

Close the rotation

shell
plexctl signing close --scope domain:<uuid> --old <old-key-id> --new <new-key-id>
# SCOPE      OLD_KEY_ID       NEW_KEY_ID
# domain:…   domain-2026-04   domain-2026-05

The command invokes the CloseRotation RPC after the overlap. The old key is retired and no longer advertised. Rotation is per-Domain independent — one scope's rotation never touches another's. Exit codes follow the same table as rotate.

Verification

The rotate and close commands print the transition they applied, so the immediate success signal is in-band: a rotate whose CLOSES_AT sits in the future opened the overlap window, and a close that echoes the (SCOPE, OLD_KEY_ID, NEW_KEY_ID) tuple retired the old key.

Confirm the durable effects:

  • The JWKS no longer lists the retired key ID, and signatures minted during the overlap still verify against the new active key.
  • A successful rotate emits a signing_key_rotated event on the signed event bus — one outbox row per Node in the scope. See the event-taxonomy row in ../../contexts/mesh/sse.md for the payload shape and Inspect the signed event bus for how to tail it.

See also