Skip to content

docs(runbooks): repair the two key-recovery instructions the re-key broke - #488

Merged
Gerrrt merged 1 commit into
mainfrom
gerrrt/second-age-recipient-sops-2e5c5c
Sep 17, 2026
Merged

Gerrrt merged 1 commit into
mainfrom
gerrrt/second-age-recipient-sops-2e5c5c

Conversation

@Gerrrt

@Gerrrt Gerrrt commented Sep 17, 2026

Copy link
Copy Markdown
Owner

What this is

Not the second recipient — that merged on 2026-09-09 in #412. This repairs the
two pieces of runbook text that re-key silently invalidated, both of which sit on
the path of the one step #294
still has open.

#294 stays open. Its last unticked box is "both keys independently verified
with make secrets-verify-backup"
, and that needs the technical second's
removable medium physically at the monitoring host. recipient-state.prom has
age1cutv5q… at 0, never proved, with SecretsKeyBackupUnproven naming it —
ADR-0024's honest state, not a fault. This PR makes the text that operator will
read correct; it cannot tick the box.

grep -A3 creation_rules .sops.yaml returns zero keys

.sops.yaml grew to four rules (lab, sensitive, soc, catch-all). The real
recipients are now ~40 lines below the anchor, and -A3 never reaches them — the
command prints a comment block and nothing else. Reproduced on main.

successor-handover.md put that in front of a reader who has lost the key,
captioned # every key that can open the secrets, one sentence above "If a
recipient there is one somebody else holds, the secrets are recoverable and this
is a phone call rather than a rotation."
A successor sees no age1…, concludes
there is no second recipient, and chooses four SNMP rotations on hardware over a
phone call — in the emergency the page exists for, and defeating precisely the
outcome #294 was filed to produce.

Replaced with ./scripts/key-recipients.sh --list, which reads the recipients
out of the ciphertext, needs no private key, and is pure grep for the reason its
own header gives: "bare metal, where python3 and PyYAML are not yet a given."
The .sops.yaml grep is kept beside it as a deliberate cross-check — policy
against fact — since the two disagreeing is the hand-edit failure .sops.yaml's
own header warns about. The same broken command appeared inline in
back-up-the-age-key.md and is fixed there too.

The expected-output block was written for one recipient

back-up-the-age-key.md §2 shows what make secrets-verify-backup prints. Against
scripts/verify-key-backup.sh:

Runbook said Script does
-- recipient matches .sops.yaml: age1… -- recipient age1… (L188)
-- this file has 2 recipient(s); this run proves one of them (L189)
(6/6 keys), "all six keys" REQUIRED holds nine names (L231-241)
multi-recipient tail block when >1 recipient (L293-299)

The first row is more than cosmetic: §3 matches the backup's public half against
the recipients read from the ciphertext, deliberately not .sops.yaml, which
is ADR-0024's central point. Advertising the check as "matches .sops.yaml"
teaches the wrong model in the one document about key recovery. An operator
proving the technical second's copy would also have seen a different hash and a
"one of two" framing the page never mentioned.

Not touched

Verification

$ ./scripts/key-recipients.sh --list        → both recipients, exit 0
$ grep -oE 'age1[a-z0-9]+' .sops.yaml       → same two, no drift
$ make check-docs                           → docs OK, 10 assertions
$ ./scripts/lint.sh                         → all five PASS

Links and the #adding-a-second-recipient anchor checked by hand — there is no
link checker in CI.

🤖 Generated with Claude Code

…roke

The second recipient landed in e638964 and silently invalidated two pieces of
runbook text, both on the path of the one step #294 still has open.

`grep -A3 creation_rules .sops.yaml` no longer prints a single key. .sops.yaml
grew to four rules; the real recipients sit ~40 lines below the anchor and -A3
never reaches them, so the command returns a comment block. successor-handover.md
put that in front of a reader who has lost the key, captioned "every key that can
open the secrets", one sentence above "this is a phone call rather than a
rotation" — so the output says rotate when the truth is phone the technical
second. It is replaced by scripts/key-recipients.sh --list, which reads the
recipients out of the ciphertext, needs no private key, and is pure grep for the
reason its own header gives: bare metal, where python3 and PyYAML are not given.
The .sops.yaml grep stays as the deliberate cross-check, framed as policy against
fact, because the two disagreeing is the hand-edit failure that file warns about.

back-up-the-age-key.md's expected output for make secrets-verify-backup was
written for one recipient and six required keys. The script prints nine, no
longer says "matches .sops.yaml" — section 3 matches against the ciphertext now,
which is ADR-0024's whole point — and emits two lines plus a tail block that only
exist once a file has more than one recipient. An operator proving the technical
second's copy would have seen a different hash and a "one of two" framing the
page never mentioned.

Verified: both documented commands run here and agree with each other;
make check-docs, lint.sh and markdownlint pass.

Refs #294.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Gerrrt
Gerrrt merged commit fc4490e into main Sep 17, 2026
3 checks passed
@Gerrrt
Gerrrt deleted the gerrrt/second-age-recipient-sops-2e5c5c branch September 17, 2026 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant