96 lines
3.8 KiB
Text
96 lines
3.8 KiB
Text
let pb = import "schemas/lib/playbook.ncl" in
|
|
|
|
pb.make_playbook {
|
|
id = "rotate_keys",
|
|
name = "Key Rotation",
|
|
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.",
|
|
version = 1,
|
|
|
|
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",
|
|
],
|
|
|
|
params = [
|
|
{ name = "workspace", description = "Target workspace (e.g. libre-wuji)", required = true, default_val = "" },
|
|
{ name = "key_type", description = "Which key to rotate: ops-controller | keeper | operator", required = true, default_val = "" },
|
|
{ name = "new_key_path", description = "Path to the new Ed25519 private key PEM", required = true, default_val = "" },
|
|
{ name = "new_pubkey_path", description = "Path to the new Ed25519 public key PEM", required = true, default_val = "" },
|
|
{ name = "ops_vm_host", description = "ops-vm SSH host (required for keeper rotation)", required = false, default_val = "" },
|
|
],
|
|
|
|
steps = [
|
|
{
|
|
id = "validate_new_key",
|
|
name = "Validate new key is a valid Ed25519 keypair",
|
|
script = "steps/validate_new_key.nu",
|
|
dry_run_arg = "--dry-run",
|
|
on_error = 'Stop,
|
|
depends_on = [],
|
|
},
|
|
{
|
|
id = "propose_delegation_patch",
|
|
name = "Propose delegation patch on state-<workspace> repo",
|
|
script = "steps/propose_delegation_patch.nu",
|
|
dry_run_arg = "--dry-run",
|
|
on_error = 'Stop,
|
|
depends_on = ["validate_new_key"],
|
|
},
|
|
{
|
|
id = "sign_delegation_patch",
|
|
name = "Await quorum signatures on delegation patch",
|
|
script = "steps/sign_delegation_patch.nu",
|
|
dry_run_arg = "--dry-run",
|
|
on_error = 'Stop,
|
|
depends_on = ["propose_delegation_patch"],
|
|
},
|
|
{
|
|
id = "activate_new_key",
|
|
name = "Activate new key (deploy to target service)",
|
|
script = "steps/activate_new_key.nu",
|
|
dry_run_arg = "--dry-run",
|
|
on_error = 'Rollback,
|
|
depends_on = ["sign_delegation_patch"],
|
|
},
|
|
{
|
|
id = "verify_continuity",
|
|
name = "Verify audit/signing continuity with new key",
|
|
script = "steps/verify_continuity.nu",
|
|
dry_run_arg = "--dry-run",
|
|
on_error = 'Continue,
|
|
depends_on = ["activate_new_key"],
|
|
},
|
|
{
|
|
id = "revoke_old_key",
|
|
name = "Revoke old key from delegation set",
|
|
script = "steps/revoke_old_key.nu",
|
|
dry_run_arg = "--dry-run",
|
|
on_error = 'Continue,
|
|
depends_on = ["verify_continuity"],
|
|
},
|
|
{
|
|
id = "emit_audit",
|
|
name = "Emit key-rotation audit event",
|
|
script = "steps/emit_audit.nu",
|
|
params = { event_type = "key_rotation" },
|
|
dry_run_arg = "--dry-run",
|
|
on_error = 'Continue,
|
|
depends_on = ["revoke_old_key"],
|
|
},
|
|
],
|
|
|
|
rollback_strategy = 'manual,
|
|
|
|
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",
|
|
],
|
|
|
|
emit_audit = true,
|
|
adr_refs = ["adr-037", "adr-038"],
|
|
}
|