Repository navigation
Description
Finding
PR #453 adds secrets/opendesign.sops.yaml (commit 644b54774f9b6df7ce852100191a9a6f5c1cd450) holding the Open Design API key, SOPS-encrypted to two age recipients:
age125acas5y8hnwl5uhcp364xm8zlhtwsjqvxxpge8k04dzzr67acrq0ttc34(laptop-framework)age1nw32awc9ans8vv62h3zglz7sul8jt3vewlshz5vmyu9hexuddgys85h43p(Muse sandbox)
The sandbox recipient is the point of the file: per the PR body, the key serves the sandbox's OpenCode server (provider opendesign). The ciphertext itself is fine — this run verified the PR-head file decrypts with the sandbox age identity. No plaintext credential is exposed.
The problem: the dual-recipient encryption is not encoded anywhere in the repo's SOPS configuration, and the file does not pass the repo's own format check.
1. .sops.yaml has no rule covering the sandbox recipient — updatekeys silently drops it
The repo-root .sops.yaml on current main (and unchanged on PR #453's head) contains exactly one creation rule with no path_regex, listing only the laptop-framework key:
keys:
- &laptop-framework age125acas5y8hnwl5uhcp364xm8zlhtwsjqvxxpge8k04dzzr67acrq0ttc34
creation_rules:
- age: *laptop-frameworkEvery pre-existing SOPS file in the repo (secrets/sops/common/*.yaml, secrets/sops/hosts/laptop-framework/secrets.yaml, …) accordingly has only that one recipient.
Verified empirically this run (sops 3.13.2, scratch copy outside the repo): running sops updatekeys --yes on a copy of PR #453's file with the repo's .sops.yaml in place rewrites it to only the laptop-framework recipient — the sandbox recipient is removed. So the first routine key rotation / updatekeys / re-creation performed from the repo config silently revokes the sandbox's access to the very key this PR exists to give it, with no error anywhere; the failure only surfaces later when the sandbox can no longer decrypt.
(For contrast, dezren39/nix's .sops.yaml scopes its rule with path_regex: secrets/.*\.yaml$ — this repo's config has no path-scoped rule at all. This finding is specific to this repo's config and this file; no equivalent sandbox-recipient file exists in the other three repos.)
2. The PR's own treefmt check fails on the new file
PR #453 checks: Nix flake evaluation pass, run pass, treefmt check fail — run 39532849517, job 119002576950:
ERRO file has changed path=secrets/opendesign.sops.yaml prev_size=1722 … current_size=1640
Error: unexpected changes detected, --fail-on-change is enabled
The formatter rewrites the sops-generated YAML, so the file as committed cannot land under the repo's normal checks. Any reformat must be followed by re-verifying the file still decrypts and both recipients survive (formatting after encryption, never a decrypt → reformat → re-encrypt round-trip through the current .sops.yaml, which would hit problem 1).
Why it matters
The file's only consumer is the sandbox OpenCode server. As written, the PR merges a secret whose access grant exists only in the committed ciphertext, not in the configuration that governs every future edit of that file — a silent, self-inflicted revocation waiting on the next routine SOPS maintenance, plus a red format check on the PR itself.
Acceptance criteria
-
.sops.yamlgains a creation rule that coverssecrets/opendesign.sops.yaml(path-scoped, e.g.path_regex) listing both recipients above (viakey_groupsor anage:list), without changing the rule that governs the existingsecrets/sops/**files. - Demonstrated: on a copy of the merged file,
sops updatekeysleaves both recipients in place, andsops -dwith the sandbox age identity still succeeds. - The committed
secrets/opendesign.sops.yamlpassestreefmt check(the PR secrets: Open Design API key (SOPS) #453 check is green), with decryption and both recipients re-verified after any formatting. - PR secrets: Open Design API key (SOPS) #453 is merged only with the above in place (in the PR or a companion change), so the dual-recipient grant is reproducible from repo config, not just from the original author's command line.
Found by the pr-diff-review job reviewing PR #453 (open, 1 commit, +25/-0, 1 file).
Activity
fix(sops): scope opendesign file to both recipients in .sops.yaml; tr…
dezren39 commentedon Oct 21, 2026
Fixed in PR #453 (commit 1b28342, pushed onto the PR branch by the issue sweep):
.sops.yamlgains a path-scoped creation rule forsecrets/opendesign.sops.yaml(placed before the catch-all rule) listing both recipients viakey_groups— laptop-frameworkage125ac…0ttc34+ claude-sandboxage1nw32a…s85h43p. The existing catch-all rule forsecrets/sops/**is unchanged.secrets/opendesign.sops.yamlreformatted with prettier (the failing treefmt check): ciphertext untouched, indentation only.Verified locally (sops 3.13.2, sandbox age identity):
sops -dreturns byte-identical plaintext to the pre-format file (sha256 match), both recipients present.sops updatekeys --yeson a copy under the new config: "already up to date" — both recipients retained (pre-fix config rewrote the file to the laptop recipient only).secrets/sops/common/dummy.yamlgets the laptop recipient only; atsecrets/opendesign.sops.yamlgets both.dezren39 commentedon Oct 21, 2026
Merged: PR #453 merged as 6146abb with the fix in place. PR CI on fixed head 1b28342 was all-PASS (treefmt check, Nix flake evaluation, run, CodeRabbit). On merged main, secrets/opendesign.sops.yaml still carries both recipients and decrypts with the sandbox identity to the identical plaintext (sha256-verified against the pre-fix file). All acceptance criteria met.
Merge pull request #458 from developing-today/creator/457-roadmap-done