Skip to content

fix(watchsync): persist per-connection plugin config for later RPCs - #718

Closed
cursor[bot] wants to merge 3 commits into
mainfrom
cursor/critical-bug-management-035f
Closed

fix(watchsync): persist per-connection plugin config for later RPCs#718
cursor[bot] wants to merge 3 commits into
mainfrom
cursor/critical-bug-management-035f

Conversation

@cursor

@cursor cursor Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Problem

Related issue: N/A — regression in #550

Connecting a plugin watch provider with a profile-specific server URL or secret only applied those values to ExchangeAPIKey. Scheduled sync, token refresh, scrobble, and export then sent the installation-wide ProviderConfig.

The initial patch persisted that per-connection overlay, but review found follow-on hazards:

  • authenticated plugin faults could echo transmitted config or credential secrets into plaintext errors, logs, and API-visible status;
  • in-flight RPCs and stale replicas could overwrite a newer reconnect's credentials, account identity, overlay, or diagnostic state;
  • reconnecting a provider without config could retain an obsolete overlay.

Approach

  • Store the flattened per-connection overlay in the existing encrypted plugin_credentials bundle and merge it over installation config for every authenticated plugin RPC.
  • Build redaction candidates from the exact authenticated request plus returned credentials. Include nested scalar leaves from structured secrets, preserve exact JSON number lexemes, redact overlapping values longest-first, and discard diagnostics containing ambiguous short secrets.
  • Add credential_revision as a credential/config-specific concurrency fence. Plugin-returned rotations update only the encrypted credential fields with a database compare-and-swap; connect/reconnect advances the revision and replaces the complete overlay, including clearing obsolete values.
  • Preserve provider account identity and credentials during routine state upserts. Persist authoritative refresh diagnostics separately with both diagnostic-value and credential-revision preconditions so concurrent errors and reconnects win.
  • Add database triggers that reject plugin credential/account updates without a revision advance and reject revision-zero plugin inserts. This fences both halves of pre-upgrade replicas' upserts during a rolling deployment.

Connections created before this fix keep using installation-wide config until they are reconnected.

Validation

Passed locally:

make embed-stub
go build ./...
gofmt -l .
go vet ./...
golangci-lint run --new-from-merge-base="origin/main" ./...  # 0 issues
go test ./internal/watchsync/...
go test -race ./internal/watchsync -run 'TestServicePluginRefresh|TestPersistConnectionStoresPluginConfigOverlay|TestPersistUpdatedCredentials' -count=20
make migrate-validate
pnpm install --frozen-lockfile
pnpm run lint                 # exit 0; inherited warnings only
pnpm run format:check
pnpm run build
make test-web                 # 292 files, 2154 tests
make verify-settings-bindings-all
make verify-playback-fixtures
make verify-local-paths

A fresh PostgreSQL 18 database passed all migrations, the database-backed reconnect/rotation/legacy-writer test, and an explicit down/up cycle for 20260826032121_add_watch_provider_credential_revision.sql. The test covers stale rotation, account identity, concurrent diagnostics, old update fencing, and delete-then-old-insert fencing.

make test-go reached the full tree. Every changed watchsync package passed; two unrelated, environment-reproducible process-identity tests in internal/jellycompat failed on this Mac.

Risks

  • The migration adds one non-null bigint, a small trigger function, and two triggers. Existing plugin rows start at revision 0 and advance on their next reconnect or credential rotation.
  • During a rolling deployment, pre-upgrade replicas receive SQLSTATE 40001 if they try to insert a plugin connection or rewrite plugin account/credential fields. This intentionally favors credential correctness over transient old-replica availability.
  • Exact-value redaction cannot recognize arbitrary encodings or hashes produced by a plugin. Transport errors remain generic, and short ambiguous matches discard the plugin diagnostic entirely.
  • Existing connections made against feat(watchsync): add per-connection plugin config #550 still need reconnect because their overlay was never stored.

AI Disclosure

  • Tool(s): Cursor cloud agent; OpenAI Codex
  • Model(s): Cursor Grok 4.6; GPT-5.6
  • Involvement: Fully AI-generated; no human verification claimed
  • Adversarial review: independent read-only passes traced every authenticated RPC fault/result sink and challenged the rolling-upgrade, reconnect, credential-rotation, account-identity, diagnostic, and overlay-clear races. The final pass found the legacy insert path; it was fenced and covered by a PostgreSQL regression test before publication.

Checklist

  • I read and can explain the complete diff.
  • This pull request addresses one concern.
Open in Web View Automation 

cursoragent and others added 2 commits August 25, 2026 22:57
ExchangeAPIKey merged a profile's server URL and secrets into ProviderConfig,
then dropped them. Later sync, refresh, scrobble, and export used only the
installation-wide config, so a household member's personal server was ignored
after connect. Store the overlay in the encrypted plugin credential bundle and
merge it on every authenticated plugin RPC.

Co-authored-by: Quick <Quick104@users.noreply.github.com>
@Quick104
Quick104 force-pushed the cursor/critical-bug-management-035f branch from 50b286a to 6b82ca2 Compare August 26, 2026 03:28
@Quick104
Quick104 marked this pull request as ready for review August 26, 2026 13:38

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b82ca211a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/watchsync/repository.go
Comment thread internal/watchsync/repository.go Outdated
Comment thread internal/watchsync/service.go Outdated
Comment thread internal/watchsync/repository.go
@Quick104 Quick104 closed this Aug 28, 2026
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