diff --git a/docs/superpowers/plans/2026-06-29-browser-receiver-pairing.md b/docs/superpowers/plans/2026-06-29-browser-receiver-pairing.md new file mode 100644 index 0000000..cf6c75b --- /dev/null +++ b/docs/superpowers/plans/2026-06-29-browser-receiver-pairing.md @@ -0,0 +1,171 @@ +# Browser Receiver Pairing Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a token-based pairing gate to the local browser capture receiver. + +**Architecture:** Keep Browser Inbox as a plugin. Add a transport-level token option to `internal/core/browserreceiver`, preserve the open constructor for current development behavior, and document the extension header contract. + +**Tech Stack:** Go desktop core package tests, browser extension protocol docs, Markdown docs. + +## Global Constraints + +- Do not move Browser Inbox queues or conversion workflows into desktop core. +- Preserve existing receiver behavior when no token is configured. +- Paired mode must not publish capture events for missing or wrong tokens. +- Use TDD: write the failing Go receiver test first, run it red, then implement. +- Commit and push each affected repository after meaningful changes. + +--- + +### Task 1: Document The Pairing Contract + +**Files:** +- Create: `/home/mirivlad/git/verstak2/verstak-docs/docs/superpowers/specs/2026-06-29-browser-receiver-pairing-design.md` +- Create: `/home/mirivlad/git/verstak2/verstak-docs/docs/superpowers/plans/2026-06-29-browser-receiver-pairing.md` + +**Interfaces:** +- Produces documented `X-Verstak-Receiver-Token` pairing contract. + +- [ ] **Step 1: Write spec and plan** + +Write the design and this implementation plan. + +- [ ] **Step 2: Verify docs** + +Run: + +```bash +cd /home/mirivlad/git/verstak2/verstak-docs +git diff --check +``` + +Expected: exits 0. + +- [ ] **Step 3: Commit and push docs** + +Run: + +```bash +cd /home/mirivlad/git/verstak2/verstak-docs +git add docs/superpowers/specs/2026-06-29-browser-receiver-pairing-design.md docs/superpowers/plans/2026-06-29-browser-receiver-pairing.md +git commit -m "docs: plan browser receiver pairing" +git push +``` + +Expected: docs `main` is clean and pushed. + +### Task 2: Desktop Receiver Token Gate + +**Files:** +- Modify: `/home/mirivlad/git/verstak2/verstak-desktop/internal/core/browserreceiver/receiver_test.go` +- Modify: `/home/mirivlad/git/verstak2/verstak-desktop/internal/core/browserreceiver/receiver.go` + +**Interfaces:** +- Produces: + - `type Options struct { RequireToken bool; ReceiverToken string }` + - `func NewWithOptions(bus *events.Bus, options Options, providers ...WorkspaceProvider) *Receiver` + +- [ ] **Step 1: Write the failing tests** + +Add tests proving missing/wrong token rejection and correct token acceptance. + +- [ ] **Step 2: Run RED** + +Run: + +```bash +cd /home/mirivlad/git/verstak2/verstak-desktop +go test ./internal/core/browserreceiver +``` + +Expected: fails because `Options` / `NewWithOptions` do not exist. + +- [ ] **Step 3: Implement token gate** + +Add `Options`, `NewWithOptions`, header validation, and constant-time token +comparison. Keep `New` behavior unchanged. + +- [ ] **Step 4: Run GREEN** + +Run: + +```bash +cd /home/mirivlad/git/verstak2/verstak-desktop +go test ./internal/core/browserreceiver +go test ./internal/core/... +``` + +Expected: both commands exit 0. + +- [ ] **Step 5: Commit and push desktop** + +Run: + +```bash +cd /home/mirivlad/git/verstak2/verstak-desktop +git add internal/core/browserreceiver/receiver.go internal/core/browserreceiver/receiver_test.go +git commit -m "feat: require token for paired browser receiver" +git push +``` + +Expected: commit is pushed. Existing unrelated `frontend/wailsjs/go/models.ts` +remains unstaged. + +### Task 3: Extension And Roadmap Docs + +**Files:** +- Modify: `/home/mirivlad/git/verstak2/verstak-browser-extension/README.md` +- Modify: `/home/mirivlad/git/verstak2/verstak-docs/05_Official_Plugins.md` +- Modify: `/home/mirivlad/git/verstak2/verstak-docs/07_Full_Implementation_Roadmap.md` + +**Interfaces:** +- Consumes verified receiver token gate. +- Produces docs matching implemented pairing behavior. + +- [ ] **Step 1: Update extension README** + +Change the receiver token header description from optional future work to: + +```md +- `X-Verstak-Receiver-Token: ` required when the desktop receiver is in paired mode +``` + +- [ ] **Step 2: Verify and commit extension docs** + +Run: + +```bash +cd /home/mirivlad/git/verstak2/verstak-browser-extension +git diff --check +git add README.md +git commit -m "docs: describe receiver token pairing" +git push +``` + +Expected: extension `main` is clean and pushed. + +- [ ] **Step 3: Update platform docs** + +In `05_Official_Plugins.md`, describe that Browser Inbox receives captures +through the local receiver token pairing model. In +`07_Full_Implementation_Roadmap.md`, mark: + +```md +- [x] define local receiver permission/pairing model; +``` + +- [ ] **Step 4: Verify and commit docs** + +Run: + +```bash +cd /home/mirivlad/git/verstak2/verstak-docs +git diff --check +rg -n "define local receiver permission/pairing model|X-Verstak-Receiver-Token" 05_Official_Plugins.md 07_Full_Implementation_Roadmap.md +git add 05_Official_Plugins.md 07_Full_Implementation_Roadmap.md +git commit -m "docs: mark browser receiver pairing complete" +git push +``` + +Expected: docs `main` is clean and pushed. diff --git a/docs/superpowers/specs/2026-06-29-browser-receiver-pairing-design.md b/docs/superpowers/specs/2026-06-29-browser-receiver-pairing-design.md new file mode 100644 index 0000000..69a8a66 --- /dev/null +++ b/docs/superpowers/specs/2026-06-29-browser-receiver-pairing-design.md @@ -0,0 +1,103 @@ +# Browser Receiver Pairing Design + +## Purpose + +The browser extension already sends captures to the desktop local receiver and +can include `X-Verstak-Receiver-Token`. The desktop receiver currently accepts +captures without a pairing model. This slice defines and implements the local +receiver token gate without moving Browser Inbox behavior into desktop core. + +## Scope + +This slice covers only the local receiver permission/pairing model: + +- receiver token validation; +- clear HTTP responses for paired/unpaired requests; +- documentation of how the extension presents the token. + +It does not implement domain-to-workspace binding, inbox conversion to +notes/files/activity, browser UI for pairing QR codes, or encrypted token +storage. Those remain later Phase 5 work. + +## Model + +The receiver has two modes: + +- **Open legacy mode:** no receiver token is configured. This preserves current + development behavior and accepts captures without the token header. +- **Paired mode:** a receiver token is configured and enabled. Every capture + request must include: + +```text +X-Verstak-Receiver-Token: +``` + +The receiver compares the supplied token to the configured token using a +constant-time comparison. It does not publish browser capture events when the +token is missing or wrong. + +## HTTP Contract + +Endpoint: + +```text +POST /api/browser-inbox/v1/captures +``` + +Successful capture: + +```http +202 Accepted +``` + +```json +{ "status": "accepted", "captureId": "capture-id" } +``` + +Missing token in paired mode: + +```http +401 Unauthorized +``` + +```json +{ "error": "receiver token required" } +``` + +Wrong token in paired mode: + +```http +401 Unauthorized +``` + +```json +{ "error": "receiver token invalid" } +``` + +Other validation behavior remains unchanged: invalid payloads return `400`, +missing Browser Inbox consumers return `503`, and non-POST methods return +`405`. + +## Runtime API + +Desktop core exposes the model inside `internal/core/browserreceiver`: + +```go +type Options struct { + RequireToken bool + ReceiverToken string +} + +func NewWithOptions(bus *events.Bus, options Options, providers ...WorkspaceProvider) *Receiver +``` + +`New(bus, providers...)` remains the open legacy constructor. + +## Testing + +`internal/core/browserreceiver/receiver_test.go` must prove: + +- paired receivers reject missing tokens with `401`; +- paired receivers reject wrong tokens with `401`; +- paired receivers accept correct tokens and publish the capture event; +- open legacy receivers still accept captures without a token.