104 lines
2.4 KiB
Markdown
104 lines
2.4 KiB
Markdown
# 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: <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.
|