Skip to content

watch credential secrets and re-issue when they disappear - #5

Open
vramperez wants to merge 1 commit into
mainfrom
feat/self-healing-credential-secrets
Open

watch credential secrets and re-issue when they disappear#5
vramperez wants to merge 1 commit into
mainfrom
feat/self-healing-credential-secrets

Conversation

@vramperez

Copy link
Copy Markdown

The reconciler's fast path skipped any request whose Ready condition was True and whose nextRenewalTime was still in the future, consulting only the status and never the storage backend. The controller also watched nothing but the VerifiableCredentialRequest itself. A credential Secret deleted out-of-band therefore stayed missing until the next scheduled renewal -- typically hours or days -- leaving the consuming service without a credential for that whole window.

Watch the credential Secrets the controller owns, and make the renewal gate consult the CredentialStore before skipping.

Retrieve now reports an absent credential as credentialstore.ErrNotFound, which the reconciler treats as "re-issue now" regardless of the renewal schedule. Mapping the existing "Secret has no credential key" branch onto the same sentinel makes an emptied Secret self-heal like a deleted one. Any other error is assumed to mean the credential is present: a successful reconciliation resets the workqueue rate limiter, so treating transient backend failures as credential loss would let a flaky backend drive an unthrottled re-issuance storm against the issuer.

The watch uses Owns, since credential Secrets already carry an owner reference with Controller: true, and watch on Secrets was already granted. A predicate drops the operator's own writes, which would otherwise enqueue a redundant reconciliation after every issuance plus one per managed Secret on each informer resync.

A restore reuses the renewal accounting path, so it increments renewalCount and credentials_renewed_total; the StoredCredentialMissing warning event is what distinguishes it from a scheduled renewal.

The reconciler's fast path skipped any request whose Ready condition was
True and whose nextRenewalTime was still in the future, consulting only the
status and never the storage backend. The controller also watched nothing
but the VerifiableCredentialRequest itself. A credential Secret deleted
out-of-band therefore stayed missing until the next scheduled renewal --
typically hours or days -- leaving the consuming service without a
credential for that whole window.

Watch the credential Secrets the controller owns, and make the renewal gate
consult the CredentialStore before skipping.

Retrieve now reports an absent credential as credentialstore.ErrNotFound,
which the reconciler treats as "re-issue now" regardless of the renewal
schedule. Mapping the existing "Secret has no credential key" branch onto
the same sentinel makes an emptied Secret self-heal like a deleted one.
Any other error is assumed to mean the credential is present: a successful
reconciliation resets the workqueue rate limiter, so treating transient
backend failures as credential loss would let a flaky backend drive an
unthrottled re-issuance storm against the issuer.

The watch uses Owns, since credential Secrets already carry an owner
reference with Controller: true, and watch on Secrets was already granted.
A predicate drops the operator's own writes, which would otherwise enqueue
a redundant reconciliation after every issuance plus one per managed Secret
on each informer resync.

A restore reuses the renewal accounting path, so it increments renewalCount
and credentials_renewed_total; the StoredCredentialMissing warning event is
what distinguishes it from a scheduled renewal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vramperez
vramperez requested review from Mortega5 and wistefan August 7, 2026 08:12
@vramperez vramperez added the minor label Aug 7, 2026
// credential is no longer present in the storage backend (for example,
// the target Secret was deleted or emptied out-of-band), which triggers
// an immediate re-issuance.
ReasonStoredCredentialMissing = "StoredCredentialMissing"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants