Skip to content

feat(secrets): stop retaining Secret values in the control plane#208

Merged
sunib merged 3 commits into
mainfrom
feat/secret-value-retention
Jul 8, 2026
Merged

feat(secrets): stop retaining Secret values in the control plane#208
sunib merged 3 commits into
mainfrom
feat/secret-value-retention

Conversation

@sunib

@sunib sunib commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements Track A of the secret-handling roadmap
(docs/future/secret-handling-roadmap.md) — the
process- and crypto-level security wins for #205,
ahead of the separate RBAC-narrowing track. The controller no longer keeps cluster Secret
values in memory, no longer writes private age keys to disk, and reacts to credential/age-key
changes on one 5-minute reconcile instead of a Secret informer.

What changed

  • No Secret retention / watches. Removed the GitTarget control-plane Secret watch and set
    Client.Cache.DisableFor: corev1.Secret, so typed Secret reads go straight to the API and
    nothing caches Secret values. GitProvider stays pull-based.
  • Public-recipient-only SOPS. Dropped private age identities from the encrypt path — no
    SOPS_AGE_KEY_FILE, no identity temp file, no blanket secret-data env. Recipients still
    reach sops via the target .sops.yaml, so encrypted output is unchanged.
  • Unified reconcile. Collapsed the 2 / 5 / 10-minute requeue intervals into one 5-minute
    RequeueSteadyInterval for the four control-plane reconcilers.
  • Credentials read once per push cycle. The branch worker resolved the credentials Secret
    on every commit but only used it on the first (the remote fetch); it now resolves lazily.
    No cache/memo is introduced.

Out of scope

RBAC narrowing (Track B). This is a runtime change: the default install still grants
secrets get;list;watch plus the dynamic-watch wildcard until the RBAC-narrowing track lands.
security-model.md and design/reconcile-triggering.md call that distinction out.

Validation

task lint ✅ · task test ✅ (coverage ratchet holds) · task test-e2e ✅ (55 passed / 0
failed), including the SOPS/age specs that exercise the recipients-from-.sops.yaml path
end-to-end.

Summary by CodeRabbit

  • New Features

    • Secret values are now read directly when needed instead of being kept in shared cache memory.
    • Reconciliation now uses a single 5-minute steady retry interval across controllers.
  • Bug Fixes

    • Credential and age-key updates are picked up more reliably during periodic reconciliations.
    • Encryption handling no longer passes private age material through the write path.
    • Repeated commits in a push cycle now avoid redundant credential reads.
  • Documentation

    • Updated security and design docs to reflect the new secret-handling and reconcile behavior.

…cret watches

Implements Track A, steps 1-3 of the secret-handling roadmap
(docs/future/secret-handling-roadmap.md) - the immediate process- and
crypto-level security wins, ahead of the separate RBAC-narrowing track.

Step 1 - stop caching Secret values, drop control-plane Secret watches:
- Remove the GitTarget control-plane Secret watch (and its now-orphaned
  encryptionSecretToGitTargets map func). A full-object Secret watch made the
  controller-runtime cache retain every Secret value in every namespace.
- Disable the typed-Secret cache on the manager (Client.Cache.DisableFor
  corev1.Secret), so typed Secret reads go straight to the API server and stay
  fresh. GitProvider stays pull-based (no auth-Secret watch, by design).

Step 2 - remove private age identities from the SOPS encrypt path:
- The write path only encrypts, so it needs public age recipients only.
- Drop AgeIdentities/Environment from ResolvedEncryptionConfig; delete the age
  identity temp-file path / SOPS_AGE_KEY_FILE; stop passing blanket Secret data
  into the sops process env. Recipients still reach sops via the target
  .sops.yaml, so encryption output is unchanged. A BYO age-key Secret is still
  read to derive its public recipient; the private identity is never returned,
  written to disk, or handed to sops.

Step 3 - unify the control-plane reconcile fallback at 5 minutes:
- Collapse the short (2m) / medium (5m) / long (10m) requeue intervals into a
  single RequeueSteadyInterval (5m) for GitProvider, GitTarget, WatchRule, and
  ClusterWatchRule. Genuine spec changes still trigger immediately via watches;
  this is only the periodic fallback that picks up out-of-band credential and
  age-key changes now that Secrets are not watched.

Docs: security-model.md and design/reconcile-triggering.md updated to the
direct-read model (behavior change, not yet an RBAC change - the default install
still grants secrets get;list;watch until the RBAC-narrowing track lands); the
gitpathaccepted-projection-race design doc gets a dated note for the removed
Secret watch and unified interval. Adds the roadmap and two plan docs under
docs/future/.

RBAC narrowing (Track B) is intentionally out of scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR unifies controller requeue timing around a single 5-minute steady interval, removes Secret watch/caching paths from controller runtime behavior, refactors age encryption to use recipients only, and updates design documents to match the new runtime and future RBAC plans.

Changes

Requeue interval unification and Secret retention removal

Layer / File(s) Summary
Steady requeue constant
internal/controller/constants.go, internal/controller/dependency_watches_test.go
Replaces the split requeue constants with RequeueSteadyInterval and adds a test that checks it equals 5 minutes.
Manager Secret cache disablement
cmd/main.go
Configures the manager client to disable caching for corev1.Secret objects.
GitTarget controller requeue and Secret watch removal
internal/controller/gittarget_controller.go
GitTarget reconcile paths switch to the steady interval, and the Secret watch plus encryption-secret enqueue helper are removed.
GitProvider controller requeue unification
internal/controller/gitprovider_controller.go, internal/controller/gitprovider_controller_test.go
GitProvider status-update paths now always use the steady interval, and the tests expect the updated cadence.
WatchRule and ClusterWatchRule requeue unification
internal/controller/watchrule_controller.go, internal/controller/clusterwatchrule_controller.go
Both controllers drop caller-provided requeue delays and return the steady interval for ready and failure paths.
Age encryption recipients-only refactor
internal/git/encryption.go, internal/git/encryption_test.go
Encryption resolution now returns recipients only, stops carrying private age material through the SOPS write path, and updates tests to match.
Comment update
internal/watch/manager.go
Updates the GitPath acceptance comment to reference the new steady interval.

Estimated code review effort: 4 (Complex) | ~60 minutes

Design and roadmap documentation

Layer / File(s) Summary
Existing design doc revisions
docs/design/manifest/gitpathaccepted-projection-race-and-external-drift.md, docs/design/reconcile-triggering.md, docs/security-model.md
Updates prior docs to reflect the removed Secret watch, unified 5-minute cadence, and direct Secret-read behavior.
New future-plan documents
docs/future/secret-handling-roadmap.md, docs/future/secret-value-retention-plan.md, docs/future/scoped-rbac-least-privilege-plan.md
Adds roadmap documents for Secret handling, Secret-value retention, and scoped least-privilege RBAC.

Estimated code review effort: 2 (Simple) | ~15 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the change and validation, but it omits required template sections like Type of Change and Checklist. Add the template headings for Type of Change, Checklist, Screenshots, and Additional Notes, and fill in the Related Issues section explicitly.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change: stopping Secret value retention in the control plane.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/secret-value-retention

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/future/secret-value-retention-plan.md`:
- Around line 29-31: Update the “Current problem” wording to match the new
Secret cache exception introduced by Client.Cache.DisableFor for corev1.Secret.
In the secret-value-retention-plan document, revise the manager/cache baseline
text so it no longer says the manager is simply created without cache options;
make it clear that the shared cache still applies generally, but Secrets are now
explicitly excluded, and keep the distinction between the existing state and
what Track A changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 14b58067-3923-4abf-8f21-bbee92c19a97

📥 Commits

Reviewing files that changed from the base of the PR and between e5722a7 and b55ef89.

📒 Files selected for processing (17)
  • cmd/main.go
  • docs/design/manifest/gitpathaccepted-projection-race-and-external-drift.md
  • docs/design/reconcile-triggering.md
  • docs/future/scoped-rbac-least-privilege-plan.md
  • docs/future/secret-handling-roadmap.md
  • docs/future/secret-value-retention-plan.md
  • docs/security-model.md
  • internal/controller/clusterwatchrule_controller.go
  • internal/controller/constants.go
  • internal/controller/dependency_watches_test.go
  • internal/controller/gitprovider_controller.go
  • internal/controller/gitprovider_controller_test.go
  • internal/controller/gittarget_controller.go
  • internal/controller/watchrule_controller.go
  • internal/git/encryption.go
  • internal/git/encryption_test.go
  • internal/watch/manager.go

Comment thread docs/future/secret-value-retention-plan.md
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

sunib and others added 2 commits July 7, 2026 21:01
…ommit

Follow-up to the secret-value-retention change (plan step 5). With the Secret
cache disabled, every typed Secret read is a real API GET, so redundant reads now
cost etcd round-trips.

commitPendingWrites resolved the credentials Secret unconditionally but only used
it on the first commit of a push cycle (the branch that runs PrepareBranch to
fetch the remote tip). Every subsequent commit in a burst read the credentials
Secret - plus its known_hosts lookups - and discarded it. Resolve auth lazily,
only inside the !hasPendingCommits branch that actually touches the remote,
mirroring ensureRepositoryInitialized.

No memo/cache is introduced (the plan cautions against that - it would reintroduce
stale-credential behavior): pushPendingCommits and the first commit still resolve
directly, once per operation, reflecting current credentials. This only removes
the reads that were never used.

Adds a unit test that counts credentials-Secret GETs across a two-commit cycle:
1 on the first commit, 0 on the second.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the three secret-handling docs to the current state: Track A (retention +
crypto exposure) shipped in this PR; Track B (RBAC narrowing) not started. Marks
the retention-plan "Done" checklist complete and annotates the roadmap's
recommended order, keeping the runtime-vs-RBAC distinction explicit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sunib sunib changed the title feat(secrets): stop retaining Secret values (roadmap Track A, steps 1-3) feat(secrets): stop retaining Secret values in the control plane Jul 8, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/future/secret-value-retention-plan.md`:
- Around line 3-20: The blockquote in secret-value-retention-plan.md contains an
empty quoted line that triggers markdownlint MD028. Update the quoted section
near the Status/Scope text so there is no blank line inside the blockquote, or
make the blank line an explicit quoted line using the same blockquote marker.
Keep the fix localized to the roadmap note content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 18d7661a-8b60-4b38-9c36-8de072a7986d

📥 Commits

Reviewing files that changed from the base of the PR and between b55ef89 and 7302552.

📒 Files selected for processing (5)
  • docs/future/scoped-rbac-least-privilege-plan.md
  • docs/future/secret-handling-roadmap.md
  • docs/future/secret-value-retention-plan.md
  • internal/git/branch_worker.go
  • internal/git/branch_worker_credread_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/future/scoped-rbac-least-privilege-plan.md

Comment thread docs/future/secret-value-retention-plan.md
@sunib
sunib merged commit 535c5ed into main Jul 8, 2026
17 checks passed
@sunib
sunib deleted the feat/secret-value-retention branch July 8, 2026 05:11
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