Signer Lifecycle Reference
Signer lifecycle administration is a Pro capability. It manages KMS-backed signer rotation, inventory, allowance evidence, retirement gates, and the forensic history of those actions. The shared schema, signer adapters, and movement-time signer safety remain Free.API
All mutation endpoints require theadmin role. Rotation history is readable
by admin and operator. The shared HTTP client signs each request; configure
QTG_KEY_ID, QTG_HMAC_SECRET_FILE (or QTG_HMAC_SECRET), and QTG_API_URL
before using the CLI or the expected-identity request below.
Rotation accepts the replacement signer and reason.
--drill returns
outcome: "drilled", persists drill evidence, and does not change signer
bindings; a state-changing rotation requires the impact acknowledgement.
Inventory persists evidence by default, and --no-persist is the explicitly
read-only form. The allowances command persists audit/snapshot evidence.
Retirement requires the signer to be deprecated, balances at or below the
configured token-unit dust ceiling, no nonterminal node reference, and an
allowance acknowledgement only when non-zero allowances deliberately remain.
There is no force flag. A retirement gate denial is HTTP 409 with
outcome: "denied" in the error body; the CLI raises QtgHttpError, exits
nonzero, and does not print a successful 200 response.
CLI
Theqtg signer group is the HTTP-backed CLI for the lifecycle endpoints:
--ack-allowances only for a documented exceptional residual allowance.
dust_threshold_usd is currently a raw token-unit ceiling, not a USD
conversion; drain to zero in production.
Enrollment and identity gate
Add replacement KMS signers throughMG_EXTRA_SIGNERS_JSON, then restart the
Pro application so its runtime registration and sync_registry_to_db()
materialization run. Never enroll a runtime signer with direct SQL. Verify the
active replacement through GET /v3/signers, then use the admin-only
PUT /v3/signers/{signer_key}/expected-identity endpoint to pin the identity.
For every drill and live rotation, the server automatically resolves the
replacement signer’s fresh runtime identity and compares its canonical value
with the persisted expected_signer_address. This signer I/O occurs outside the
lifecycle database transaction. The locked lifecycle path then revalidates the
operator pin, runtime signer continuity, and canonical identity equality before
impact, audit, rotation-event, metadata, or authority-remap work.
observed_signer_address is diagnostic and non-authoritative. It and
identity_state: "match" remain useful roster and health-refresh evidence, but
neither can authorize a drill or live rotation. The fresh manual probe in the
runbook remains defense in depth; the CLI does not compare identities locally.
Identity denials are HTTP 409 responses containing only a stable error code,
without addresses or backend exception text:
Compact request and response contracts
All bodies below require an admin HMAC key. Malformed bodies receive FastAPI 422 validation responses; the endpoint-specific errors below are the common current contracts. FastAPI serializes eachHTTPException body beneath the
top-level detail key, and QtgHttpError.body sees that full response JSON.
Rotate
drill: true, success returns outcome: "drilled", an impact report,
audit_event_id, and signer_rotation_event_id; it persists drill evidence
without changing signer bindings. With drill: false and
ack_impact: true, success returns outcome: "changed" with the same
evidence identifiers and performs the atomic rotation. A non-drill request
without acknowledgement returns 400 with
{"detail": {"error": "ack_impact_required"}}; failed preconditions return
409 with {"detail": {"error": "validation_failed", "cause": "<DETAIL>"}}.
A replacement health failure returns 409 with
{"detail": {"error": "new_signer_health_check_failed"}}. Identity failures
return one of the following code-only bodies:
Inventory
audit_event_id plus signer_rotation_event_id when persistence is
enabled. persist: false is the read-only form and leaves those evidence IDs
null; an unknown signer returns 404 with
{"detail": {"error": "signer_not_found", "signer_key": "<SIGNER_KEY>"}}.
Allowances
audit_event_id and signer_rotation_event_id.
unsupported_spender_not_checked signals a partial scan where configured
spender coverage was unavailable. An unknown signer returns 404 with
{"detail": {"error": "signer_not_found", "signer_key": "<SIGNER_KEY>"}}.
Retire
outcome: "retired" with audit_event_id and
signer_rotation_event_id. A hard gate denial returns HTTP 409 with
{"detail": {"outcome": "denied", "failed_gate": "<GATE>"}}, plus the
denial reason and nonterminal nodes when relevant; an unknown signer returns
404 with {"detail": {"error": "signer_not_found", "signer_key": "<SIGNER_KEY>"}}.
Secret-safe evidence
Record signer keys, addresses, outcomes, audit IDs, and sanitized snapshots as needed. Do not record KMS credentials, HMAC secrets,MG_EXTRA_SIGNERS_JSON,
raw provider responses, or secret-bearing URLs.
For the complete operator procedure, evidence expectations, exception handling,
and post-cutover AWS KMS deletion, use the KMS signer rotation runbook.