Disconnected emergency certificate issuance
Use this path when the control-plane API or PostgreSQL is unavailable. The
isolated trstctl-signer process reads a sealed recovery CA and a public policy
from its own files. It does not contact the control plane or database. Two
different custodians sign one exact request; knowing their names is insufficient.
The resulting certificate uses the workload's CSR public key. Keep the workload
private key with the workload.
Prepare before an outage
- Give each custodian a separate signer host/account and have each run
trstctl-signer --offline-generate-custodian <id> --offline-custodian-private-out <private.pem> --offline-custodian-public-out <public.json>. The private file is created owner-only. Each custodian keeps only their own private file. Collect the two public JSON roster entries, never the private files. - Write a public policy JSON with
tenant_id,deployment_id, an increasingepoch,threshold(at least 2),max_ttl_seconds, a SAN allowlist such aspermitted_dns_suffixes, and thecustodiansroster. A request outside these bounds is refused even if custodians sign it. - On the isolated recovery signer, run
trstctl-signer --offline-init-policy <policy.json> --keystore <offline-vault> --kek <offline-kek>. The signer seals a new CA key in the dedicated vault and prints the public kit path and its SHA-256. The offline CA handle is markedofflineOnly; the generic signerSignRPC refuses it, even when pointed at the same key store. Protect the vault and KEK as distinct backup assets. - Copy only
offline-kit.jsonto the control-plane host. In the deployment config, setbreakglass.offline_kit_fileto that read-only copy andbreakglass.offline_kit_sha256to the independently recorded printed digest. The kit's tenant must equalbreakglass.tenant_id, and its deployment must equalbreakglass.deployment_id. A licensed deployment must also matchlicense.deployment_id. Startup refuses a kit bound to another tenant or deployment; the free core can setbreakglass.deployment_idwithout a license. Keep the existingbreakglass.ca_cert_fileandpublic_key_filetrust anchor for online ceremonies. Start the control plane and check the recovery route. Startup fails if the kit differs from the pin. A self-signed kit alone is not a trust anchor.
Issue while disconnected
- Create a PKCS#10 CSR with the workload key. Inspect its common name and
every SAN with
openssl req -in <csr.pem> -noout -text. Copy the public CSR to the isolated signer. - Run
trstctl-signer --offline-prepare-kit <offline-kit.json> --offline-prepare-csr <csr.pem> --offline-prepare-requester <requester> --offline-prepare-subject <exact-csr-common-name> --offline-prepare-reason <incident-reason> --offline-prepare-ttl 10m --offline-output <request.json>. This chooses a random operation ID and a one-hour approval deadline. Give the same request file to each custodian. - Each custodian checks the request's tenant, deployment, policy epoch,
ca_public_key_sha256against their retained public kit, reason, TTL, common name and signed SANs, then runstrstctl-signer --offline-approve-request <request.json> --offline-approve-key <their-key.pem> --offline-approve-custodian <their-id> --offline-output <their-approval.json>in their own session. Combine the public approval JSON objects into an array file, for examplejq -s '.' alice-approval.json bob-approval.json > approvals.json. - Run
trstctl-signer --offline-issue-request <request.json> --offline-issue-approvals <approvals.json> --keystore <offline-vault> --kek <offline-kek> --offline-output <bundle.json>. The signer verifies both signatures and the identity policy before signing. It seals an executing marker before the private-key operation and the complete bundle before replying. If the process dies before the completed result, the signer resumes the same approved intent; if it dies after completion, an exact retry returns the same bundle. Changing the request under the same ID is refused. - Install the leaf and retained workload private key using the target's
emergency deployment procedure. Check the served endpoint independently with
openssl s_client -connect <host:port> -servername <name> -CAfile <offline-ca.pem> -verify_hostname <name> -verify_return_error.
Recover the control plane
After restoring the API and database, submit the complete bundle with
POST /api/v1/breakglass/reconcile or trstctl-cli breakglass reconcile -f <body.json>, where the body is {"bundles":[<bundle>]}. The Incidents console
also accepts this JSON. The server checks the independently pinned kit, all
custodian signatures, the CSR/leaf identity and lifetime, and the CA signature.
It writes an immutable breakglass.issued v3 event containing the public proof.
An exact retry with another HTTP idempotency key records zero new events; a
different bundle under the same tenant and request ID conflicts. Save the
incident's stock-client served-certificate readback alongside the audit export.
--allow-insecure-dev-nonlinux is only for a local macOS lab. Production
recovery signers should run on a hardened Linux host with protected KEK storage.