Runbook: Attestor - Signing Key Expired
Purpose: restore attestation signing after a Stella Ops signing key has expired (or is about to), without breaking verification of evidence that was already signed. Audience: Platform and Security on-call engineers responding to a blocked release or a failing key-material Doctor check.
Metadata
| Field | Value |
|---|---|
| Component | Attestor (Signer) |
| Severity | Critical |
| On-call scope | Platform team, Security team |
| Last updated | 2026-05-31 |
| Doctor check | check.attestation.keymaterial |
Source-of-truth note (reconciled 2026-05-31): This runbook was reconciled against
src/Attestor,src/Cli, andsrc/Doctor/__Plugins/StellaOps.Doctor.Plugin.Attestor. SeveralstellaCLI commands referenced in earlier drafts do not exist in the shipped CLI; they are flagged inline below. Production key rotation is driven by the Signer service trust-anchor API (/api/v1/anchors/...). There are two CLI surfaces, and neither is a live operator tool today:
stella key(singular) — wired into the root CLI (CommandFactory.BuildKeyCommand→Commands/Proof/KeyRotationCommandGroup.cs). It mirrors the Signer trust-anchor API with subcommandslist,add,revoke,rotate,status,history,verify, each taking a trust-anchor GUID argument. However, every subcommand currently fails closed: it logs the request and returnsError: key rotation <op> requires a configured key rotation service. No simulated trust-anchor change or validity result was produced.(exit codeProofExitCodes.SystemError). The API-client wiring is not yet present, so use the Signer HTTP API directly until it is.stella keys(plural) — the unregistered sample-data group was deleted on 2026-07-29. It is not an operator command. Do not confuse it with the registered, fail-closedstella keygroup or withstella crypto keys.Commands flagged NOT IMPLEMENTED are called out in the relevant sections below.
Symptoms
- [ ] Attestation creation failing with a signing error
- [ ] Doctor check
check.attestation.keymaterialreturns Fail (N signing key(s) have expired) - [ ] Doctor check returns Fail for keys expiring within 7 days (critical) or Warn within 30 days
- [ ] Signer
GetRotationWarningsreportsExpiryApproachingfor an active key - [ ] New attestations cannot be created; release gates that require signed evidence block
Note: The Attestor’s own DSSE verification (
AttestationChainValidator/KeyRotationService.CheckKeyValidityAsync) treats a key asExpiredfor a signature only whensignedAt >= ExpiresAt. Existing attestations signed before expiry remain verifiable — see Impact below.
Impact
| Impact Type | Description |
|---|---|
| User-facing | No new attestations can be signed; releases blocked at signing |
| Data integrity | Existing attestations signed before the key’s expiry remain valid; only new signing is blocked |
| SLA impact | Release SLO violated; compliance posture compromised |
Diagnosis
Quick checks
Run the Doctor key-material check:
stella doctor run --check check.attestation.keymaterialClassification thresholds (from
SigningKeyExpirationCheck):- Expired (
daysUntilExpiry < 0) → Fail - Critical (
< 7days) → Fail - Warning (
< 30days) → Warn - Otherwise → Pass
For
keylesssigning mode the check returns Skip (no expiring key material).- Expired (
Inspect the configured signing mode and key material: The signing mode is read from configuration by the
CosignKeyMaterialCheck(these keys are the check’s inputs — there is no boundAttestorSigningoptions class insrc/Attestor; the check falls back to the unprefixedSigning:*keys, then tokeyless):Attestor:Signing:Mode—keyless(default),file, orkmsAttestor:Signing:KeyPath— file-key path (modefile)Attestor:Signing:KmsKeyRef— KMS key reference (modekms)
# Verify key material availability (existence + mode), not expiration: stella doctor run --check check.attestation.cosign.keymaterialList and check signing keys (CLI): The wired CLI surface is
stella key(singular). It maps to the Signer trust-anchor API and takes the anchor GUID as an argument, but currently fails closed (“requires a configured key rotation service”; see the source-of-truth note above), so it is informational only today:stella key list <anchorId> # --include-revoked, --output text|json stella key status <anchorId> # rotation status + warningsThe former plural
stella keyssample-data group was not wired and was deleted on 2026-07-29; it is not an alternate inspection path.
Deep diagnosis
Query the Signer trust-anchor key state (production path): Key lifecycle is owned by the Signer service. Use the trust-anchor API (
StellaOps.Signer.WebService→KeyRotationEndpoints), which requires thesigner:rotatescope. The endpoint group enforces theKeyManagementauthorization policy (KeyRotationEndpoints.MapKeyRotationEndpoints→.RequireAuthorization("KeyManagement")).KeyManagementis a legacy alias ofSignerPolicies.KeyManagement("Signer.KeyManagement"); both are registered inProgram.csand both map toStellaOpsScopes.SignerRotate(signer:rotate):# Full key history for an anchor (added/revoked/expires timestamps): GET /api/v1/anchors/{anchorId}/keys/history # Rotation warnings (ExpiryApproaching / LongLived / AlgorithmDeprecating): GET /api/v1/anchors/{anchorId}/keys/warnings # Whether a key was valid at a given signing time (status: Active/Expired/Revoked/NotYetValid): GET /api/v1/anchors/{anchorId}/keys/{keyId}/validity?signedAt=<iso8601>Signer
KeyRotationOptionsdefaults:ExpiryWarningDays = 60,MaxKeyAgeDays = 365. Deprecated algorithms warned by default:RSA-2048,SHA1-RSA.Review the key audit trail: Production path — the Signer key history (
GET /api/v1/anchors/{anchorId}/keys/history), which is backed by the auditedKeyAuditLog(operationsAdd/Revoke). The wired CLI equivalent is:stella key history <anchorId> [--key-id <id>] [--limit 50] [--output text|json](Currently fail-closed — see source-of-truth note.) Use the Signer history endpoint until the registered CLI client is wired.
NOT IMPLEMENTED (removed from earlier draft): the following commands have no implementation in
src/Cli:stella keys list --type signing --show-expiration,stella keys show <id> --details,stella crypto cert verify-chain --key <id>,stella keys list --type signing --status inactive,stella keys rotation-history. Use the Signer trust-anchor API (/api/v1/anchors/.../keys/historyand.../validity) instead.
Resolution
Immediate mitigation
The Signer key model revokes/rotates keys rather than “activating a backup”. A key removed from an anchor’s AllowedKeyIds stays valid for historical proofs but cannot sign new material.
Rotate the expired/critical key with an overlap period (CLI): The wired command is
stella key rotate(singular), which takes the trust-anchor GUID plus the old and new key IDs:stella key rotate <anchorId> <oldKeyId> <newKeyId> --overlap-days 7 \ --algorithm Ed25519 --public-key <new-key.pem>--overlap-daysdefaults to 30;--algorithmacceptsEd25519(default),ES256,ES384, orRS256. This currently fails closed (“requires a configured key rotation service”) — until the API client is wired, perform the rotation via the Signer API directly (step 2 below).Production path — add a new key to the trust anchor (Signer API):
# Requires scope signer:rotate (policy Signer.KeyManagement) POST /api/v1/anchors/{anchorId}/keys { "keyId": "...", "publicKey": "<PEM>", "algorithm": "ES256", "expiresAt": "<iso8601>" } # Then revoke the expired key (it remains valid for proofs signed before revocation): POST /api/v1/anchors/{anchorId}/keys/{keyId}/revoke { "reason": "expired - rotated", "effectiveAt": "<iso8601>" }Allowed algorithm strings (Signer
KeyRotationOptions.AllowedAlgorithms):ES256,ES384,ES512,RS256,RS384,RS512,PS256,PS384,PS512,ED25519/EdDSA,SM2,GOST12-256,GOST12-512,DILITHIUM3,FALCON512(plus deprecatedRSA-2048,SHA1-RSA).Verify signing works by creating and verifying a test attestation (see Verification).
Root cause fix
Rotate to a fresh signing key:
Add the new key to the trust anchor via the Signer API (
POST /api/v1/anchors/{anchorId}/keys) with the desiredalgorithmandexpiresAt. (Once the CLI client is wired, the equivalent isstella key add <anchorId> <keyId> --algorithm <alg> --public-key <pem>; it fails closed today.)Keep an overlap window so verifiers accept both old and new keys during transition. Until the expired key is revoked, both stay in the anchor’s
AllowedKeyIds; the wired CLI helper (currently fail-closed) is:stella key rotate <anchorId> <oldKeyId> <newKeyId> --overlap-days 14Revoke the expired key once the overlap window closes (
POST /api/v1/anchors/{anchorId}/keys/{keyId}/revoke). Revocation is audited (KeyAuditLogEntity, operationRevoke).
Set up expiration monitoring:
Monitoring today is not a single CLI command. Use one (or both) of:
- The Doctor check
check.attestation.keymaterial(Warn at 30 days, Fail at 7 days / expired) on a scheduledstella doctor run(e.g. via cron / CI), and - The Signer trust-anchor
warningsendpoint (GET /api/v1/anchors/{anchorId}/keys/warnings, default 60-dayExpiryApproachingwarning).
Wire either signal into your existing notification routing (stella notify rules ...).
NOT IMPLEMENTED —
stella notify channels add: thestella notify channelsgroup exposes onlylist,show, andtestsubcommands (CommandFactory.cs) — there is noadd/createsubcommand, and no--event/--threshold-daysoptions. Thestella notify channels add --type email --event key.expiring --threshold-days 30line that theSigningKeyExpirationCheckemits as remediation text (and that earlier drafts repeated) does not resolve to a real command. Channel CRUD is managed elsewhere (Console / Notify API); the CLI only lists/shows/tests channels and manages routingrules.ROADMAP / NOT IMPLEMENTED (other): automatic-rotation config keys referenced in earlier drafts (
rotation.auto,rotation.before_expiry,rotation.overlap_days,alerts.expiring_days,alerts.expiring_days_critical) and the commandsstella keys generate,stella keys register,stella keys activate,stella keys config set,stella attest config set,stella attest reload,stella attest test-sign,stella attest retry,stella issuer keys publishdo not exist in the current codebase. Expiry monitoring today is the Doctor checkcheck.attestation.keymaterial(thresholds 30/7 days) plus the Signerwarningsendpoint (default 60-day expiry warning). The HelmautoRotatesnippet is aspirational.
Verification
# Re-run the Doctor key-material check (should no longer Fail/Warn):
stella doctor run --check check.attestation.keymaterial
# Show rotation status + warnings for the anchor (wired CLI; fail-closed until API client lands):
stella key status <anchorId>
# Verify an attestation chain attached to an OCI artifact (CLI prints sample output — see note below):
stella verify attestation --image <image-ref>
# Confirm a key's validity at a signing time via the Signer API:
GET /api/v1/anchors/{anchorId}/keys/{keyId}/validity?signedAt=<iso8601>
CLI shape (corrected) + demo caveat:
stella verify attestation(inCommands/VerifyCommandGroup.cs→BuildVerifyAttestationCommand) takes a required--image/-ioption — not a positional argument — plus optional--predicate-type/-t,--policy/-p,--output/-o, and--strict. As shipped it prints hardcoded sample output (verified = truewith placeholder signers); it does not yet perform real chain verification, so do not treat its output as authoritative. The SignervalidityAPI (above) is the trustworthy check today.NOT IMPLEMENTED:
stella attest create --type test ...,stella attest test-sign, andstella verify attestation --lastare not part of the CLI.
Prevention
- [ ] Rotation: Rotate keys before expiry with an overlap window (
--overlap-days, or the SigneraddKey+ delayedrevokeflow). There is no built-in auto-rotation today. - [ ] Monitoring: Track the Doctor check
check.attestation.keymaterial(Warn at 30 days, Fail at 7 days / expired) and the Signerwarningsendpoint (default 60-day expiry warning). - [ ] Algorithm hygiene: Migrate off deprecated algorithms (
RSA-2048,SHA1-RSA) flagged by SignerAlgorithmDeprecatingwarnings; preferEd25519/ES256. - [ ] Documentation: Document key rotation procedures and approval process; key add/revoke operations are audit-logged in the Signer
KeyAuditLog.
Related Resources
- Architecture:
docs/modules/attestor/architecture.md - Doctor article (companion):
docs/doctor/articles/attestor/keymaterial.md - Cosign key material check article:
docs/doctor/articles/attestor/cosign-keymaterial.md - Related runbooks:
attestor-signing-failed.md,attestor-hsm-connection.md(both in thisrunbooks/dir);key-rotation-runbook.md,hsm-setup-runbook.md,key-escrow-runbook.md(all indocs/operations/) - Doctor check source:
src/Doctor/__Plugins/StellaOps.Doctor.Plugin.Attestor/Checks/SigningKeyExpirationCheck.cs,.../Checks/CosignKeyMaterialCheck.cs - Key rotation source:
src/Attestor/__Libraries/StellaOps.Signer.KeyManagement/KeyRotationService.cs(options/enums inIKeyRotationService.cs, entities inEntities/KeyEntities.cs),src/Attestor/StellaOps.Signer/StellaOps.Signer.WebService/Endpoints/KeyRotationEndpoints.cs, policy registration.../StellaOps.Signer.WebService/Program.cs+Security/SignerPolicies.cs - CLI source: wired
stella keygroupsrc/Cli/StellaOps.Cli/Commands/Proof/KeyRotationCommandGroup.cs(root-wired viaCommandFactory.BuildKeyCommand); the former demo/unwired pluralstella keysgroup was deleted on 2026-07-29;stella verify attestationsrc/Cli/StellaOps.Cli/Commands/VerifyCommandGroup.cs; Doctor CLICommands/DoctorCommandGroup.cs
