Skip to content

ci: detect drift between source SOPS YAMLs and embedded @gen/env payloads#18

Open
cooper (czxtm) wants to merge 1 commit intomainfrom
fix/secrets-codegen-drift-check
Open

ci: detect drift between source SOPS YAMLs and embedded @gen/env payloads#18
cooper (czxtm) wants to merge 1 commit intomainfrom
fix/secrets-codegen-drift-check

Conversation

@czxtm
Copy link
Copy Markdown
Contributor

Summary

Closes stackpanel-04d. Adds a GitHub Actions check that fails the build if anyone edits a SOPS source file (.stack/secrets/vars/*.sops.yaml, etc.) without re-running codegen and committing the regenerated embedded payloads under packages/gen/env/.

This is the bug class that ate ~half a day on PRs #15/#17/#18:

What this changes

A single new workflow (.github/workflows/secrets-codegen-check.yml):

  1. Triggers on PR/push touching any source-of-truth path — SOPS YAMLs, Nix env declarations, codegen implementation, or the generated outputs themselves.

  2. Enters the Nix devshell (which writes .stack/gen/codegen/env-manifest.json and runs codegen as part of the shell-hook).

  3. Re-runs stackpanel codegen build explicitly with the same SOPS_AGE_KEY the deploy workflows use — belt-and-braces guarantee the build was run with the right decrypt key.

  4. git diff --quiet against:

    • packages/gen/env/data/_envs/
    • packages/gen/env/src/runtime/generated-payloads/_envs/

    The diff is deliberately scoped to just the embedded runtime payloads. The typed env wrappers under src/<app>/ get cosmetic churn on every devshell entry and aren't on the deploy hot path.

  5. On drift: surfaces the file list inline and tells the contributor exactly what to run (nix develop --impure --command stackpanel codegen build) and what to commit.

Test plan

Verified locally inside the devshell on this branch:

  • Clean tree → check passes ("OK: embedded SOPS payloads are in sync").
  • Simulated sops set edit on cloudflare_api_token → check fails with the expected file list (packages/gen/env/data/_envs/deploy.sops.json, packages/gen/env/src/runtime/generated-payloads/_envs/deploy.ts).
  • Restoring source from HEAD → next codegen run produces no diff.

CI verification will happen automatically on this PR.

Notes

  • The check runs in parallel with the deploy workflows; it adds zero latency to a successful deploy.
  • Cachix (devenv + darkmatter) keeps the devshell entry fast; first-run cold cache is ~3-5 min, subsequent runs are seconds.
  • Future improvement (filed as a separate beads issue if needed): also wire this into the existing pre-commit git-hooks pipeline so contributors get the same feedback locally before pushing.

…oads

PR #15/#17/#18 chased an Unauthorized: Authentication error in CI for
roughly half a day. The proximate fix was a fresh Cloudflare token, but the
real bug was that rotating that token in shared.sops.yaml never propagated
into packages/gen/env/src/runtime/generated-payloads/_envs/deploy.ts, which
is what loaders.deploy() actually decrypts at runtime. Codegen only runs on
devshell entry; CI deploys never run codegen, so they happily shipped the
old cfat_KJ57… value into every Worker request.

This workflow closes the drift class with a hard CI gate:
- On PR/push touching any source-of-truth path
- enter the devshell and re-run stackpanel codegen build
- git diff --quiet against the embedded runtime payloads under
  packages/gen/env/data/_envs/ and
  packages/gen/env/src/runtime/generated-payloads/_envs/
- on drift, print the affected files + a remediation command

Verified locally that a simulated sops set edit triggers the failure and
that a clean tree passes.

Closes stackpanel-04d.
@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 29, 2026

PR Summary

Low Risk
Low risk: adds a new CI workflow and updates internal issue tracking, without changing runtime or secret-handling code paths. Main impact is potential CI noise/time if codegen is non-deterministic or Nix/caches are flaky.

Overview
Adds a new GitHub Actions workflow (secrets-codegen-check.yml) that re-enters the Nix devshell, re-runs stackpanel codegen build with the repo’s SOPS age key, and fails CI if the embedded runtime secret payloads under packages/gen/env/{data/_envs,src/runtime/generated-payloads/_envs} would change (preventing stale secrets from being deployed).

Updates .beads/issues.jsonl with new/updated tracking issues documenting the secret-drift class and related deploy blockers.

Reviewed by Cursor Bugbot for commit 9020ad6. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Drift check misses per-app encrypted runtime payloads
    • Updated the workflow drift-check target paths to diff the full packages/gen/env/data and packages/gen/env/src/runtime/generated-payloads trees so per-app encrypted payload drift is detected.

Create PR

Or push these changes by commenting:

@cursor push 102850f4d5
Preview (102850f4d5)
diff --git a/.github/workflows/secrets-codegen-check.yml b/.github/workflows/secrets-codegen-check.yml
--- a/.github/workflows/secrets-codegen-check.yml
+++ b/.github/workflows/secrets-codegen-check.yml
@@ -115,8 +115,8 @@
           # stackpanel-04d for the rationale: the ONLY drift class that broke
           # production was the embedded encrypted payload + its TS wrapper.
           target_paths=(
-            packages/gen/env/data/_envs
-            packages/gen/env/src/runtime/generated-payloads/_envs
+            packages/gen/env/data
+            packages/gen/env/src/runtime/generated-payloads
           )
 
           if git diff --quiet -- "${target_paths[@]}"; then

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 9020ad6. Configure here.

target_paths=(
packages/gen/env/data/_envs
packages/gen/env/src/runtime/generated-payloads/_envs
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drift check misses per-app encrypted runtime payloads

High Severity

The target_paths array only covers the _envs/ subdirectories, but the repository has per-app encrypted payloads under packages/gen/env/src/runtime/generated-payloads/api/, web/, docs/, stackpanel-go/ and companion data files under packages/gen/env/data/dev/, prod/, staging/. These are real encrypted runtime payloads loaded via loadGeneratedPayload() in registry.ts — not the "typed env wrappers under src/<app>/" that get cosmetic churn (those live at src/exports/). A SOPS source rotation affecting per-app secrets like POSTGRES_URL would leave per-app payloads stale while this check passes silently — the same bug class the workflow was built to prevent.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9020ad6. Configure here.

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