Rotate the root key safely
This operations page is for administrators of self-hosted Workspace installations. Use it for a planned key change, a legacy single-key migration, or the controlled shutdown of a compromised key.
When you finish, every participating process uses the same bundle with the intended active key version. You either keep the old key for the required transition period or remove it only after every mandatory check is complete.
Choose the correct path first
| Situation | Correct path | What happens to the old key |
|---|---|---|
The installation still uses one legacy NUCLEUS_KEY. | Migrate to the bundle format first. | Preserve the existing key material. This is not a rotation yet. |
| The change is planned and no compromise is suspected. | Use routine rotation. | Keep the old key decrypt-enabled for 365 days after observation starts. |
| The key is confirmed or strongly suspected to be compromised. | Use emergency rotation. | Skip only the remaining observation time. Every technical gate remains mandatory. |
| A check is incomplete or blocked. | Abort or roll back to the last complete bundle. | Keep the old key. There is no unchecked force path. |
Treat an unclear situation as blocked. Removing a required key is not recoverable without the protected backup.
Understand the tool boundary
nucli operates a running server through authenticated APIs. It must not read local root-key bundles, configuration keys, databases, or storage files. Its built-in skill explains the workflow but does not perform the rotation:
nucli skills show root-key-rotationMCP clients receive the same guidance from nucleus_root_key_rotation_skill. The tool accepts no key values or local file paths.
Use only the approved, version-matched, privileged operations procedure supplied with your installation for bundle changes, configuration rekey, inventory, gates, and retirement. numin describes the bounded offline contracts of numin; root-key rotation is not one of them.
Follow the common phases
Routine and emergency rotation share the same technical sequence:
- Verify backups, a tested restore path, expected process roles, monitoring, and the change window.
- Prepare a new key version without activating it.
- Distribute the prepared bundle through the secret manager to every signer and verifier.
- Restart affected Go processes and require complete distribution before activation.
- Activate the new
kid, distribute the activated bundle, and restart the processes again. - Re-encrypt the server configuration and rewrite supported encrypted records only through their authoritative product APIs.
- Inspect configuration, fields, Blobs, runtime domains, and operation error counters. Start the owner-only observation state.
- Choose routine or emergency and remove the old key only after that path and every common gate are clear.
- Distribute the retired bundle, restart every affected process, and verify final convergence.
Never edit ciphertext directly in PostgreSQL. An unreachable database, incomplete storage scan, or missing process role means “not inspected,” not “no dependency.”
Compare routine and emergency
| Rule | Routine | Emergency |
|---|---|---|
| Intended use | Planned maintenance without a compromise | Confirmed or strongly suspected compromise |
| Observation time | Wait 365 days | May skip the remaining time |
| Distribution, inventory, runtime, error, and per-key gates | Mandatory | Mandatory |
| Still-valid tokens | Remain verifiable during retention | May be invalidated; explicit confirmation is required |
| Additional evidence | Change reference | Incident reference and token-invalidation confirmation |
Emergency is not force. It skips only time. The shutdown duration still includes bundle rollout, restarts, configuration and record rekey, inventory, and metric convergence. Different installations do not share one guaranteed duration.
Read the gates
| Gate | Successful result |
|---|---|
| Distribution | Every expected process reports the same bundle fingerprint and active kid. |
| Inventory | Configuration, fields, and Blobs no longer need the retiring kid; no missing, invalid, or uncovered data remains. |
| Runtime | Every expected signing and verification domain has post-activation activity on the active kid. |
| Errors | No relevant decrypt, verify, replay, signing, or unknown-KID error remains. |
| Observation | Mode, start time, fingerprint, target kid, and expected roles match the current rollout. |
| Removal | retirement and retirement_gate[<kid>] are clear for the old key. |
Handle a legacy single key
A legacy migration preserves the current key bytes and places them in the versioned bundle format. Distribute that bundle and verify a common fingerprint after restart. Perform the actual rotation to a new kid as a separate change afterward.
Keeping format migration and key rotation separate makes rollback and incident analysis easier.
Confirm success
The rotation is complete when every expected process reports the same final bundle and active kid, protected reads still work, no at-rest reference or new signature uses the retired kid, every gate is clear, and no unknown-KID, verify, decrypt, or read error appears.
An interrupted or incomplete inspection is not success. Keep the old key and repeat the inspection completely.
Next steps
- Inspect Blob dependencies with Blob v2 key inventory.
- Review installation and secret-storage prerequisites under Server installation.
- Use numin only for its explicitly documented offline contracts.