Fail at build time when a machine cannot decrypt a secret it is given #20

Open
opened 2026-07-31 00:54:26 +01:00 by allod-agent · 0 comments
Member

Make a machine refuse to build when it would be handed a secret it cannot decrypt. This is the check issue #17 asked for and did not get: the assertion added for it compared which credentials were present, never whether the machine could actually open them, so the failure #17 describes still lands at first boot rather than at build time.

The rule that works: for every encrypted secret a machine generates, that machine's own host public key must be among the ciphertext's recipients. It fires exactly when activation would fail to decrypt, it generalises to every secret a fork adds rather than naming two by hand, and it never blocks a legitimate credential split — unlike the pairing assert removed in allod/archetypes#19, which refused a machine that may rebuild itself but may not open pull requests.

What it needs

The data is nearly in place. allod/secrets already derives every recipient list from machineHostKeys via vmKeys in secrets.nix, and already binds that file as secretsNix in its own flake.nix, where its own checks consume it. It is never exported, so archetypes cannot see recipients.

  • allod/secrets: export it, lib.secretRecipients = secretsNix;. One line.
  • allod/archetypes: for each machine, compare the recipients of every age.secrets entry's file against that machine's registered host public key, and fail evaluation naming the machine and the secret when the key is absent.

Land the export first; the check cannot be written before it exists.

Scope

In scope: the export, the check, and paired fixtures — a machine holding a secret it is not a recipient of must fail, and today's machines must pass.

Out of scope: changing any recipient set, and the forgejo-token-groups.json drift tracked separately.

Why it matters beyond this bug

An undecryptable secret currently surfaces during provisioning, which for a new machine is the worst moment: the operator is mid-install and the diagnostic is an age --decrypt failure that names a file, not the mistake. This turns it into a build-time error that names the machine and the secret.

Make a machine refuse to build when it would be handed a secret it cannot decrypt. This is the check issue #17 asked for and did not get: the assertion added for it compared which credentials were present, never whether the machine could actually open them, so the failure #17 describes still lands at first boot rather than at build time. The rule that works: for every encrypted secret a machine generates, that machine's own host public key must be among the ciphertext's recipients. It fires exactly when activation would fail to decrypt, it generalises to every secret a fork adds rather than naming two by hand, and it never blocks a legitimate credential split — unlike the pairing assert removed in allod/archetypes#19, which refused a machine that may rebuild itself but may not open pull requests. ### What it needs The data is nearly in place. `allod/secrets` already derives every recipient list from `machineHostKeys` via `vmKeys` in `secrets.nix`, and already binds that file as `secretsNix` in its own `flake.nix`, where its own checks consume it. It is never exported, so `archetypes` cannot see recipients. - `allod/secrets`: export it, `lib.secretRecipients = secretsNix;`. One line. - `allod/archetypes`: for each machine, compare the recipients of every `age.secrets` entry's file against that machine's registered host public key, and fail evaluation naming the machine and the secret when the key is absent. Land the export first; the check cannot be written before it exists. ### Scope In scope: the export, the check, and paired fixtures — a machine holding a secret it is not a recipient of must fail, and today's machines must pass. Out of scope: changing any recipient set, and the `forgejo-token-groups.json` drift tracked separately. ### Why it matters beyond this bug An undecryptable secret currently surfaces during provisioning, which for a new machine is the worst moment: the operator is mid-install and the diagnostic is an `age --decrypt` failure that names a file, not the mistake. This turns it into a build-time error that names the machine and the secret.
Sign in to join this conversation.
No description provided.