docs: document required browser receiver pairing
This commit is contained in:
parent
2ac7dd52ec
commit
c3ee4cc21f
|
|
@ -250,9 +250,12 @@ search.provider
|
||||||
Текущий статус: базовый `verstak.browser-inbox` implemented as both a global
|
Текущий статус: базовый `verstak.browser-inbox` implemented as both a global
|
||||||
sidebar view and a workspace item. Workspace tabs keep their own pending queue;
|
sidebar view and a workspace item. Workspace tabs keep their own pending queue;
|
||||||
the global sidebar view aggregates queues from all workspaces plus unscoped
|
the global sidebar view aggregates queues from all workspaces plus unscoped
|
||||||
global captures. The local receiver now has an opt-in paired mode that requires
|
global captures. The local receiver starts in paired mode: it generates an
|
||||||
`X-Verstak-Receiver-Token` before publishing browser capture events. Browser
|
installation-local token and requires `X-Verstak-Receiver-Token` before
|
||||||
Inbox stores plugin-owned `domainBindings` and routes unscoped captures with an
|
publishing browser capture events. The Browser Inbox settings panel exposes the
|
||||||
|
receiver URL and token, and rotates the token through the dangerous
|
||||||
|
`browser.receiver.manage` permission. Browser Inbox stores plugin-owned
|
||||||
|
`domainBindings` and routes unscoped captures with an
|
||||||
exact domain match into the bound workspace queue. Its first conversion workflow
|
exact domain match into the bound workspace queue. Its first conversion workflow
|
||||||
creates ordinary Markdown notes through the public Files API and publishes a
|
creates ordinary Markdown notes through the public Files API and publishes a
|
||||||
`browser.capture.converted` event, which Activity records through its public
|
`browser.capture.converted` event, which Activity records through its public
|
||||||
|
|
|
||||||
|
|
@ -53,14 +53,15 @@ Known remaining gaps:
|
||||||
- Activity, Journal, Browser Inbox conversion workflows, indexed Search, and
|
- Activity, Journal, Browser Inbox conversion workflows, indexed Search, and
|
||||||
Secrets now have baseline plugin implementations and public API contracts.
|
Secrets now have baseline plugin implementations and public API contracts.
|
||||||
Their remaining work is product UX depth: richer Today aggregation,
|
Their remaining work is product UX depth: richer Today aggregation,
|
||||||
actionable Activity to Journal review flows, capture/pairing discoverability,
|
actionable Activity to Journal review flows, capture-from-clipboard/manual
|
||||||
production-grade reporting, and final polish.
|
capture, production-grade reporting, and final polish.
|
||||||
- Templates plugin is not implemented yet.
|
- Templates plugin is not implemented yet.
|
||||||
- File/image preview exists as a basic provider with bounded inline image
|
- File/image preview exists as a basic provider with bounded inline image
|
||||||
rendering through the public Files API.
|
rendering through the public Files API.
|
||||||
- Browser extension repository has protocol, queue, and Chromium/Firefox build
|
- Browser extension repository has protocol, queue, and Chromium/Firefox build
|
||||||
scaffold; desktop has a local receiver and mounted-view inbox plugin; receiver
|
scaffold; desktop has a bounded, token-paired local receiver and mounted-view
|
||||||
pairing, basic Browser Inbox domain binding, create-note conversion, and
|
inbox plugin; receiver pairing settings, basic Browser Inbox domain binding,
|
||||||
|
create-note conversion, and
|
||||||
create-link conversion are implemented, text file attachment conversion is
|
create-link conversion are implemented, text file attachment conversion is
|
||||||
implemented, bounded binary attachment conversion is implemented, and Activity
|
implemented, bounded binary attachment conversion is implemented, and Activity
|
||||||
records conversions. Chunked large-file attachment capture remains future
|
records conversions. Chunked large-file attachment capture remains future
|
||||||
|
|
@ -176,6 +177,9 @@ Tasks:
|
||||||
- [x] implement browser extension capture scaffold for URL, selected text,
|
- [x] implement browser extension capture scaffold for URL, selected text,
|
||||||
page title, and link captures;
|
page title, and link captures;
|
||||||
- [x] define local receiver permission/pairing model;
|
- [x] define local receiver permission/pairing model;
|
||||||
|
- [x] require an installation-local pairing token and expose its rotation through
|
||||||
|
Browser Inbox settings;
|
||||||
|
- [x] bound browser receiver payloads and file content before publishing events;
|
||||||
- [x] add domain-to-workspace binding;
|
- [x] add domain-to-workspace binding;
|
||||||
- [x] convert inbox entries into notes through public plugin APIs;
|
- [x] convert inbox entries into notes through public plugin APIs;
|
||||||
- [x] record converted inbox entries in Activity through public plugin events;
|
- [x] record converted inbox entries in Activity through public plugin events;
|
||||||
|
|
@ -189,10 +193,10 @@ Verification:
|
||||||
- local receiver API tests;
|
- local receiver API tests;
|
||||||
- inbox plugin smoke/e2e tests.
|
- inbox plugin smoke/e2e tests.
|
||||||
|
|
||||||
Status: baseline capture, routing, conversion, and Activity recording workflows
|
Status: baseline capture, pairing, routing, conversion, and Activity recording
|
||||||
are implemented. Remaining work is UX follow-up: make extension pairing,
|
workflows are implemented. Remaining work is UX follow-up:
|
||||||
capture-from-clipboard/manual capture, domain binding state, and conversion
|
capture-from-clipboard/manual capture, domain binding state, and conversion
|
||||||
outcomes obvious in the visible app flow.
|
outcomes in the visible app flow.
|
||||||
|
|
||||||
### Phase 6 - Secrets
|
### Phase 6 - Secrets
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,22 @@
|
||||||
|
|
||||||
**Goal:** Add a token-based pairing gate to the local browser capture receiver.
|
**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.
|
**Architecture:** Keep Browser Inbox as a plugin. The desktop runtime generates
|
||||||
|
and requires an installation-local receiver token, while the plugin and browser
|
||||||
|
extension expose the settings transfer and rotation flow.
|
||||||
|
|
||||||
**Tech Stack:** Go desktop core package tests, browser extension protocol docs, Markdown docs.
|
**Tech Stack:** Go desktop core package tests, browser extension protocol docs, Markdown docs.
|
||||||
|
|
||||||
|
## Completion Status (2026-07-10)
|
||||||
|
|
||||||
|
Completed. The implementation also added bounded ingress validation, token
|
||||||
|
persistence and rotation, the `browser.receiver.manage` SDK permission, a
|
||||||
|
Browser Inbox settings panel, and extension popup token persistence.
|
||||||
|
|
||||||
## Global Constraints
|
## Global Constraints
|
||||||
|
|
||||||
- Do not move Browser Inbox queues or conversion workflows into desktop core.
|
- Do not move Browser Inbox queues or conversion workflows into desktop core.
|
||||||
- Preserve existing receiver behavior when no token is configured.
|
- Production startup must fail closed when a token cannot be persisted.
|
||||||
- Paired mode must not publish capture events for missing or wrong tokens.
|
- 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.
|
- Use TDD: write the failing Go receiver test first, run it red, then implement.
|
||||||
- Commit and push each affected repository after meaningful changes.
|
- Commit and push each affected repository after meaningful changes.
|
||||||
|
|
@ -27,11 +35,11 @@
|
||||||
**Interfaces:**
|
**Interfaces:**
|
||||||
- Produces documented `X-Verstak-Receiver-Token` pairing contract.
|
- Produces documented `X-Verstak-Receiver-Token` pairing contract.
|
||||||
|
|
||||||
- [ ] **Step 1: Write spec and plan**
|
- [x] **Step 1: Write spec and plan**
|
||||||
|
|
||||||
Write the design and this implementation plan.
|
Write the design and this implementation plan.
|
||||||
|
|
||||||
- [ ] **Step 2: Verify docs**
|
- [x] **Step 2: Verify docs**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
|
|
@ -42,7 +50,7 @@ git diff --check
|
||||||
|
|
||||||
Expected: exits 0.
|
Expected: exits 0.
|
||||||
|
|
||||||
- [ ] **Step 3: Commit and push docs**
|
- [x] **Step 3: Commit and push docs**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
|
|
@ -66,11 +74,11 @@ Expected: docs `main` is clean and pushed.
|
||||||
- `type Options struct { RequireToken bool; ReceiverToken string }`
|
- `type Options struct { RequireToken bool; ReceiverToken string }`
|
||||||
- `func NewWithOptions(bus *events.Bus, options Options, providers ...WorkspaceProvider) *Receiver`
|
- `func NewWithOptions(bus *events.Bus, options Options, providers ...WorkspaceProvider) *Receiver`
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing tests**
|
- [x] **Step 1: Write the failing tests**
|
||||||
|
|
||||||
Add tests proving missing/wrong token rejection and correct token acceptance.
|
Add tests proving missing/wrong token rejection and correct token acceptance.
|
||||||
|
|
||||||
- [ ] **Step 2: Run RED**
|
- [x] **Step 2: Run RED**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
|
|
@ -81,12 +89,12 @@ go test ./internal/core/browserreceiver
|
||||||
|
|
||||||
Expected: fails because `Options` / `NewWithOptions` do not exist.
|
Expected: fails because `Options` / `NewWithOptions` do not exist.
|
||||||
|
|
||||||
- [ ] **Step 3: Implement token gate**
|
- [x] **Step 3: Implement token gate**
|
||||||
|
|
||||||
Add `Options`, `NewWithOptions`, header validation, and constant-time token
|
Add `Options`, `NewWithOptions`, header validation, and constant-time token
|
||||||
comparison. Keep `New` behavior unchanged.
|
comparison. Keep `New` behavior unchanged.
|
||||||
|
|
||||||
- [ ] **Step 4: Run GREEN**
|
- [x] **Step 4: Run GREEN**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
|
|
@ -98,7 +106,7 @@ go test ./internal/core/...
|
||||||
|
|
||||||
Expected: both commands exit 0.
|
Expected: both commands exit 0.
|
||||||
|
|
||||||
- [ ] **Step 5: Commit and push desktop**
|
- [x] **Step 5: Commit and push desktop**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
|
|
@ -123,7 +131,7 @@ remains unstaged.
|
||||||
- Consumes verified receiver token gate.
|
- Consumes verified receiver token gate.
|
||||||
- Produces docs matching implemented pairing behavior.
|
- Produces docs matching implemented pairing behavior.
|
||||||
|
|
||||||
- [ ] **Step 1: Update extension README**
|
- [x] **Step 1: Update extension README**
|
||||||
|
|
||||||
Change the receiver token header description from optional future work to:
|
Change the receiver token header description from optional future work to:
|
||||||
|
|
||||||
|
|
@ -131,7 +139,7 @@ Change the receiver token header description from optional future work to:
|
||||||
- `X-Verstak-Receiver-Token: <token>` required when the desktop receiver is in paired mode
|
- `X-Verstak-Receiver-Token: <token>` required when the desktop receiver is in paired mode
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 2: Verify and commit extension docs**
|
- [x] **Step 2: Verify and commit extension docs**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
|
|
@ -145,7 +153,7 @@ git push
|
||||||
|
|
||||||
Expected: extension `main` is clean and pushed.
|
Expected: extension `main` is clean and pushed.
|
||||||
|
|
||||||
- [ ] **Step 3: Update platform docs**
|
- [x] **Step 3: Update platform docs**
|
||||||
|
|
||||||
In `05_Official_Plugins.md`, describe that Browser Inbox receives captures
|
In `05_Official_Plugins.md`, describe that Browser Inbox receives captures
|
||||||
through the local receiver token pairing model. In
|
through the local receiver token pairing model. In
|
||||||
|
|
@ -155,7 +163,7 @@ through the local receiver token pairing model. In
|
||||||
- [x] define local receiver permission/pairing model;
|
- [x] define local receiver permission/pairing model;
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 4: Verify and commit docs**
|
- [x] **Step 4: Verify and commit docs**
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,31 +2,31 @@
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
The browser extension already sends captures to the desktop local receiver and
|
The browser extension sends captures to the desktop local receiver with
|
||||||
can include `X-Verstak-Receiver-Token`. The desktop receiver currently accepts
|
`X-Verstak-Receiver-Token`. The desktop runtime starts that receiver only in
|
||||||
captures without a pairing model. This slice defines and implements the local
|
paired mode, without moving Browser Inbox queues or conversion behavior into
|
||||||
receiver token gate without moving Browser Inbox behavior into desktop core.
|
desktop core.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
This slice covers only the local receiver permission/pairing model:
|
This slice covers only the local receiver permission/pairing model:
|
||||||
|
|
||||||
- receiver token validation;
|
- generation and local persistence of a receiver token;
|
||||||
- clear HTTP responses for paired/unpaired requests;
|
- receiver token validation and rotation without a server restart;
|
||||||
- documentation of how the extension presents the token.
|
- bounded capture payload validation before an event is published;
|
||||||
|
- Browser Inbox and extension settings for transferring the token.
|
||||||
|
|
||||||
It does not implement domain-to-workspace binding, inbox conversion to
|
It does not implement browser UI for pairing QR codes or encrypted keyring
|
||||||
notes/files/activity, browser UI for pairing QR codes, or encrypted token
|
storage. The current token is kept in the installation-local app settings file,
|
||||||
storage. Those remain later Phase 5 work.
|
which the manager writes with mode `0600`.
|
||||||
|
|
||||||
## Model
|
## Model
|
||||||
|
|
||||||
The receiver has two modes:
|
The production receiver always starts in paired mode. On startup it generates a
|
||||||
|
32-byte random token when none exists, stores it outside the vault in
|
||||||
- **Open legacy mode:** no receiver token is configured. This preserves current
|
`~/.config/verstak/config.json`, and does not log it. If that token cannot be
|
||||||
development behavior and accepts captures without the token header.
|
persisted, the local receiver is disabled rather than opened without a token.
|
||||||
- **Paired mode:** a receiver token is configured and enabled. Every capture
|
Every capture request must include:
|
||||||
request must include:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
X-Verstak-Receiver-Token: <token>
|
X-Verstak-Receiver-Token: <token>
|
||||||
|
|
@ -34,7 +34,8 @@ X-Verstak-Receiver-Token: <token>
|
||||||
|
|
||||||
The receiver compares the supplied token to the configured token using a
|
The receiver compares the supplied token to the configured token using a
|
||||||
constant-time comparison. It does not publish browser capture events when the
|
constant-time comparison. It does not publish browser capture events when the
|
||||||
token is missing or wrong.
|
token is missing or wrong. The package keeps the open constructor for embedded
|
||||||
|
legacy callers, but `main.go` does not use it.
|
||||||
|
|
||||||
## HTTP Contract
|
## HTTP Contract
|
||||||
|
|
||||||
|
|
@ -76,7 +77,8 @@ Wrong token in paired mode:
|
||||||
|
|
||||||
Other validation behavior remains unchanged: invalid payloads return `400`,
|
Other validation behavior remains unchanged: invalid payloads return `400`,
|
||||||
missing Browser Inbox consumers return `503`, and non-POST methods return
|
missing Browser Inbox consumers return `503`, and non-POST methods return
|
||||||
`405`.
|
`405`. Payloads over 12 MiB return `413`; capture text, file metadata, encoded
|
||||||
|
binary data, and decoded file content are capped before publication.
|
||||||
|
|
||||||
## Runtime API
|
## Runtime API
|
||||||
|
|
||||||
|
|
@ -93,6 +95,12 @@ func NewWithOptions(bus *events.Bus, options Options, providers ...WorkspaceProv
|
||||||
|
|
||||||
`New(bus, providers...)` remains the open legacy constructor.
|
`New(bus, providers...)` remains the open legacy constructor.
|
||||||
|
|
||||||
|
The Wails bridge exposes `PluginBrowserReceiverPairing` and
|
||||||
|
`PluginRotateBrowserReceiverToken` only to plugins declaring the dangerous
|
||||||
|
`browser.receiver.manage` permission. `verstak.browser-inbox` presents the
|
||||||
|
receiver URL, token copy action, and rotation control; the browser extension
|
||||||
|
stores the values in its local settings.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
`internal/core/browserreceiver/receiver_test.go` must prove:
|
`internal/core/browserreceiver/receiver_test.go` must prove:
|
||||||
|
|
@ -100,4 +108,6 @@ func NewWithOptions(bus *events.Bus, options Options, providers ...WorkspaceProv
|
||||||
- paired receivers reject missing tokens with `401`;
|
- paired receivers reject missing tokens with `401`;
|
||||||
- paired receivers reject wrong tokens with `401`;
|
- paired receivers reject wrong tokens with `401`;
|
||||||
- paired receivers accept correct tokens and publish the capture event;
|
- paired receivers accept correct tokens and publish the capture event;
|
||||||
- open legacy receivers still accept captures without a token.
|
- a token persists across settings reload and changes immediately on rotation;
|
||||||
|
- oversized or malformed capture payloads do not publish an event;
|
||||||
|
- Browser Inbox and extension smoke tests cover the settings transfer path.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue