Files
vdm/extension/docs/amo-permissions.md
T
samiandClaude Sonnet 5 25c171f742 ext: pairing-restart test + AMO permission justification
tests/transport/storage.test.ts covers the storage half of "the pairing
token survives a browser restart" (round-trip, unpair-clears, corrupted
override falls back to auto). websocket.test.ts adds the transport half:
a fresh WebSocketTransport instance over the same backing store reuses
the persisted token with no re-pairing, plus pairWithCode/unpair
coverage. "Wrong token rejected and rate-limited" was already covered
(websocket.test.ts's NotPaired/RateLimited cases).

docs/amo-permissions.md is the submission-ready permission justification
for AMO's Notes to Reviewer field, covering every permission in
manifest.json plus what was deliberately not requested and how cookie/
header data is handled.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ed8KEmAW48v4YHdxLtqsMB
2026-09-11 12:25:42 +04:00

5.6 KiB

AMO permission justification

Submitted with every version bump in the AMO "Notes to Reviewer" field. Kept here so the justification is reviewed and versioned alongside the permission list itself (manifest.json), instead of living only in a web form. docs/05 §7 is the design-time version of this; this file is the submission-ready copy.

What Velox is

A download manager. It intercepts a response Firefox is about to download, hands the URL, request headers, and cookies to a companion native application (veloxd), and lets that application fetch the file with resumable, multi-connection transfers. The extension itself never stores or transfers file bytes — see CLAUDE.md §3 and the no-download-logic ESLint gate (eslint.config.mjs) that fails CI if it ever does.

Permissions requested

Permission Why Narrowest alternative considered
webRequest + webRequestBlocking The whole feature: inspect response headers on onHeadersReceived to decide whether to intercept a download, and {cancel: true} before Firefox starts its own download. This is the one thing MV3 Chrome removed and MV3 Firefox kept — it's why the extension can exist as designed (docs/05 §1). None. Without blocking webRequest there is no way to stop Firefox's own download before it starts; polling downloads.onCreated alone (which we also use, see below) only catches what already started.
downloads Belt-and-braces safety net (capture/downloads-api.ts): some downloads (form POSTs, service-worker blobs) never reach onHeadersReceived in a way we can act on and only surface via downloads.onCreated. Also used to cancel/erase a download we're taking over so Firefox doesn't keep two copies. Drop the safety net and accept that those cases silently bypass Velox. Rejected — docs/05 §2 calls this out explicitly as a known gap the safety net exists to close.
cookies A file behind a login (private CDN links, forum attachments) needs its session cookies handed to veloxd, or the daemon's fetch gets a 403 the browser's own request wouldn't have. Read via cookies.getAll(url) only for a URL we are about to offer to the daemon — never harvested in bulk or logged. Skip cookies and only support anonymous URLs. Rejected — it's a top user-facing IDM-parity feature and the reason people leave Chrome download managers behind.
contextMenus "Download with Velox" on a link/image/video, and "Download all links…" (docs/05 §3). Table-stakes UI for a download manager extension. None smaller — there's no partial grant for context menus.
storage browser.storage.local holds only extension-local state: the WebSocket pairing token, the manual transport override, the last-good WS port, and the user's default-category preference (transport/storage.ts, options/prefs.ts). No browsing data. None — some persistence is required for pairing to survive a restart (M1 DoD), which is the point of the token existing at all.
notifications Tells the user when the daemon can't be reached for a download that fell back to Firefox, and (native-messaging path) surfaces pairing prompts if the GUI isn't running. Silent failure. Rejected — capture fails open by design (CLAUDE.md §4) and a silent fallback with no notification would look like a bug.
nativeMessaging Opportunistic transport to veloxd over a Unix socket, for installs where it works (docs/adr/0003). Not the default path — WebSocket is — but shipped because it avoids the WebSocket port-scan on installs where the native host manifest is reachable. Drop native messaging and use WebSocket exclusively. Considered and rejected in ADR 0003: keeping both means the extension keeps working across deb/snap/flatpak Firefox without per-flavour capture-logic forks.
<all_urls> (host permission) Downloads happen from every site on the web; webRequest's header inspection and cookies.getAll both need to run against whatever site the user is on. This is the item AMO reviewers push back on hardest for extensions of this shape. A fixed list of "known download sites" — unworkable for a general-purpose download manager, and defeats the point of an IDM-style interceptor. Mitigation, not a narrower permission: the exclusion list in Options is front-and-center (options.html → "Capture policy") so a user can scope capture down to nothing on sites they don't want Velox touching, and the bypass modifier (default Alt) lets a single click skip capture without changing settings.

What is explicitly not requested

  • No <all_urls> XHR/fetch use — webRequest/cookies read metadata about a request Firefox is already making; the extension never issues its own network request for file bytes (enforced by the ESLint gate above).
  • No identity, history, bookmarks, tabs beyond what contextMenus/commands already imply, management, or any permission unrelated to capturing and handing off a download.
  • No remote code: the manifest ships no CDN scripts and no eval; web-ext lint fails the build otherwise (CI's extension-lint job).

Data handling

  • Cookies and headers are held in memory only long enough to answer one capture.offer call to the local daemon (capture/headers.ts's ring buffer, 5-minute TTL) — never written to disk by the extension and never sent anywhere but 127.0.0.1.
  • The daemon connection is local-only: WebSocketTransport connects to ws://127.0.0.1:<port>, never a remote host (docs/05 §4, conformance-tested).
  • Nothing is sent to Anthropic, Mozilla, or any third party beyond the user's own local veloxd process.