# 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:` 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.