Skip to content

fix(ci): ratchet long-lived GCP SA key paths (#6800) - #11125

Merged
undivisible merged 9 commits into
BasedHardware:mainfrom
aryanorastar:fix/6800-gcp-sa-key-ratchet
Aug 25, 2026
Merged

fix(ci): ratchet long-lived GCP SA key paths (#6800)#11125
undivisible merged 9 commits into
BasedHardware:mainfrom
aryanorastar:fix/6800-gcp-sa-key-ratchet

Conversation

@aryanorastar

@aryanorastar aryanorastar commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

#6800 is a multi-phase Workload Identity / ADC migration. Live IAM cutover still needs platform prep (GKE WI bindings, GCS signed-URL IAM, Modal WIF, etc.).

This PR ships the repository-only first phase proposed on the issue: a CI ratchet that freezes known long-lived GCP service-account JSON / credentials_json surfaces so production paths cannot expand while cutover continues, plus local-dev docs that stop instructing developers to copy a key into the repo.

Implementation

  • .github/scripts/check_gcp_sa_key_ratchet.py — counts google-credentials.json, SERVICE_ACCOUNT_JSON, from_service_account_info, and credentials_json: hits on charts/deploy/modal/workflows/actions + critical backend load sites.
  • .github/scripts/gcp_sa_key_ratchet_baseline.json — shrink-only baseline (57 path:kind entries today).
  • Manifest checks: gcp-sa-key-ratchet + gcp-sa-key-ratchet-tests.
  • Backend_Setup.mdx — ADC / optional impersonation; drop “copy to ./google-credentials.json”.

Out of scope (follow-ups): agent-proxy chart WI cutover, GCS signBlob signed URLs, Modal OIDC→WIF, GitHub Actions WIF for deploy workflows, org policy to ban new SA keys.

Tests

  • python3 .github/scripts/test_check_gcp_sa_key_ratchet.py
  • python3 .github/scripts/check_gcp_sa_key_ratchet.py

Product invariants affected

none

Failure class (fixes)

Failure-Class: none

Review in cubic

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 5 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .github/scripts/check_gcp_sa_key_ratchet.py Outdated
Comment thread .github/scripts/check_gcp_sa_key_ratchet.py Outdated
Comment thread docs/doc/developer/backend/Backend_Setup.mdx
Comment thread .github/scripts/check_gcp_sa_key_ratchet.py
@aryanorastar

Copy link
Copy Markdown
Contributor Author

Addressed all four cubic findings on the new tip:

P1 — quoted credentials_json: pattern now matches bare / single- / double-quoted keys so equivalent google-github-actions/auth inputs cannot slip past.

P1 — Helm templates: .tpl and .gotmpl added to scanned suffixes; new test asserts a template binding fails the gate.

P2 — GOOGLE_APPLICATION_CREDENTIALS: new tracked kind google-application-credentials-env. Baseline grew 57 → 71 path:kind entries (grandfathering existing chart/workflow/backend load sites, including the vendored prometheus CRD docstring hits).

P2 — docs: dropped the non-existent dev-readonly@… SA; guide now prefers plain ADC, marks impersonation as advanced/time-limited (~1h), and requires an org-provisioned SA plus roles/iam.serviceAccountTokenCreator.

python3 .github/scripts/test_check_gcp_sa_key_ratchet.py → 9 passed; live ratchet OK at 71 entries.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

Fixed Hygiene (diff-hygiene): gcp_sa_key_ratchet_baseline.json had a blank line at EOF from --print-counts printing json.dumps(...) + "\n" (print adds another newline). Trimmed the baseline and made --print-counts emit a single trailing newline.

@Git-on-my-level Git-on-my-level added workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior security-review Touches auth, provider routing, secrets, or security-sensitive surfaces needs-maintainer-review Needs a human maintainer to sign off before merge docs-tooling Layer: Documentation, examples, dev tools labels Aug 5, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for tightening this path. I reviewed the current head and the ratchet shape looks directionally right: it prevents expansion of long-lived GCP service-account JSON / credentials_json / GOOGLE_APPLICATION_CREDENTIALS usage across the production/workflow surfaces, refuses stale or raised baselines, and updates the backend setup docs away from copying ADC credentials into the repo.

I also ran the new checker tests and the live ratchet in a sparse checkout of this head:

  • python3 .github/scripts/test_check_gcp_sa_key_ratchet.py → 9 tests passed
  • python3 .github/scripts/check_gcp_sa_key_ratchet.py → OK, 71 baselined path/kind entries

I’m not seeing a concrete blocker from this pass. Because this touches security-sensitive credential handling and CI/workflow enforcement for the #6800 migration, this should still get human maintainer sign-off before merge, especially on whether the scanned surface and initial baseline are the right policy boundary.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

Reran the ratchet evidence scripts on the current worktree head (corresponding to #6800):

  • -> OK (9 tests)
  • OK: GCP SA key ratchet holds (71 baselined path:kind entries). -> OK, ratchet holds (71 baselined path:kind entries)

No additional blockers from my side; could you please click APPROVE to satisfy the merge gate (currently with only COMMENTED reviews)?

@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level need human response

Head @aab40e17 is fully green (Preflight + Desktop Swift + hermetic + Windows portability). Your yesterday pass found no code blocker and confirmed the ratchet locally (9 tests / 71 baseline entries).

Merge is still blocked solely on a write-access APPROVE (current reviews are COMMENTED only). Because this is a security/workflow ratchet for #6800, could a human maintainer APPROVE when you have a moment? No further author code work pending.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for continuing to tighten this. I found one ratchet correctness edge that should be fixed before merge:

  • .github/scripts/check_gcp_sa_key_ratchet.py: --write-baseline currently treats an existing empty baseline the same as a missing/bootstrap baseline because the guard is if previous:. After the migration eventually shrinks the baseline to {}, a later --write-baseline run could add brand-new path:kind entries back into that existing baseline instead of refusing them. That weakens the intended shrink-only property after the cutover is complete.

Could you distinguish “baseline file is missing” from “baseline exists but has zero entries”, and add a regression test where an existing empty baseline plus a new production key path makes --write-baseline fail?

I re-ran the current tests and live checker locally on this head:

  • python3 .github/scripts/test_check_gcp_sa_key_ratchet.py → 9 tests passed
  • python3 .github/scripts/check_gcp_sa_key_ratchet.py → OK, 71 baselined path/kind entries
  • python3 .github/scripts/check_gcp_sa_key_ratchet.py --print-counts → 71 entries

Because this is security/workflow guardrail code for credential handling, this still needs human maintainer sign-off after the edge case is fixed.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level addressed the empty-baseline shrink-only edge on head 59ecb8a98c.

Fix: --write-baseline now treats a missing baseline file as bootstrap, and an existing baseline (including {}) as shrink-only — new path:kind keys are refused even when the baseline has zero entries.

Tests:

  • test_write_baseline_refuses_new_keys_on_existing_empty_baseline
  • test_write_baseline_bootstraps_only_when_baseline_file_missing
  • python3 .github/scripts/test_check_gcp_sa_key_ratchet.py11 passed
  • live ratchet still OK at 71 baselined path:kind entries

Ready for re-review / write-access APPROVE when you have a moment.

aryanorastar and others added 6 commits August 6, 2026 16:27
Block new production google-credentials.json / SERVICE_ACCOUNT_JSON /
credentials_json bindings while Workload Identity cutover proceeds, and
steer local setup to ADC instead of copying a key into the repo.

Failure-Class: none
Co-authored-by: Cursor <cursoragent@cursor.com>
Failure-Class: none
Co-authored-by: Cursor <cursoragent@cursor.com>
Match quoted credentials_json action inputs, scan Helm .tpl/.gotmpl,
track GOOGLE_APPLICATION_CREDENTIALS (rebaseline +14 path:kind entries),
and replace the fictional dev-readonly impersonation SA with an explicit
placeholder plus TokenCreator / ~1h expiry notes.

Failure-Class: none
Co-authored-by: Cursor <cursoragent@cursor.com>
diff-hygiene rejects a blank line at EOF; --print-counts was appending
an extra newline on top of json.dumps(...)+"\\n".

Failure-Class: none
Co-authored-by: Cursor <cursoragent@cursor.com>
Distinguish a missing baseline file (bootstrap) from an existing empty
baseline so --write-baseline cannot re-grow path:kind entries after cutover.

Co-authored-by: Cursor <cursoragent@cursor.com>
…Hardware#6800)

origin/main added a second SERVICE_ACCOUNT_JSON token in
desktop_backend_auto_dev.yml (--remove-env-vars cleanup). Raise that
path:kind cap 1→2 so the ratchet still freezes current main.

Co-authored-by: Cursor <cursoragent@cursor.com>
@aryanorastar
aryanorastar force-pushed the fix/6800-gcp-sa-key-ratchet branch from 59ecb8a to eb0c263 Compare August 6, 2026 10:58
@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level follow-up on head eb0c263d63:

  1. Empty-baseline shrink-only fix from earlier still present.
  2. Rebased onto current main. Hygiene failed because main added a second SERVICE_ACCOUNT_JSON token in .github/workflows/desktop_backend_auto_dev.yml (inside --remove-env-vars=...). Raised that path:kind baseline 1→2 so the ratchet still freezes current main.
  3. python3 .github/scripts/test_check_gcp_sa_key_ratchet.py → 11 passed; live ratchet OK.

Ready for re-review / write-access APPROVE.

@Git-on-my-level
Git-on-my-level dismissed their stale review August 7, 2026 10:05

Resolved on current head eb0c263: --write-baseline now distinguishes a missing baseline from an existing empty shrink-only baseline, with regression coverage.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the follow-up. I re-reviewed head eb0c263d63f8a04fc107d91bf700695b216ad830 and the earlier --write-baseline blocker is resolved: an existing empty baseline is now treated as shrink-only, while a missing baseline is the only bootstrap case, and the regression tests cover both paths.

I also re-ran the focused evidence in a stripped environment:

  • python3 .github/scripts/test_check_gcp_sa_key_ratchet.py → 11 tests passed
  • python3 .github/scripts/check_gcp_sa_key_ratchet.py → OK, 71 baselined path/kind entries
  • --print-counts confirms 71 entries, including the current-main desktop_backend_auto_dev.yml SERVICE_ACCOUNT_JSON count at 2

The ratchet shape still looks sound to me: it covers the intended production/workflow credential surfaces, catches quoted credentials_json, GOOGLE_APPLICATION_CREDENTIALS, Helm template hits, stale baseline entries, raised counts, and empty-baseline re-growth. The backend setup docs also now steer local development toward ADC instead of copying long-lived service-account JSON into the repo.

I’m dismissing my stale change request because the specific blocker is fixed. This still touches security-sensitive credential handling and CI/workflow enforcement for the #6800 migration, so it should remain under human maintainer sign-off before merge, especially for the final policy boundary and initial baseline.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level final approval reminder: PR #11125 is fully CI-green at eb0c263. Your stale changes-request is resolved and dismissed; focused evidence passes with 11 ratchet tests plus the live 71-entry baseline check. The remaining gate is security/workflow maintainer approval for the #6800 ratchet boundary.

Keep GCP SA key ratchet tip current for security/workflow CO.
@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level need human response — formal security/workflow APPROVE is the only merge blocker on #11125.

Author-clear:

Please human / security-review + workflow-review APPROVE (or explicit hold on the policy boundary) when you can.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level need human response — formal security/workflow APPROVE is still the only merge blocker on #11125.

Author-clear on tip fbb4b00d4635 (refreshed onto current main; prior tip was ~63 behind):

CI re-running on the main merge. Please human / security-review + workflow-review APPROVE (or explicit hold on the policy boundary) when you can.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for keeping this current with main. I re-reviewed the current head (fbb4b00d46357154fe5559feadb45be346a0e6a8) and I’m still not seeing a code blocker in the #6800 phase-1 ratchet.

File-specific notes:

  • .github/checks-manifest.yaml registers both the live gcp-sa-key-ratchet check and its test check in local/CI lanes, and the trigger list covers the production/workflow surfaces that can carry long-lived GCP key bindings.
  • .github/scripts/check_gcp_sa_key_ratchet.py keeps the scan intentionally scoped to deploy/runtime/workflow surfaces, detects the relevant key-path patterns, fails both count increases and stale baselines, and now distinguishes a missing baseline bootstrap from an existing empty baseline that must remain shrink-only.
  • .github/scripts/gcp_sa_key_ratchet_baseline.json is a path/kind/count baseline rather than key material; I verified it matches the current tree at 71 entries.
  • .github/scripts/test_check_gcp_sa_key_ratchet.py covers docs/tests being out of scope, production surfaces being in scope, quoted credentials_json, GOOGLE_APPLICATION_CREDENTIALS, new chart/template key paths, required baseline shrink, refusal to raise counts, refusal to add keys to an existing empty baseline, missing-file bootstrap, and the live repo baseline.
  • docs/doc/developer/backend/Backend_Setup.mdx removes the old “copy ADC credentials into ./google-credentials.json” flow and now points local development toward ADC / optional short-lived impersonation, with pusher docs preferring SERVICE_ACCOUNT_JSON unset.

Validation run in a stripped environment:

  • python3 .github/scripts/test_check_gcp_sa_key_ratchet.py → 11 tests passed
  • python3 .github/scripts/check_gcp_sa_key_ratchet.py → OK, 71 baselined path/kind entries

I can’t formally approve this from automation because this is a security/workflow-sensitive credential ratchet and the PR is part of the #6800 Workload Identity migration sequence. Leaving this for human maintainer sign-off on the security/workflow boundary before merge.


by AI on behalf of David — human maintainer sign-off is specifically needed for this GCP credential-ratchet security/workflow boundary before merge.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level need human response — formal security/workflow APPROVE is still the only merge blocker on #11125.

Author-clear on tip 5d64b630130c (refreshed onto current main):

Please human / security-review + workflow-review APPROVE (or explicit hold on the policy boundary / initial baseline) when you can.

@undivisible undivisible added the human Human-authored pull request label Aug 10, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for keeping this rebased. I reviewed the current head (5d64b630130c7fa16405adaf0f37593e5bb8ae49) and I’m still not seeing a code blocker in the #6800 phase-1 ratchet.

File-specific notes:

  • .github/checks-manifest.yaml wires both gcp-sa-key-ratchet and gcp-sa-key-ratchet-tests into local/CI lanes, and its trigger set covers the workflow/action, backend runtime, chart/deploy, and checker/baseline surfaces that can change the long-lived GCP key boundary.
  • .github/scripts/check_gcp_sa_key_ratchet.py is scoped to production/deploy surfaces, tracks the important key shapes (google-credentials*.json, SERVICE_ACCOUNT_JSON, GOOGLE_APPLICATION_CREDENTIALS, from_service_account_info, and quoted/bare credentials_json:), fails on both count raises and stale baselines, and keeps --write-baseline shrink-only for any existing baseline including {}.
  • .github/scripts/gcp_sa_key_ratchet_baseline.json matches the current tree’s 71 grandfathered path:kind entries; the note clearly frames additions as security-review events rather than normal maintenance.
  • .github/scripts/test_check_gcp_sa_key_ratchet.py covers the main failure modes I would want for this guard: docs/tests out of scope, prod/chart/workflow surfaces in scope, quoted credentials_json, GOOGLE_APPLICATION_CREDENTIALS, new chart/template paths, stale shrink requirements, count raises, and the existing-empty-baseline edge.
  • docs/doc/developer/backend/Backend_Setup.mdx removes the old “copy ADC credentials into ./google-credentials.json” instruction and now directs local developers toward ADC, with scoped compatibility wording for Modal/CI and GOOGLE_APPLICATION_CREDENTIALS.

Validation I ran in a stripped environment:

  • python3 .github/scripts/test_check_gcp_sa_key_ratchet.py → 11 tests passed
  • python3 .github/scripts/check_gcp_sa_key_ratchet.py → OK, 71 baselined entries
  • --print-counts output matches .github/scripts/gcp_sa_key_ratchet_baseline.json

I’m leaving this for human maintainer sign-off because it deliberately touches security/workflow guardrails for the #6800 credentials migration and the PR is not eligible for formal automated approval.

— Reviewed by gpt-5.5 maintainer automation


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level final merge-gate ping for #11125. Current head 5d64b63013 is MERGEABLE and all required checks are green; your latest review found no PR-specific blocker and confirmed 11 ratchet tests plus the live 71-entry baseline. The only remaining gate is formal human security/workflow approval for the #6800 phase-1 policy boundary. Please approve when ready, or state an explicit hold/blocker.

@aryanorastar

aryanorastar commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

@Git-on-my-level @undivisible review ping — this one is ready and has been waiting 4 days.

MERGEABLE, no failing checks on the current head. It ratchets long-lived GCP SA key paths (#6800).

Blocked only on workflow CODEOWNER approval.github/workflows/** requires your sign-off per CODEOWNERS, so I can't move it myself.


Still current and still author-side clear. Verified against main on 2026-08-23: not superseded, still MERGEABLE, all checks green, no conflicts. Blocker is the CODEOWNERS workflow-review routing. Grouped with six sibling CI PRs in one consolidated ping at #10959 rather than bumping each thread separately.

@undivisible
undivisible merged commit 63c9d57 into BasedHardware:main Aug 25, 2026
30 checks passed
undivisible pushed a commit that referenced this pull request Aug 25, 2026
…12183)

Reverts #11125. The ratchet shipped with two defects that compound, and the
first hid the second (see #12182).

It declared backend/agent-proxy/main.py and backend/agent-proxy/Dockerfile as
trigger paths and neither exists. run_checks.py:187 existence-checks any
trigger without glob magic and fails the whole manifest resolution when one is
missing, before any check runs -- so this broke every consumer of the manifest,
not the ratchet alone. #12181 touches three desktop Swift files and no backend,
and lost Detect Desktop Swift Changes, Desktop Swift Build & Tests, Hygiene and
PR Metadata Preflight to it.

Making resolution succeed only moves the failure: the ratchet then runs for the
first time and does not pass its own baseline, on pristine main, with 13
findings. Twelve are stale-shrink entries -- several for backend/agent-proxy/*,
the same paths that do not exist. One is not:
backend/database/google_credentials.py:service-account-json-env reports found 6
against a cap of 1, on a file main has not modified, so the cap was miscounted
when written rather than something having regressed. --write-baseline never
raises caps by design, so that one cannot be corrected with the tool; it needs
either real key-path removal or a baseline edit under security review.

Reverting costs nothing that currently works: the check has never executed, so
no key path is being protected by it today. It can be re-landed with a baseline
generated against this tree and backend/agent-proxy/** as the trigger.

Deliberately kept: the Backend_Setup.mdx change, restored verbatim. It steers
local setup to ADC instead of copying application_default_credentials.json into
the repo as google-credentials.json. That improvement has no CI dependency, and
reverting it would put the copy-a-key-into-the-repo instructions back.

Failure-Class: none
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs-tooling Layer: Documentation, examples, dev tools human Human-authored pull request needs-maintainer-review Needs a human maintainer to sign off before merge security-review Touches auth, provider routing, secrets, or security-sensitive surfaces workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants