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

96 lines
4.1 KiB
Markdown

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