verstak-sync-server/docs/superpowers/plans/2026-07-10-sync-tenant-isol...

4.1 KiB

Sync Tenant Isolation Implementation Plan

For agentic workers: execute this plan task by task with focused tests before each implementation change.

Goal: prevent cross-user and cross-vault operation visibility while binding the persisted source device to the authenticated token.

Architecture: the server derives user, device, and vault scope from the bearer token. SQLite rows carry that scope; desktop sends the immutable vault ID only while creating a pairing. Existing unscoped devices and operations are migrated into a deterministic legacy scope.

Tech stack: Go, database/sql, SQLite, net/http, desktop Go sync client.

Task 1: Establish server behaviour tests

Files:

  • Modify: internal/server/server_test.go

  • Add helpers that create confirmed users and token-authenticated devices with a specified vault ID.

  • Add a failing test where separate users push and pull from the same vault ID; each pull must contain only its own operation and cursor.

  • Add a failing test where one user owns devices in two vaults; pulls must remain vault-local.

  • Add a failing test that sends another device's ID in push; assert the stored and returned operation uses the authenticated device ID.

  • Add a failing test for identical idempotency keys in different scopes.

  • Run: go test ./internal/server -run 'TestSync.*Isolation|TestSyncPush' and confirm the new assertions fail for the intended missing behaviour.

Task 2: Add idempotent SQLite scope migration

Files:

  • Modify: internal/server/schema.go

  • Modify: internal/server/server.go

  • Test: internal/server/server_test.go

  • Define vault_id on new devices and user_id/vault_id on new operations; define scoped tombstone and idempotency primary keys.

  • Add startup migration helpers that inspect columns, add compatible columns, backfill owner IDs, assign legacy:<user_id> to old scopes, and rebuild the two tables whose primary keys change.

  • Add a failing legacy-schema fixture test, then make it pass by opening the database through NewServer and asserting its operation has the expected owner and legacy scope.

  • Run: go test ./internal/server -run 'Test.*Migration|TestSync.*'.

Task 3: Apply authenticated scope to sync handlers

Files:

  • Modify: internal/server/middleware.go

  • Modify: internal/server/handlers_api.go

  • Test: internal/server/server_test.go

  • Extend authenticated device lookup to provide the effective vault scope; missing user ownership must not authorize sync operations.

  • Require vault_id when creating a new client pairing and store it with the device.

  • Make push use authenticated device/user/vault values for inserts, conflicts, revisions, tombstones, and idempotency lookup/storage.

  • Make pull filter operations and its reported cursor by authenticated user/vault.

  • Run the focused tests from Task 1 until green, then go test ./internal/server.

Task 4: Send the current vault ID while pairing

Files:

  • Modify: ../verstak-desktop/internal/core/sync/client.go

  • Modify: ../verstak-desktop/internal/core/sync/client_test.go

  • Modify: ../verstak-desktop/internal/api/app.go

  • Modify: ../verstak-desktop/internal/api/app_test.go

  • Add vault_id to the pair request and expose it in the pairing client method without changing push/pull wire compatibility.

  • Read the open vault metadata in syncConfigure; reject configuration if the vault ID is absent.

  • Add a failing client/API test that captures the pair request and asserts the persistent vault ID is sent.

  • Run: go test ./internal/core/sync ./internal/api.

Task 5: Document, verify, and publish

Files:

  • Modify: README.md

  • Modify: docs/superpowers/specs/2026-07-10-sync-tenant-isolation-design.md

  • Document that pairing is vault-bound and that sync cursors are scoped.

  • Run gofmt on all changed Go files.

  • Run go test ./... in both verstak-sync-server and verstak-desktop, then git diff --check in both repositories.

  • Commit and push the sync-server and desktop changes as coordinated security commits.