Skip to content

feat(sync): migrate cloud sync from Cloudflare Workers to PocketBase#148

Merged
vscarpenter merged 5 commits intomainfrom
feature/pocketbase-migration
Feb 28, 2026
Merged

feat(sync): migrate cloud sync from Cloudflare Workers to PocketBase#148
vscarpenter merged 5 commits intomainfrom
feature/pocketbase-migration

Conversation

@vscarpenter
Copy link
Owner

@vscarpenter vscarpenter commented Feb 28, 2026

Summary

  • Replace entire Cloudflare Workers backend (D1 + KV + R2) with self-hosted PocketBase at api.vinny.io
  • Drop E2E encryption — tasks stored as plaintext (user owns the server), removing ~500 lines of crypto code
  • PocketBase realtime SSE for instant cross-device sync, replacing manual push/pull cycles
  • Last-write-wins (LWW) with client_updated_at timestamps, replacing vector clock conflict resolution
  • Google + GitHub OAuth via PocketBase built-in auth, replacing custom OIDC with Google/Apple
  • Delete worker/ entirely — zero backend code to maintain (~95 files, ~2,500 lines removed)
  • MCP server updated to use PocketBase SDK instead of custom API client + encryption manager
  • All 1,280 tests pass, typecheck clean, lint clean

New modules

  • lib/sync/pocketbase-client.ts — PocketBase SDK singleton wrapper
  • lib/sync/pb-sync-engine.ts — Push/pull sync with LWW resolution
  • lib/sync/pb-realtime.ts — SSE subscription manager with echo filtering
  • lib/sync/pb-auth.ts — OAuth login/logout via PocketBase SDK
  • lib/sync/task-mapper.ts — camelCase ↔ snake_case field mapping

Sync UI bug fixes (latest)

  • Surface per-item push errorspushLocalChanges() now returns failedCount and lastError so partial failures show the actual PocketBase error instead of being silently swallowed
  • Fix stale closureuseSync.sync() returns PBSyncResult directly instead of reading from stale React state in the handleSync closure
  • Fix error auto-reset timing — errors persist 10s (was 3s) so users can read them; success still resets after 3s
  • Fix double toast — removed duplicate showSyncResultToast from sync-button since fullSync() already fires notifications via sonner
  • Add partial status — new PBSyncResult status for when some items sync but others fail

Impact

  • 184 files changed, +2,441 / -26,138 lines (net ~23,700 lines removed)
  • Version bumped to 6.9.2

Test plan

  • bun run test — 72/72 files, 1,280/1,280 tests pass
  • bun typecheck — 0 errors
  • bun lint — 0 errors
  • MCP server npx tsc --noEmit — 0 errors
  • Manual E2E: Google OAuth login via PocketBase
  • Manual E2E: Create task → verify in PocketBase admin dashboard
  • Manual E2E: Cross-device realtime sync via SSE
  • Manual E2E: Offline create → reconnect → verify sync
  • MCP server: Build and test with Claude Desktop config

🤖 Generated with Claude Code

vscarpenter and others added 5 commits February 27, 2026 23:41
…ketBase

Replace the entire Cloudflare Workers backend (D1 + KV + R2) with a self-hosted
PocketBase instance at api.vinny.io. This dramatically simplifies the sync
architecture:

- Drop E2E encryption — tasks stored as plaintext (user owns the server)
- Use PocketBase realtime SSE for instant cross-device sync
- Last-write-wins (LWW) replaces vector clock conflict resolution
- Google + GitHub OAuth via PocketBase built-in auth (replaces custom OIDC)
- Delete worker/ entirely — zero backend code to maintain

New modules: pocketbase-client, pb-sync-engine, pb-realtime, pb-auth, task-mapper
Deleted: ~95 files including worker/, crypto, vector-clock, old sync engine,
  OAuth handshake, token manager, queue optimizer, encryption UI

Net change: 79 files, +1,744 -4,211 lines (≈2,500 lines removed)

All 1,280 tests pass, typecheck clean, lint clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update all project documentation to reflect the migration from
Cloudflare Workers to PocketBase:

- README.md: Version 6.9.0, PocketBase sync features, updated MCP
  server docs, new tech stack, removed worker deployment commands
- SECURITY.md: Replace E2E encryption/zero-knowledge sections with
  self-hosted PocketBase model, update OAuth providers, CSP headers,
  MCP config, and audit results
- TECHNICAL.md: Database v13 migration entry, remove encryption
  passphrase dialog, update sync component list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ompatibility

- Fix OAuth by updating .env.local to point at production PocketBase URL
- Fix 404 errors by adding setup-pocketbase-collections.sh script for tasks collection
- Fix 429 rate limiting by batch-fetching remote task IDs and throttling push ops
- Fix 400 errors by using client_updated_at instead of system fields in sort/filter
- Fix import rejecting legacy exports by using .strip() schema for vectorClock compat
- Close settings dialog after successful import for better UX
- Update CLAUDE.md with PocketBase v0.23+ gotchas and dev notes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…c UI

- Surface per-item push errors: pushLocalChanges() now returns failedCount
  and lastError so partial failures show the actual PocketBase error message
  instead of being silently swallowed
- Fix stale closure: useSync.sync() returns PBSyncResult directly instead
  of reading from stale React state captured in the handleSync closure
- Fix error auto-reset timing: errors now persist 10s (was 3s) so users
  can actually read them; success still resets after 3s
- Fix double toast: removed duplicate showSyncResultToast from sync-button
  since fullSync() already fires notifications via sonner
- Add 'partial' status to PBSyncResult for when some items sync but
  others fail, with failedCount field for UI display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- [High] Preserve task metadata (notifications, time tracking) during MCP
  write operations instead of hardcoding defaults
- [High] Always trigger pull sync in background even when local queue is
  empty, so remote changes are fetched when SSE is unavailable
- [Medium] Escape interpolated values in PocketBase filter expressions to
  prevent potential injection
- [Medium] Enforce HTTPS for PocketBase URL in MCP config (localhost exempt)
- [Medium] Replace useState misuse with useEffect in ImportDialog so task
  count updates when fileContents prop changes
- [Low] Use client_updated_at instead of client_created_at for lastUpdated
  stat in getTaskStats
- [Low] Replace raw console.* calls with structured logger in retry-manager

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
D Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@vscarpenter vscarpenter merged commit 4b9083f into main Feb 28, 2026
1 of 3 checks passed
@vscarpenter vscarpenter deleted the feature/pocketbase-migration branch February 28, 2026 21:31
vscarpenter added a commit that referenced this pull request Mar 1, 2026
…igration

Remove dependencies, scripts, documentation, and code references left over
from the Cloudflare Workers and Supabase sync systems that were replaced by
PocketBase in #148.

- Remove wrangler devDependency and hono override from package.json
- Delete 8 stale files: Worker/sync/security docs, jwt.ts, scripts
- Update MCP server CLI, config, and tool schemas to remove encryption refs
- Rewrite architecture-diagrams.ts with PocketBase diagrams
- Update 5 documentation files to reflect PocketBase architecture
- Remove vectorClock from all test fixtures (schema v13 dropped it)

All 1280 tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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