Derive declared credentials' recipients from the rotation registry #47
No reviewers
Labels
No labels
blocked
bug
decision
duplicate
enhancement
handoff
help wanted
invalid
landed?
question
ready-to-merge
stale
wontfix
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
allod/secrets!47
Loading…
Reference in a new issue
No description provided.
Delete branch "agent/derive-registry-recipients"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
secrets.nixnow computes the recipients of every credential listed inrotation-registry.json, the way it already computes them for forge keys, VM host keys and Pi credentials, so a declared credential needs no hand-written recipient line. On the template nothing changes: its registry is empty, and the evaluatedsecrets.nixis byte for byte what it was. What proves it: the newregistry-recipientscheck pins the derivation on a synthetic registry, refuses a target machine missing from the key file, two credentials on one path and a bad hypervisor key list, and assertssecrets.nixcarries every derived line unchanged; the whole gate passes. If it is wrong, revert the one commit; nothing consumes the new map until a fork adopts it and the tool stops writing its line, both tracked separately.This is the first of three steps in allod/secrets#46. The second is each deployment fork adopting the derivation in its own secrets repo; the third is
allod secret declaredropping itssecrets.nixinsertion, filed on allod/tools once this merges and reaching hosts only after the fork step.What changed
lib/registry-recipients.nixreturns{ recipients; diagnostics; }.recipientsmaps each registry credential'ssecret_pathtohypervisorPublicKeysfollowed by the active and staged keys of each target whosekindis notnixos-host, in target order, each machine once; it throws with every diagnostic when there is one.diagnosticsis the list itself, so a check can assert the wording: a target machine absent frommachine-host-keys.jsonnames the credential and the machine and says a hypervisor target needskind "nixos-host"; two credentials on onesecret_pathname the path; a hypervisor key list that is empty, non-unique or not strings is named. Builtins only, because agenix evaluatessecrets.nixstandalone.secrets.nixreadsrotation-registry.jsonand merges the derived map last.flake.nixexportslib.registryRecipients, built from the validated registry, and hands it to the checks.checks/registry-recipients.nix: an empty registry derives nothing; the fixture of two groups and three credentials, one host-only and one with a repeated target, derives the expected lists with no diagnostics; four sabotage fixtures are refused, and the two that name things are checked for their exact message; every path in the flake's map is present insecrets.nixwith equal recipients. That last assertion is vacuous on the template, whose registry is empty, and bites in a fork; the README row says so.librow, the check row, and one bullet under the recipient model.Risk
R2. A new derivation merged into the recipient map of a security-relevant file, localised and fixture-tested, with the template's output proven identical. Rollback is a revert. The fork step and the tool step carry their own scores.
Validation
nix build .#checks.x86_64-linux.registry-recipientsbuilds in the worktree.nix eval --json --file secrets.nixin the shared checkout and in the worktree, compared withcmp: identical.allod checkin the worktree: all 10 steps passed, 9 checks built, the new one among them.gpt-5.6-solthrough pi'snullsinkprovider, since codex holds no credentials on this VM. Eight findings, all verified against the source and all taken in one fix round: a diagnostic that interpolated an unvalidated credential name (now guarded); a target withoutkindbeing looked up as a guest (kept as a refusal, now naming the remedy); the staged key read tolerating an absent field (now strict, matching the existingvmKeyshelper); sabotage fixtures that only proved some throw happened (the lib now exposesdiagnosticsand the check asserts the exact messages); thesecrets.nixconsumption assertion being vacuous on the template (true, stated in the README row); and three comments that explained or narrated (cut to one line each or deleted). The three witnesses above were re-run on the final code.Refs allod/secrets#46