allod secret rotate: move value rotation from rotate-token into the secret namespace #197

Closed
opened 2026-09-11 00:16:19 +01:00 by allod-agent · 1 comment
Member

Move value rotation into allod secret rotate <name> so every landing of a credential's ciphertext — first value, new value, new recipients — is one command on the host, and rotate-token can retire.

Primary goals:

  • Same gate, same shape — the branch, clean-tree, recipients-from-secrets.nix, checks-then-commit-and-push behavior of allod secret create (allod/tools#182), applied to replacing an existing ciphertext's value from stdin.
  • Formats carry forward — the two structured formats decrypt the old value and replace one field (credential-store-url keeps user and host; rclone-remote-stanza keeps host and user and obscures the new password), as rotate-token does today.
  • Registry-driven output — the deploy, verify, and revocation steps come from the group's targets and service, with the Forgejo copy only for forgejo groups.
  • Local auth refresh decided, not ported blindly — rotate-token refresh-local-auth installs root and user netrc bundles under sudo; decide whether that belongs in allod secret or stays a separate host script before building it.

Current state and specifics

rotate-token (scripts/rotate-token in nexus, about 1450 lines of bash) handles group selection, three formats, local auth refresh, dry runs, and printed instructions, with 37 tests. allod/tools#182 deliberately shipped create and rekey only and deferred this, since building both would duplicate rotation. The registry stayed at forgejo-token-groups.json and lib.forgejoTokenGroups with service generalized to forgejo or none; renaming the file and export is cross-repo (archetypes reads it) and belongs with this work or its own issue, decided here.

Scope

In: the rotate path in Go behind the secret tag, the format carry-forward, dry run, and printed steps; the decision on local auth refresh and on the registry rename. Out: creating or revoking values at their source, rotation policy, and the Pi credential lifecycle, which pi-provider owns.

Move value rotation into `allod secret rotate <name>` so every landing of a credential's ciphertext — first value, new value, new recipients — is one command on the host, and `rotate-token` can retire. Primary goals: - **Same gate, same shape** — the branch, clean-tree, recipients-from-`secrets.nix`, checks-then-commit-and-push behavior of `allod secret create` (allod/tools#182), applied to replacing an existing ciphertext's value from stdin. - **Formats carry forward** — the two structured formats decrypt the old value and replace one field (`credential-store-url` keeps user and host; `rclone-remote-stanza` keeps host and user and obscures the new password), as `rotate-token` does today. - **Registry-driven output** — the deploy, verify, and revocation steps come from the group's targets and `service`, with the Forgejo copy only for `forgejo` groups. - **Local auth refresh decided, not ported blindly** — `rotate-token refresh-local-auth` installs root and user netrc bundles under sudo; decide whether that belongs in `allod secret` or stays a separate host script before building it. ### Current state and specifics `rotate-token` (`scripts/rotate-token` in nexus, about 1450 lines of bash) handles group selection, three formats, local auth refresh, dry runs, and printed instructions, with 37 tests. allod/tools#182 deliberately shipped `create` and `rekey` only and deferred this, since building both would duplicate rotation. The registry stayed at `forgejo-token-groups.json` and `lib.forgejoTokenGroups` with `service` generalized to `forgejo` or `none`; renaming the file and export is cross-repo (archetypes reads it) and belongs with this work or its own issue, decided here. ### Scope In: the rotate path in Go behind the `secret` tag, the format carry-forward, dry run, and printed steps; the decision on local auth refresh and on the registry rename. Out: creating or revoking values at their source, rotation policy, and the Pi credential lifecycle, which `pi-provider` owns.
Author
Member

Registry shape input from the first non-Forgejo, non-rclone credential (per-machine tailscale auth keys, allod/nexus#53 and allod/nexus#54): the format and verify.type vocabularies are now enumerated in three places that must agree, rotate-token's validator, the deployment's secrets flake check, and whatever this port validates. Each new kind of credential is a code change in all three, and #54 was that change for one word each. Two shapes worth deciding here rather than carrying forward:

  1. A plain value is the default, not a format. Only two formats are real containers where rotation keeps part of the text (credential-store-url keeps user and host, rclone-remote-stanza keeps everything but the password). raw-forgejo-token and raw both mean "the value itself" and differ only in a name that was Forgejo-shaped by accident. Let format be optional, meaning plain, and name only the containers; accept raw-forgejo-token and raw as aliases of absent while registries still carry them.

  2. Verify is a per-target line, not an enum. rotate-token never runs the probe; it prints one command for the operator. A verify.command on the target, printed verbatim (prefixed with ssh <system> when the target is not the host), needs no list to extend, and the three existing probes become three example lines. If a probe must stay structured for the local-auth refresh rule, keep that one and free the rest.

Either way the vocabulary should live in one checked place that both the secrets flake and this command read, not be copied.

Registry shape input from the first non-Forgejo, non-rclone credential (per-machine tailscale auth keys, allod/nexus#53 and allod/nexus#54): the `format` and `verify.type` vocabularies are now enumerated in three places that must agree, rotate-token's validator, the deployment's secrets flake check, and whatever this port validates. Each new kind of credential is a code change in all three, and #54 was that change for one word each. Two shapes worth deciding here rather than carrying forward: 1. **A plain value is the default, not a format.** Only two formats are real containers where rotation keeps part of the text (`credential-store-url` keeps user and host, `rclone-remote-stanza` keeps everything but the password). `raw-forgejo-token` and `raw` both mean "the value itself" and differ only in a name that was Forgejo-shaped by accident. Let `format` be optional, meaning plain, and name only the containers; accept `raw-forgejo-token` and `raw` as aliases of absent while registries still carry them. 2. **Verify is a per-target line, not an enum.** rotate-token never runs the probe; it prints one command for the operator. A `verify.command` on the target, printed verbatim (prefixed with `ssh <system>` when the target is not the host), needs no list to extend, and the three existing probes become three example lines. If a probe must stay structured for the local-auth refresh rule, keep that one and free the rest. Either way the vocabulary should live in one checked place that both the secrets flake and this command read, not be copied.
vnprc closed this issue 2026-09-12 02:24:16 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/tools#197
No description provided.