verstak-docs/docs/superpowers/specs/2026-06-29-browser-receiver...

2.4 KiB

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:
X-Verstak-Receiver-Token: <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:

POST /api/browser-inbox/v1/captures

Successful capture:

202 Accepted
{ "status": "accepted", "captureId": "capture-id" }

Missing token in paired mode:

401 Unauthorized
{ "error": "receiver token required" }

Wrong token in paired mode:

401 Unauthorized
{ "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:

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.