Skip to content

fix(v2): reject stale identity snapshots after completed mutations - #888

Merged
seakee merged 1 commit into
seakee:v2from
camy-x:fix/v2-reconcile-stale-snapshot
Sep 29, 2026
Merged

seakee merged 1 commit into
seakee:v2from
camy-x:fix/v2-reconcile-stale-snapshot

Conversation

@camy-x

@camy-x camy-x commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Summary

An in-flight passive inventory can overwrite a completed API-key rotation/deletion or credential deletion after its pending intent is removed. Reproduced on v2 at f5d12c6ca4af1f36f087e8acd28e565c5a0ae655: the old source gets a new local identity, and a rotated identity becomes missing.

Capture the existing source-binding revision before fetching inventory and check it inside the reconciliation writer transaction. A conflicting snapshot is discarded; the next fresh capture reconciles normally.

Scope

  • Manager Server

Changes

  • Fence remote inventory against completed local source-binding changes independently of Supervisor generation, which does not change for these mutations.
  • Check the captured revision before pending-intent resolution or any snapshot writes; reuse the existing counter and triggers without a schema migration.
  • Add deterministic SQLite regressions for completed rotation, API-key deletion and credential deletion during inventory capture, atomic rejection, fresh retry, and revision-read failure/cancellation.

User Impact

Prevents obsolete inventory responses from recreating retired local identities or temporarily marking a successfully rotated identity missing. The bug affects Manager identity records; it does not restore deleted keys or files in CPA.

Compatibility / Runtime Notes

  • CPA panel mode: unchanged.
  • Manager Server: the Embedded passive identity worker always supplies the captured revision. Existing pending-intent suppression and restart recovery remain in place.
  • Full Docker / native packages: no configuration or packaging changes. A conflict delays reconciliation until a fresh capture on the next worker iteration.

Data / Security Notes

Reuses gateway_source_binding_revision; no database migration, raw usage mutation, or new secret storage. Revision validation and snapshot writes share the existing SQLite immediate transaction. The counter is global, so an unrelated binding change may conservatively invalidate the whole capture. Locally constructed repository snapshots retain the existing optional-fence behavior; the remote reconciliation service always sets it, including revision zero.

Risk / Rollback

Risk level: Low

Rollback notes: revert this change; no persisted schema change. Under frequent binding changes, passive synchronization may wait for a capture with a stable revision.

Verification

  • Tests

On WSL Ubuntu/Linux amd64 with Go 1.24.0, GOFLAGS=-mod=readonly. Manager SQLite temporary files were placed in tmpfs; these results are not disk durability or performance claims.

go test -p 4 -count=1 ./...
go test -p 4 -race -count=1 ./internal/application/identityreconcile ./internal/adapters/sqlite/identitystore ./internal/application/identityprojection ./internal/adapters/sqlite/identityprojection ./internal/application/quotaevaluation ./internal/service/cpaupdate ./internal/service/runtime ./internal/repository/setting ./internal/service/bootstrap
go vet -p 4 ./...
git diff --check

All passed. The behavioral regression cases fail against the original v2 source (rotation, API-key delete, credential delete) and pass with this change. Existing helper/opt-in baseline skips remain. Docker E2E, real CPA integration, and native Windows/macOS tests were not run. No frontend changes.

Screenshots / Recordings

N/A — backend identity reconciliation only.

Docs

  • Not needed — explanation included below

Docs decision: internal correctness fix; no new user-facing operation or configuration. The storage port and reconciliation sequence document the new precondition.

Related

V2 Phase 3 passive identity reconciliation and mutation-intent recovery. No linked issue.

@camy-x
camy-x marked this pull request as ready for review September 29, 2026 06:35
@seakee
seakee merged commit 58076c6 into seakee:v2 Sep 29, 2026
14 checks passed
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.

2 participants