136 lines
3.9 KiB
JSON
136 lines
3.9 KiB
JSON
{
|
|
"adr_refs": [
|
|
"adr-037",
|
|
"adr-038"
|
|
],
|
|
"description": "Rotates ops-controller key, keeper key, or operator key. For ops-controller: generates new key, proposes delegation patch on state-<workspace> signed by quorum, activates new key, verifies continuity. Keeper and operator key rotation follow the same quorum-sign pattern.",
|
|
"emit_audit": true,
|
|
"id": "rotate_keys",
|
|
"name": "Key Rotation",
|
|
"params": [
|
|
{
|
|
"default_val": "",
|
|
"description": "Target workspace (e.g. libre-wuji)",
|
|
"name": "workspace",
|
|
"required": true
|
|
},
|
|
{
|
|
"default_val": "",
|
|
"description": "Which key to rotate: ops-controller | keeper | operator",
|
|
"name": "key_type",
|
|
"required": true
|
|
},
|
|
{
|
|
"default_val": "",
|
|
"description": "Path to the new Ed25519 private key PEM",
|
|
"name": "new_key_path",
|
|
"required": true
|
|
},
|
|
{
|
|
"default_val": "",
|
|
"description": "Path to the new Ed25519 public key PEM",
|
|
"name": "new_pubkey_path",
|
|
"required": true
|
|
},
|
|
{
|
|
"default_val": "",
|
|
"description": "ops-vm SSH host (required for keeper rotation)",
|
|
"name": "ops_vm_host",
|
|
"required": false
|
|
}
|
|
],
|
|
"preconditions": [
|
|
"Identify which key type to rotate: ops-controller | keeper | operator",
|
|
"Ensure quorum of existing signers is available to sign the delegation patch",
|
|
"For ops-controller rotation: audit mirror must be paused or confirmed resilient to a 30-60s gap",
|
|
"Generate the new key offline before running this playbook"
|
|
],
|
|
"rollback_strategy": "manual",
|
|
"steps": [
|
|
{
|
|
"depends_on": [],
|
|
"dry_run_arg": "--dry-run",
|
|
"id": "validate_new_key",
|
|
"name": "Validate new key is a valid Ed25519 keypair",
|
|
"on_error": "Stop",
|
|
"params": {},
|
|
"script": "steps/validate_new_key.nu"
|
|
},
|
|
{
|
|
"depends_on": [
|
|
"validate_new_key"
|
|
],
|
|
"dry_run_arg": "--dry-run",
|
|
"id": "propose_delegation_patch",
|
|
"name": "Propose delegation patch on state-<workspace> repo",
|
|
"on_error": "Stop",
|
|
"params": {},
|
|
"script": "steps/propose_delegation_patch.nu"
|
|
},
|
|
{
|
|
"depends_on": [
|
|
"propose_delegation_patch"
|
|
],
|
|
"dry_run_arg": "--dry-run",
|
|
"id": "sign_delegation_patch",
|
|
"name": "Await quorum signatures on delegation patch",
|
|
"on_error": "Stop",
|
|
"params": {},
|
|
"script": "steps/sign_delegation_patch.nu"
|
|
},
|
|
{
|
|
"depends_on": [
|
|
"sign_delegation_patch"
|
|
],
|
|
"dry_run_arg": "--dry-run",
|
|
"id": "activate_new_key",
|
|
"name": "Activate new key (deploy to target service)",
|
|
"on_error": "Rollback",
|
|
"params": {},
|
|
"script": "steps/activate_new_key.nu"
|
|
},
|
|
{
|
|
"depends_on": [
|
|
"activate_new_key"
|
|
],
|
|
"dry_run_arg": "--dry-run",
|
|
"id": "verify_continuity",
|
|
"name": "Verify audit/signing continuity with new key",
|
|
"on_error": "Continue",
|
|
"params": {},
|
|
"script": "steps/verify_continuity.nu"
|
|
},
|
|
{
|
|
"depends_on": [
|
|
"verify_continuity"
|
|
],
|
|
"dry_run_arg": "--dry-run",
|
|
"id": "revoke_old_key",
|
|
"name": "Revoke old key from delegation set",
|
|
"on_error": "Continue",
|
|
"params": {},
|
|
"script": "steps/revoke_old_key.nu"
|
|
},
|
|
{
|
|
"depends_on": [
|
|
"revoke_old_key"
|
|
],
|
|
"dry_run_arg": "--dry-run",
|
|
"id": "emit_audit",
|
|
"name": "Emit key-rotation audit event",
|
|
"on_error": "Continue",
|
|
"params": {
|
|
"event_type": "key_rotation"
|
|
},
|
|
"script": "steps/emit_audit.nu"
|
|
}
|
|
],
|
|
"success_criteria": [
|
|
"New key is active and accepted by the target service",
|
|
"Delegation patch is merged in state-<workspace> Radicle repo",
|
|
"Old key is removed from delegation set",
|
|
"ops history shows key-rotation audit event",
|
|
"No interruption in audit trail during rotation"
|
|
],
|
|
"version": 1
|
|
}
|