From 853666a719adc49206cf36d557a9bbd302afef17 Mon Sep 17 00:00:00 2001 From: StarlightDaemon <23347919+StarlightDaemon@users.noreply.github.com> Date: Wed, 9 Sep 2026 02:32:56 -0600 Subject: [PATCH 01/28] docs(release): preserve v1 release-readiness audit evidence Add the two independent 2026-09-09 release audits (Fable readiness audit, Astra blind audit) and their index under docs/release/v1/audits/. These are historical assessment evidence for the v1 release program, not current repository truth; findings are reconciled against the live tree before any implementation. --- .../ASTRA_BLIND_RELEASE_AUDIT_2026-09-09.md | 1476 +++++++++++++++++ ...ABLE_RELEASE_READINESS_AUDIT_2026-09-09.md | 1048 ++++++++++++ docs/release/v1/audits/README.md | 19 + 3 files changed, 2543 insertions(+) create mode 100644 docs/release/v1/audits/ASTRA_BLIND_RELEASE_AUDIT_2026-09-09.md create mode 100644 docs/release/v1/audits/FABLE_RELEASE_READINESS_AUDIT_2026-09-09.md create mode 100644 docs/release/v1/audits/README.md diff --git a/docs/release/v1/audits/ASTRA_BLIND_RELEASE_AUDIT_2026-09-09.md b/docs/release/v1/audits/ASTRA_BLIND_RELEASE_AUDIT_2026-09-09.md new file mode 100644 index 0000000..11b7baa --- /dev/null +++ b/docs/release/v1/audits/ASTRA_BLIND_RELEASE_AUDIT_2026-09-09.md @@ -0,0 +1,1476 @@ +--- +artifact: CTRL Astra Independent Blind Release Audit +audit_date: 2026-09-09 +role: independent adversarial repository/release assessment +status: historical assessment evidence; findings require reconciliation against current repository state before implementation +source_agent: Astra +blindness_note: dedicated prior audits were quarantined, but repository operational state exposed some earlier conclusions before source reconstruction +--- + +# Decisions Supported by Evidence + +**CTRL is not ready for either store. It needs targeted repairs, a smaller verified support surface, and a reproducible release process—not a rewrite.** + +The strongest evidence supports these decisions: + +- Fix server identity and stale-response handling before enabling public queue control. +- Replace the incorrect positional diff mechanism with reliable snapshots initially. +- Preserve AES-GCM encryption and the current session-only vault key design; repair vault consistency, corruption handling, and export sanitization. +- Treat every torrent client as **unverified end to end** until tested against a real server in both browsers. +- Reject the existing Firefox package as a release candidate: Mozilla’s validator reports a missing mandatory add-on ID. +- Replace contradictory privacy claims with an accurate account of communication with user-configured servers. +- Establish one authoritative source-to-package process before producing submission artifacts. + +These are audit recommendations. No implementation authority was assumed. + +# Decisions Requiring Operator Choice + +The material decisions are: + +1. **Client scope:** verify a small initial set, or delay release until all nine implementations pass. +2. **Transport policy:** support explicitly disclosed HTTP connections to user-controlled local/LAN clients, or require HTTPS except loopback. +3. **Page scanning:** retain and repair bulk magnet scanning, or remove it and its `scripting`/`activeTab` permissions from v1. +4. **Browser support:** establish a narrow tested desktop baseline, or fund older-browser compatibility and consent fallbacks. +5. **Release surface:** ship an English-first core product, or finish the broader settings, localization, and auxiliary features. +6. **Publisher identity:** choose the permanent Firefox add-on ID and complete the applicable publisher/trader declarations. + +Detailed alternatives appear below. None prevents completing this audit. + +# Critical Findings + +| Rank | Finding | Evidence | Consequence | +|---|---|---|---| +| 1 | **AST-STATE-001:** stale server A results can replace B’s display; a command from that stale row routes to B | Controlled execution of the current background module | Wrong-server queue operations | +| 2 | **AST-STATE-002:** list diffs corrupt identity/order during replacement, removal, and reordering | Executed current diff generator and patch applicator | Incorrect or missing rows | +| 3 | **AST-SEC-001–003:** cross-window lock divergence, corrupt-vault false unlock, and incomplete safe-export sanitization | Chrome test and source execution | Misleading lock boundary, configuration loss, secret disclosure | +| 4 | **AST-NET-001–003:** XML serialization, cookie-session handling, and qBittorrent version assumptions are defective | Source execution, browser Fetch probes, official client source/API | Advertised clients cannot be accepted as working | +| 5 | **AST-FF-001 / AST-BUILD-001:** Firefox metadata and source/rebuild preparation are inadequate | Official validator and package inspection | Firefox submission/review blocked | + +The wrong-server reproduction used synthetic clients and `deleteData: false`. **No real torrent was removed, and file deletion was not demonstrated.** + +# Important Unknowns + +- No real torrent-client environment was available or safely established. Login, add, pause, resume, remove, and reconnect remain unverified against actual servers. +- Firefox was not available for runtime testing. +- Chrome tests used the existing generated build; that build was not independently rebuilt from the audited working tree. +- The non-localhost, host-permission-granted behavior of qBittorrent’s CSRF checks remains unresolved. +- Mozilla’s treatment of unencrypted connections to user-controlled LAN servers needs explicit resolution for the chosen release scope. +- Store-account configuration, repository visibility, hosted policy availability, reviewer credentials, and completed listing assets were not established. +- Repeated clean builds and source-archive rebuilds were not performed because the normal process changes tracked files. + +# Verification Performed + +**Target** + +| Item | Recorded state | +|---|---| +| Repository | CTRL | +| Root / working directory | `E:\Citadel\CTRL` | +| Application | `E:\Citadel\CTRL\extension` | +| Branch | `main` | +| HEAD | `f088c5f857d4f229534562f8c87c92bf4ba66df9` | +| Commit date | July 15, 2026, 11:53:02, UTC−06:00 | +| Commit subject | `chore: ignore AppleDouble and .DS_Store files` | +| Remote | `git@github.com:StarlightDaemon/CTRL.git` | +| Local upstream comparison | `origin/main`: 0 ahead, 0 behind; no fetch | +| Visibility | Unknown; authenticated GitHub inspection unavailable | +| Relevant tag | `pre-dependabot-delete-backup` | +| Stash | Existing generated-build-information stash; untouched | +| Worktrees | Main checkout only; no existing isolated checkout suitable for builds | +| Staged changes | None | + +**Pre-existing work was present.** Modified paths included RAIDEN state, Serena configuration/memories, the package manifest/lockfile, background, context menus, and `KeyManager`. A Serena architecture memory was deleted; two Serena memories and `KeyManager.test.ts` were untracked. Final status matched the initial path/status inventory. + +Repository instructions were inspected, including the RAIDEN navigation, state, open loops, and agent guide. The managed Writ boundary was respected. + +**Blindness limitation:** the initial instruction-loading batch exposed operational state containing earlier review conclusions before source reconstruction. Consequently, this is **not a pristine blind pass**. Those conclusions were not used as proof. Dedicated previous reports were quarantined, and no comparison with another assessment was performed. + +**QUARANTINED — NOT READ DURING BLIND PASS:** + +- All six reports under `.audits/`, including the dated audit/exploration files. +- All files under `reports/`. +- `audit-reports/audit-2026-05-13.md`. +- `.raiden/state/LEGACY_REVIEW.md`. +- `extension/audit_extension.txt` and `extension/audit_extension_after_fix.txt`. +- Historical build logs, browser reports, and console exports. +- `docs/reference/`, withheld to avoid historical-review and store-research anchoring. + +Packaged copies of historical reports were identified by filename without reading their contents. + +**Diagnostics** + +| Check | Result | +|---|---| +| TypeScript | `node node_modules/typescript/bin/tsc --noEmit` — passed | +| ESLint | `node node_modules/eslint/bin/eslint.js src --ext .ts,.tsx` — 0 errors, 31 warnings | +| Unit tests | 16 files, **530 tests passed** | +| Diff probes | Reordering, replacement, and removal failures reproduced | +| Background probe | Delayed A result published after B; stale-row command routed to B | +| Viewport probes | New subscriber received no snapshot; outside-viewport total change emitted no update | +| Vault probe | Missing ciphertext with retained salt allowed incorrect-password “unlock” | +| Safe-export probe | Main passwords removed; `clientOptions.simpleApiKey` retained | +| Network probe | XML converted to a JSON string; caller AbortSignal replaced | +| Chrome | Installed Chrome **152.0.7977.83**, isolated temporary profiles | +| Chrome surfaces | Existing popup/options loaded; setup completed with synthetic credentials | +| Chrome lock test | Another open options window remained unlocked after session-key removal | +| Chrome Fetch | Wire inspection confirmed browser-controlled Origin, absent manual Cookie/Referer, preserved explicit Authorization, inaccessible `Set-Cookie` | +| Firefox runtime | Not performed: no available Firefox environment | +| Real-client tests | Not performed: no suitable verified client environment | +| Mozilla validator | `addons-linter@10.10.0`: **1 error, 5 warnings** | +| Dependency audit | Latest fresh result: **15 affected package nodes: 6 high, 8 moderate, 1 low**; all identified nodes marked development dependencies | +| Builds | Not run: canonical commands mutate tracked build metadata | +| Packages | Existing Firefox ZIP, automatic source ZIP, Chrome and Firefox unpacked outputs inspected | + +The unit command used Vitest’s runner configuration loader with an in-memory `__dirname` binding to avoid its normal generated configuration cache: + +```text +node --input-type=module -e 'globalThis.__dirname=process.cwd(); process.argv=[process.argv[0],"vitest","run","--no-cache","--configLoader","runner"]; await import("./node_modules/vitest/vitest.mjs");' +``` + +This was a diagnostic invocation of the existing suite, not a claim that an unmodified `npm test` invocation was run. + +The validator was installed/executed through a temporary npm cache outside the repository. Temporary Chrome profiles and synthetic loopback diagnostics likewise stayed outside the repository. + +**Policy-check date: September 9, 2026.** Official sources and their application are recorded below. + +- Files changed in repository: **none** +- Commits created: **none** +- Pushes: **none** +- Merges: **none** +- Releases: **none** +- Deployments: **none** +- Store submissions: **none** + +# Product and Architecture Reconstruction + +CTRL is a browser-based remote controller for torrent clients. Its intended user already operates a torrent daemon or Web UI and wants to send links and manage its queue without repeatedly opening that client. + +**Reviewer-safe purpose sentence:** + +> CTRL sends torrent links to a torrent client chosen by the user and displays and controls that client’s download queue. + +The manifest communicates this general purpose. The documentation weakens it by overstating client verification, language coverage, and privacy guarantees. The core functions form one coherent purpose; unrelated resource links and unfinished settings dilute it. + +CTRL observes configured client responses and, upon an explicit context-menu action, magnet links in the current page. It stores encrypted server configuration, preferences, an in-memory-session encryption key, and a session cache of torrent metadata. It transmits authentication material, links, and commands to configured clients. The clients—not CTRL—download content, contact peers, and manage files. + +```text +User + ├─ Popup ─────────────── GET_TORRENTS polling / add messages ─────┐ + ├─ Options ── Zustand sparse torrent store ◄─ viewport messages ─┤ + │ │ active-session port ────┤ + │ └─ settings hooks / vault hooks │ + └─ Context menu ── link/selection or explicit page scan ──────────┤ + ▼ + Background: resolver → client factory → adapter + │ │ │ + │ │ ├─ direct Fetch: qBittorrent + │ │ └─ shared Fetch / JSON-RPC + │ │ │ + │ └─ encrypted server configs ▼ + │ User-configured client + ├─ 2-second active polling + ├─ 1-minute idle alarm + ├─ viewport/diff broadcasts + └─ session torrent cache + +Browser storage.local: encrypted vault + salt + preferences +Browser storage.session: encryption key + torrent cache + +No persistent content scripts, external messaging API, +web-accessible resources, or developer telemetry service found. +``` + +WXT generates MV3 manifests. Chrome uses a service worker; Firefox receives background scripts. React renders popup/options; Zustand owns the options torrent display. Zod validates many adapter responses and imported configuration. `tsyringe` and `reflect-metadata` support decorators, though the factory explicitly constructs adapters. + +## Repository inventory + +Approximately **129 TypeScript/TSX source files, 19,117 lines**, with about **10,397 lines in client implementations**. There are 21 test/spec files in the inspected test tree. + +| Area | Purpose / complexity | Runtime and test assessment | +|---|---|---| +| Entrypoints | Background orchestration and two UIs; high risk | Critical ownership and concurrency gaps; no adequate lifecycle integration coverage | +| Client adapters | Nine types, extensive optional APIs; high complexity | Strongest unit-test concentration; browser contracts insufficiently tested | +| Shared transport | Fetch, JSON-RPC, errors, retries; modest size, high impact | Cross-cutting encoding/authentication defects | +| Security | Key derivation, encrypted vault, session key | Good primitives; weak transaction and multi-context behavior | +| Torrent state | Poller, viewport manager, diff, hydrator, Zustand | Several interacting owners; deterministic failures reproduced | +| Settings/UI | Server setup, import/export, appearance, diagnostics | Duplicate vault/settings snapshots; unfinished controls | +| Styling/assets | Carbon, Tailwind, Plex, two icon systems | Generated font duplication dominates distribution | +| Tests/CI | Unit tests, Chromium E2E, build jobs | Useful foundation; missing release-critical contract tests | +| Release scripts | Metadata generation, builds, ZIPs, backups | Multiple competing package paths; tracked-file mutation | +| Repository process | RAIDEN, Serena, historical reports | Development context, not extension runtime; should stay out of release sources unless necessary | + +Dead/legacy candidates include the no-op `HeaderRewriter`, unused keepalive/DOM parsing helpers, duplicate client interfaces, unconsumed settings, and numerous adapter capabilities without a UI consumer. These are scope/debt candidates, not a mandate for wholesale cleanup. + +# User Journey Reconstruction + +| Stage | Actual behavior | Release implication | +|---|---|---| +| 1. Install | MV3 manifests and optional endpoint access | Chrome package loads; Firefox metadata blocks signing | +| 2. First launch | Popup directs unconfigured users to setup; options presents vault setup | Coherent starting point | +| 3. Onboarding | Password setup precedes server configuration | After setup, empty queue incorrectly appears online | +| 4. Permissions | Server UI checks/requests endpoint-origin access | Denial/revocation recovery needs browser testing | +| 5. Credentials | Saved through encrypted vault | Existing open contexts can retain decrypted snapshots | +| 6. Server setup | Client selector, split address fields, test/save | IPv6/path handling and accessible labels deficient | +| 7. Add torrent | Popup message supports global paused default; advanced dialog supplies explicit values | Defaults differ across entrypoints | +| 8. Queue/status | Background viewport updates plus separate popup polling | Stale results, incorrect diffs, misleading connectivity | +| 9. Context menu | Add link/selection; explicit paused add; optional page scan | Scan immediately adds all found links; no preview/deduplication | +| 10. Settings | Many persisted preferences | Several affect previews or no runtime consumer | +| 11. Locked state | Session key removed; initiating UI locks | Other windows remain visually unlocked | +| 12. Revoked permission | No dedicated revocation-driven reset/recovery state established | Stale data and generic network errors possible | +| 13. Server unavailable | Adapter errors exist | Options connection banner remains “Online”; actions lack reliable feedback | +| 14. Extension restart | Session cache hydrates background | Cache lacks server/generation provenance | +| 15. Browser restart | Session key should disappear, requiring unlock | Actual full restart acceptance not performed | +| 16. Upgrade | Legacy key purge runs on background wake; plaintext migration path exists | Good mechanisms; interrupted/corrupt migration remains untested | +| 17. Backup/import/export | Validates imports before writes; exports JSON | Safe export leaks a supported API key; snapshots can diverge | +| 18. Uninstall | Browser-managed extension storage removal; remote torrents remain | No remote cleanup feature; exported files remain user-owned | + +# Findings Ledger + +“Blocks responsible quality bar” means this audit recommends withholding release. It does **not** mean a store has an automated rule detecting the defect. + +## AST-STATE-001 — Commands and responses lack stable server identity + +**Priority:** P0 +**Type:** Data integrity / command routing +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** [background.ts](/E:/Citadel/CTRL/extension/src/entrypoints/background.ts:150), [TorrentRow.tsx](/E:/Citadel/CTRL/extension/src/entities/torrent/ui/TorrentRow.tsx:25), `ServerResolver`, `useTorrentStore`. + +**Finding / importance / root cause:** Commands contain a torrent ID but no stable server identity. Background resolution uses the current server, while poll completion publishes results without checking the server generation. Configuration-array indexes are not durable identities. + +**Proof:** Executing the current background module with controlled clients produced snapshots `B → A` after switching A to B. A subsequent stale-A-row removal message for ID `7` invoked B’s remove method with `deleteData:false`. Numeric IDs can identify different torrents on different Transmission servers. + +**Falsification:** A production invariant binding displayed rows, commands, and responses to immutable server identities would disprove the routing defect. None was found; clearing `activeClient` did not prevent the reproduction. + +**Treatment:** Minimum: stable server IDs, generation-tagged responses, stale-result rejection, display reset, and explicit command targets. Ambitious: a complete per-server operation/state manager. **Preferred:** the minimum coherent ownership change, with serialized polling. + +**Files/subsystems:** Background, server configuration/resolver, message types, UI store, row actions, hydration. +**Dependencies:** Agreed identity/migration contract. +**Acceptance / verification:** Delayed A cannot update B; stale commands are rejected or explicitly target A; overlapping numeric IDs never cross-route. Test controlled delays and two real servers. +**Scope:** L. +**Deferral:** Wrong-server queue operations remain possible. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-STATE-002 — Positional diffs do not preserve torrent identity + +**Priority:** P1 +**Type:** State correctness +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** [TorrentDiffer.ts](/E:/Citadel/CTRL/extension/src/shared/lib/diff/TorrentDiffer.ts:33), `applyTorrentPatches`, `useTorrentStore`. + +**Finding / root cause:** The generator matches by ID but writes modifications to the new positional index without moving the original item. Add/remove operations can overwrite and then delete the same slot. + +**Proof:** `[A10,B20] → [B25,A10]` yielded `[A25,B20]`; `[A] → [B]` yielded an empty record; removing A from `[A,B]` left B at an index inconsistent with the new count. + +**Why it matters:** Queue identity/order and visible status become unreliable. This alone does not prove file deletion. + +**Falsification:** A sorting/identity invariant preventing all replacements, removals, and reorderings would be needed. Real queue operations invalidate that assumption. + +**Treatment:** Minimum: full viewport snapshots on identity/order changes. Ambitious: ID-keyed entities plus ordered ID lists and versioned patches. **Preferred:** snapshots first; retain diffing only after measured need. + +**Files:** Diff, viewport manager, store. +**Dependencies:** AST-STATE-001’s identity contract. +**Acceptance / verification:** Applying updates equals the complete expected list for reorder, insertion, deletion, replacement, and metadata changes. +**Scope:** M. +**Deferral:** Corrupt display survives passing unit tests. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-STATE-003 — Viewport synchronization has one global subscriber baseline + +**Priority:** P1 +**Type:** Multi-context / lifecycle correctness +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** [ViewportManager.ts](/E:/Citadel/CTRL/extension/src/features/torrent-control/services/ViewportManager.ts:14), [useTorrentPoller.ts](/E:/Citadel/CTRL/extension/src/features/torrent-control/model/useTorrentPoller.ts:48). + +**Finding / root cause:** One viewport and previous slice serve all windows. Unchanged slices emit nothing, including when total count changes outside the slice. Disconnect handling logs without reconnecting. + +**Proof:** A second same-range subscription emitted zero messages; increasing total count from one to two outside the viewport also emitted zero messages. + +**Importance:** New windows can lack an initial snapshot; windows interfere with one another; restarted backgrounds can leave stale displays. + +**Falsification:** A guaranteed independent initial snapshot/subscription baseline would invalidate the new-window concern. No such protocol was found. + +**Treatment:** Minimum: explicit initial snapshot and resynchronization, total-count updates, reconnect handling. Ambitious: subscriber-specific ranges/revisions. **Preferred:** independently synchronized snapshots before optimized subscriptions. + +**Files:** Viewport manager, poller, background ports, hydrator. +**Dependencies:** STATE-001/002. +**Acceptance / verification:** Two windows with different ranges remain correct through restart, reconnect, and offscreen list changes. +**Scope:** M. +**Deferral:** Intermittently empty/stale queues. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-SEC-001 — Vault state and decrypted snapshots diverge across contexts + +**Priority:** P1 +**Type:** Local security / configuration integrity +**Confidence:** CONFIRMED for lock divergence; HIGH-CONFIDENCE for stale-save consequences +**Evidence class:** SOURCE TRACE, LIVE BROWSER TEST +**Browsers:** Both by source; Chrome reproduced +**Evidence:** [useVault.ts](/E:/Citadel/CTRL/extension/src/features/torrent-control/model/useVault.ts:40), [useSettings.ts](/E:/Citadel/CTRL/extension/src/features/torrent-control/model/useSettings.ts:125), [options App.tsx](/E:/Citadel/CTRL/extension/src/entrypoints/options/App.tsx:35). + +**Finding / root cause:** Hooks keep separate decrypted snapshots without watching vault/session changes. Options overlays one snapshot onto another. Imports and concurrent edits can leave stale server arrays available for later writes. + +**Proof:** Removing the session key in one Chrome options context left another displaying its unlocked dashboard. Source shows settings reload does not refresh the independent vault snapshot. + +**Importance:** Locking does not consistently redact open UIs; stale configuration writes may overwrite newer changes. This is not evidence of remote password bypass. + +**Falsification:** Cross-context invalidation and revision-checked writes would disprove it; current subscriptions do not provide them. + +**Treatment:** Minimum: shared vault-state notifications, immediate redaction, revision-aware reload/save. Ambitious: all vault operations owned by background. **Preferred:** one authoritative vault interface with minimal UI-held secrets. + +**Files:** Vault hooks, settings hooks, options composition, import/save paths. +**Dependencies:** STATE-001 identity scheme. +**Acceptance / verification:** Lock all windows; import/edit in one refreshes others; stale saves are rejected. +**Scope:** L. +**Deferral:** Misleading security boundary and lost edits. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-SEC-002 — Missing vault ciphertext is accepted as an empty valid vault + +**Priority:** P1 +**Type:** Corruption handling / data integrity +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** [VaultService.ts](/E:/Citadel/CTRL/extension/src/shared/api/security/VaultService.ts:54). + +**Finding / root cause:** Initialization is inferred from salt alone. `getServers()` returns `[]` when ciphertext is absent; unlock treats that return as successful password verification. Salt and ciphertext are written separately. + +**Proof:** Initialize with synthetic data, lock, remove ciphertext while retaining salt, then supply an incorrect password: unlock returns true and servers are empty. + +**Importance:** Interrupted writes/corruption can masquerade as successful unlock and invite destructive replacement. Missing credentials were not decrypted. + +**Falsification:** Requiring a valid authenticated envelope before accepting initialization/unlock would prevent the reproduced result. + +**Treatment:** Minimum: fail closed on incomplete state and validate decrypted shape. Ambitious: versioned atomic vault envelope plus recovery copy. **Preferred:** versioned envelope, explicit corruption state, non-destructive recovery. + +**Files:** VaultService, SecurityService integration, setup/unlock UI, migration/import. +**Dependencies:** Defined migration/recovery behavior. +**Acceptance / verification:** Missing/truncated/wrong-type data never unlocks; wrong passwords fail; recovery preserves original data. +**Scope:** M. +**Deferral:** Configuration loss and false unlock success. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-SEC-003 — “Safe” export retains supported authentication material + +**Priority:** P1 +**Type:** Secret disclosure +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** [useSettings.ts](/E:/Citadel/CTRL/extension/src/features/torrent-control/model/useSettings.ts:209), BiglyBT Simple API configuration. + +**Finding / root cause:** Sanitization clears only the main password and HTTP-auth password while spreading all remaining fields. + +**Proof:** Running the actual export function with synthetic BiglyBT configuration retained `clientOptions.simpleApiKey` in a file labeled safe. Passthrough fields and credential-bearing URLs are not comprehensively classified either. + +**Importance:** A user sharing a supposedly safe configuration may disclose credentials. + +**Falsification:** Removing the key before serialization or proving it is unsupported/non-secret would falsify the specific case; current adapter code consumes it as an API key. + +**Treatment:** Minimum: allowlist non-secret exported fields and explicitly sanitize endpoint userinfo. Ambitious: typed per-client secret schemas and encrypted full backups. **Preferred:** allowlisted safe export now; clearly labeled sensitive full export. + +**Files:** Export/import schema, client configuration types, data-management UI. +**Dependencies:** Supported-client inventory. +**Acceptance / verification:** Synthetic secrets in every supported location never appear in safe exports; intentional full exports are clearly disclosed. +**Scope:** M. +**Deferral:** Credential leakage through normal sharing. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-NET-001 — Shared POST serialization breaks XML-RPC + +**Priority:** P1 +**Type:** Protocol correctness +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** [FetchHttpClient.ts](/E:/Citadel/CTRL/extension/src/shared/api/network/FetchHttpClient.ts:149), RuTorrentAdapter XML calls. + +**Finding / root cause:** Non-FormData/non-URLSearchParams bodies are JSON-stringified and labeled JSON, including XML strings. + +**Proof:** An XML request supplied with `text/xml` became a quoted JSON string with `application/json`. + +**Importance:** The rTorrent/ruTorrent XML path cannot produce its intended wire protocol. + +**Falsification:** A separate raw-body transport used by these calls would disprove it; the calls use this POST path. + +**Treatment:** Minimum: explicit raw-body and JSON APIs. Ambitious: a complete protocol-aware request abstraction. **Preferred:** a small typed transport contract retaining native BodyInit values and caller content types. + +**Files:** FetchHttpClient, RuTorrentAdapter, transport tests. +**Dependencies:** Decision whether ruTorrent remains in v1. +**Acceptance / verification:** Exact XML bytes/content type reach a loopback server, then pass a real configured XML-RPC endpoint. +**Scope:** M. +**Deferral:** Broken advertised client. +**Release classification:** **CONDITIONAL BLOCKER IF FEATURE IS ADVERTISED**. + +## AST-NET-002 — Cookie clients and Flood initialization lack a working session contract + +**Priority:** P1 +**Type:** Authentication / protocol correctness +**Confidence:** HIGH-CONFIDENCE; browser restrictions CONFIRMED +**Evidence class:** SOURCE TRACE, LIVE BROWSER TEST, OFFICIAL API DOCUMENTATION +**Browsers:** Both; Fetch behavior tested in Chrome +**Evidence:** FetchHttpClient; DelugeAdapter; UTorrentAdapter; FloodAdapter. + +**Finding / root cause:** Shared Fetch defaults to `credentials:'omit'`. Deluge relies on a session cookie. uTorrent attempts to read `Set-Cookie` and manually set `Cookie`. Flood’s cookie fallback shares the omission, and a fresh adapter does not log in before normal list operations. + +**Proof:** Chrome hid `Set-Cookie` and omitted manually supplied Cookie. Deluge’s own implementation creates and checks `_session_id`. Flood retries authentication only after `sessionVerified` is already true; factory construction does not initialize it. [Deluge authentication source](https://raw.githubusercontent.com/deluge-torrent/deluge/develop/deluge/ui/web/auth.py). + +**Falsification:** Browser-owned cookie sessions with appropriate credentials, or a verified token-only protocol and guaranteed login bootstrap, could overturn specific client failures. + +**Treatment:** Minimum: per-client authentication policy and initial-login contract. Ambitious: isolated session manager. **Preferred:** browser-managed cookies where required; do not add `cookies` permission merely to emulate Node HTTP. + +**Files:** Shared transport, these adapters, factory lifecycle. +**Dependencies:** Client/version scope and browser tests. +**Acceptance / verification:** Clean-profile login/list/reconnect works with default server security settings. +**Scope:** L. +**Deferral:** Authentication failures hidden by mocks. +**Release classification:** **CONDITIONAL BLOCKER IF FEATURE IS ADVERTISED**. + +## AST-NET-003 — qBittorrent support is not version-aware + +**Priority:** P1 +**Type:** Client compatibility +**Confidence:** HIGH-CONFIDENCE; endpoint mismatch CONFIRMED +**Evidence class:** SOURCE TRACE, OFFICIAL API DOCUMENTATION +**Browsers:** Both +**Evidence:** [QBittorrentAdapter.ts](/E:/Citadel/CTRL/extension/src/shared/api/clients/qbittorrent/QBittorrentAdapter.ts:209). + +**Finding / root cause:** Pause/resume calls target older endpoint names. The state mapper recognizes `pausedDL/pausedUP`, not the newer stopped states. Retrieved API-version information does not select a compatible implementation. + +**Proof:** qBittorrent 5.0 documents and implements `stop`/`start`; its controller declarations contain those actions rather than pause/resume. [Official API](https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-%28qBittorrent-5.0%29), [5.0 controller](https://raw.githubusercontent.com/qbittorrent/qBittorrent/release-5.0.0/src/webui/api/torrentscontroller.h). + +**Importance:** A successful login/list does not establish functioning queue control. + +**Falsification:** A supported deployed version providing compatibility aliases could reduce impact for that version. It would not justify unspecified version support. + +**Treatment:** Minimum: support explicit verified versions and map their endpoints/states. Ambitious: capability negotiation covering advanced APIs. **Preferred:** a small version adapter for advertised core operations. + +**Files:** qBittorrent adapter/schema/tests and support documentation. +**Dependencies:** Chosen supported versions. +**Acceptance / verification:** Real supported versions pass pause/resume/state-display tests. +**Scope:** M. +**Deferral:** Broken core actions on advertised configurations. +**Release classification:** **CONDITIONAL BLOCKER IF FEATURE IS ADVERTISED**. + +## AST-NET-004 — CSRF handling relies on headers Fetch does not permit callers to control + +**Priority:** P2 +**Type:** Browser networking compatibility +**Confidence:** CONFIRMED limitation; PLAUSIBLE — VERIFY client impact +**Evidence class:** SOURCE TRACE, LIVE BROWSER TEST +**Browsers:** Both potentially; Chrome tested +**Evidence:** QBittorrentAdapter `makeRequest`; FetchHttpClient. + +**Finding / root cause:** Code tries to set server-origin Origin/Referer as though Fetch were a general HTTP client. + +**Proof:** The extension-origin loopback request carried the browser’s extension Origin, not the supplied server Origin; manual Referer/Cookie were absent. Explicit Authorization remained present. + +**Importance:** qBittorrent/proxy CSRF configurations may reject requests despite unit-test success. + +**Falsification:** A host-permission-granted, non-localhost live test with normal server CSRF settings could show the supported configuration needs no workaround. That test remains missing. + +**Treatment:** Minimum: remove false assumptions and establish the actual supported browser/server contract. Ambitious: browser-specific header intervention only if necessary and permitted. **Preferred:** evidence first; avoid adding DNR/webRequest or weakening server security speculatively. + +**Files:** Transport and qBittorrent adapter. +**Dependencies:** Live server environment. +**Acceptance / verification:** Authenticated core operations work with documented security settings in both browsers. +**Scope:** S investigation; repair unknown. +**Deferral:** Unbounded compatibility claims. +**Release classification:** **CONDITIONAL BLOCKER IF FEATURE IS ADVERTISED**. + +## AST-NET-005 — Cancellation and response-body timeouts are incomplete + +**Priority:** P2 +**Type:** Reliability / resource management +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** [FetchHttpClient.ts](/E:/Citadel/CTRL/extension/src/shared/api/network/FetchHttpClient.ts:43). + +**Finding / root cause:** The transport overwrites the caller’s signal and clears its timeout before consuming the response body. Adapter timeout wrappers can reject without stopping underlying work. + +**Proof:** An already-aborted caller signal became a fresh non-aborted request signal. Source places timeout clearing before `response.text()`. + +**Importance:** Slow requests can outlive cancellation, overlap polling, and publish stale results. + +**Falsification:** A caller-independent cancellation mechanism covering body consumption would invalidate this; none was found. + +**Treatment:** Minimum: combine caller cancellation with transport timeout and keep it active through body processing. Ambitious: operation-level cancellation and retry budgets. **Preferred:** shared transport repair plus generation rejection. + +**Files:** FetchHttpClient, adapter timeout wrappers, poll orchestration. +**Dependencies:** STATE-001. +**Acceptance / verification:** Pre-abort, mid-body stall, switch, and lock terminate or safely discard work; timers are cleaned up. +**Scope:** M. +**Deferral:** Resource waste and delayed failure. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-UX-001 — Connectivity and command outcomes are misleading + +**Priority:** P1 +**Type:** Core UX correctness +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, LIVE BROWSER TEST +**Browsers:** Both; Chrome reproduced +**Evidence:** [TorrentDashboard.tsx](/E:/Citadel/CTRL/extension/src/features/torrent-control/ui/TorrentDashboard.tsx:92), VirtualizedTorrentList, TorrentRow. + +**Finding / root cause:** “Connection: Online” and “LIVE” are unconditional. Zero rows means “server empty” regardless of configuration/connectivity. Optimistic pause/resume ignores command results. + +**Proof:** Fresh setup with no configured server displayed online/empty-server messaging. + +**Importance:** Users cannot distinguish success, stale data, lack of configuration, or failure. + +**Falsification:** Binding those surfaces to verified connection/operation state would prevent the observed behavior. + +**Treatment:** Minimum: explicit unconfigured/locked/loading/connected/stale/error states and command acknowledgement with rollback. Ambitious: centralized operation history. **Preferred:** truthful states and actionable errors only. + +**Files:** Dashboard, list, row actions, store, background response contract. +**Dependencies:** STATE-001–003. +**Acceptance / verification:** No server or failed request never appears connected; failed actions remain visible and recoverable. +**Scope:** M. +**Deferral:** Users act on false status. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-UX-002 — Global add-paused behavior differs by entrypoint + +**Priority:** P1 +**Type:** Behavioral consistency +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE +**Browsers:** Both +**Evidence:** [ContextMenuService.ts](/E:/Citadel/CTRL/extension/src/features/torrent-control/model/services/ContextMenuService.ts:360), background add handler, AddTorrentDialog. + +**Finding / root cause:** Normal runtime add applies the global default. Context-menu adds call adapters directly without it. Page scanning does likewise. The advanced dialog resets paused to false and submits that explicit value. + +**Importance:** “Always add paused” can unexpectedly start a transfer, with bandwidth and peer-contact consequences. + +**Proof:** The menu supplies `{}` or no options; only its explicit Add Paused action passes true. The background default is bypassed. + +**Falsification:** A common downstream policy applying the global preference would invalidate this. Adapters do not have that global context. + +**Treatment:** Minimum: one add-command policy with explicit overrides. Ambitious: capability-aware add planning/preview. **Preferred:** centralize defaults; hide bulk scan until behavior is clear. + +**Files:** Background command layer, context menu, add dialog. +**Dependencies:** Client paused capability tests. +**Acceptance / verification:** Every add entrypoint honors the same default and explicit override. +**Scope:** M. +**Deferral:** Unexpected transfer activity. +**Release classification:** **CONDITIONAL BLOCKER IF FEATURE IS ADVERTISED**. + +## AST-UX-003 — Endpoint editing cannot reliably represent supported URL forms + +**Priority:** P2 +**Type:** Configuration correctness +**Confidence:** CONFIRMED parsing limitation; deployment impact HIGH-CONFIDENCE +**Evidence class:** SOURCE TRACE +**Browsers:** Both +**Evidence:** [ServerConfigPanel.tsx](/E:/Citadel/CTRL/extension/src/features/torrent-control/ui/ServerConfigPanel.tsx:231), adapter URL constructors. + +**Finding / root cause:** String splitting on `:` conflates host/port/path and breaks IPv6 representation. Adapter base-path behavior also differs: Transmission forces `/transmission/rpc`; uTorrent appends `gui/`; others use relative paths. + +**Importance:** Valid reverse-proxy/subpath configurations can be silently altered or sent to the wrong path. + +**Falsification:** Round-trip tests preserving those URL forms would disprove the editor concern; the displayed parsing cannot preserve IPv6 as written. + +**Treatment:** Minimum: a complete endpoint URL field with `URL` validation and per-client guidance. Ambitious: endpoint discovery. **Preferred:** explicit endpoints, no discovery for v1. + +**Files:** Server panel, permission helper, adapter constructors. +**Dependencies:** Supported endpoint contract. +**Acceptance / verification:** IPv4, DNS, IPv6, ports, HTTPS, and supported subpaths round-trip exactly. +**Scope:** M. +**Deferral:** Setup failures and support burden. +**Release classification:** **CONDITIONAL BLOCKER IF FEATURE IS ADVERTISED**. + +## AST-UX-004 — Core server configuration lacks accessible control labeling + +**Priority:** P2 +**Type:** Accessibility +**Confidence:** HIGH-CONFIDENCE +**Evidence class:** SOURCE TRACE +**Browsers:** Both +**Evidence:** [ServerConfigPanel.tsx](/E:/Citadel/CTRL/extension/src/features/torrent-control/ui/ServerConfigPanel.tsx:201), virtualized list semantics. + +**Finding / root cause:** Several labels are adjacent text without `htmlFor`, wrapping, or equivalent accessible names. The virtual list mixes list semantics with row-count attributes and lacks clear item position/set size. + +**Importance:** Server setup and queue navigation may be difficult with assistive technology. + +**Proof:** Source association gaps are explicit. A full screen-reader audit was not performed. + +**Falsification:** Browser accessibility-tree inspection showing correct names and navigation would narrow the finding. + +**Treatment:** Minimum: associated labels, descriptive errors, correct list semantics, keyboard acceptance. Ambitious: comprehensive WCAG audit. **Preferred:** repair core workflows now, then audit the broader surface. + +**Files:** Server panel, list, dialogs, settings toggles. +**Dependencies:** Final v1 UI scope. +**Acceptance / verification:** Keyboard-only setup/control and a screen-reader pass succeed; focus survives errors/dialog close. +**Scope:** M. +**Deferral:** Excludes some users from configuration. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR**. + +## AST-SCOPE-001 — Visible and declared capabilities exceed implemented behavior + +**Priority:** P2 +**Type:** Product scope / least privilege +**Confidence:** CONFIRMED for identified consumers +**Evidence class:** SOURCE TRACE, STATIC CONFIGURATION, DOCUMENTATION CLAIM +**Browsers:** Both +**Evidence:** Function/notification/layout settings, translation script, manifest. + +**Finding / root cause:** Notification style/level previews, layout preferences, and other surfaces lack corresponding runtime behavior. Performance mode is explicitly locked. Localization mixes hardcoded English with partial dictionaries; the translation script creates prefixed English placeholders. WebSocket host permissions have no live WebSocket client consumer. + +**Importance:** Unsupported controls and broad claims increase review/support burden. + +**Falsification:** Actual runtime consumers and complete language walkthroughs would promote individual features; saving a preference alone is insufficient. + +**Treatment:** Minimum: hide unimplemented controls, remove unused WebSocket declarations, narrow claims. Ambitious: finish the features. **Preferred:** scope reduction. + +**Files:** Settings, navigation, locale workflow, manifest, documentation. +**Dependencies:** Operator scope decision. +**Acceptance / verification:** Every visible setting has observable behavior; every declared permission supports retained functionality. +**Scope:** M. +**Deferral:** Misleading product surface and avoidable permission scrutiny. +**Release classification:** **CONDITIONAL BLOCKER IF FEATURE IS ADVERTISED**. + +## AST-FF-001 — Firefox package lacks mandatory submission metadata + +**Priority:** P0 +**Type:** Store submission +**Confidence:** CONFIRMED +**Evidence class:** STATIC CONFIGURATION, BUILD ARTIFACT, PACKAGE INSPECTION, OFFICIAL POLICY +**Browsers:** Firefox +**Evidence:** [wxt.config.ts](/E:/Citadel/CTRL/extension/wxt.config.ts:14), packaged manifest, `addons-linter@10.10.0`. + +**Finding / root cause:** No Gecko add-on ID or data-collection declaration is generated. + +**Proof:** Validator error `ADDON_ID_REQUIRED`; warning `MISSING_DATA_COLLECTION_PERMISSIONS`. The latter being a warning does not waive the new-submission requirement. + +**Importance:** Current ZIP fails the signing/submission prerequisite. [Mozilla manifest documentation](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_specific_settings). + +**Falsification:** Inspecting a different release artifact with correct metadata could supersede this finding; the inspected ZIP has none. + +**Treatment:** Minimum: permanent operator-owned ID, accurate data declarations, and supported-version policy. Ambitious: older-browser consent fallback. **Preferred:** desktop baseline using built-in consent. + +**Files:** WXT manifest generation, consent handling, reviewer notes. +**Dependencies:** Publisher ID and data-flow decisions. +**Acceptance / verification:** Official validator has no errors; install consent matches actual behavior; signed Firefox smoke succeeds. +**Scope:** M. +**Deferral:** Firefox submission remains blocked. +**Release classification:** **BLOCKS FIREFOX**. + +## AST-PRIV-001 — Privacy statements contradict necessary product data flows + +**Priority:** P1 +**Type:** Privacy / disclosure accuracy +**Confidence:** CONFIRMED +**Evidence class:** SOURCE TRACE, DOCUMENTATION CLAIM, OFFICIAL POLICY +**Browsers:** Both +**Evidence:** [PRIVACY_POLICY.md](/E:/Citadel/CTRL/docs/PRIVACY_POLICY.md:14), `docs/privacy.html`, network and hydration code. + +**Finding / root cause:** Claims that credentials never leave the device and links are not sent externally conflict with remote-client operation. Torrent metadata is session-cached. “Only operates when explicitly interacted with” overlooks autonomous configured-client polling. + +**Importance:** Users and reviewers receive an inaccurate explanation even though no developer telemetry was found. + +**Proof:** Adapters transmit credentials/links; background polls; StateHydrator stores torrent records. Later exceptions in the policy do not repair contradictory absolutes. + +**Falsification:** Removing those data flows would alter the product substantially; otherwise the statements must change. + +**Treatment:** Minimum: accurate policy, in-product explanation, permission justification, and store declarations. Ambitious: redesign transmission consent per feature. **Preferred:** truthful disclosure matching narrowed scope. + +**Files:** Both policies, onboarding, manifest declarations, listing materials. +**Dependencies:** Final scope and HTTP decision. +**Acceptance / verification:** Every data-inventory row maps to consistent policy/UI/store statements. +**Scope:** M. +**Deferral:** Misleading disclosures and review risk. +**Release classification:** **BLOCKS BOTH STORES** for the proposed submission materials; not an automated manifest error. [Chrome privacy policy](https://developer.chrome.com/docs/webstore/program-policies/privacy). + +## AST-BUILD-001 — Competing source-package paths do not establish a reproducible release + +**Priority:** P1 +**Type:** Release provenance / packaging +**Confidence:** CONFIRMED defects; rebuild result UNKNOWN +**Evidence class:** SOURCE TRACE, BUILD ARTIFACT, PACKAGE INSPECTION +**Browsers:** Both artifacts; Firefox review especially affected +**Evidence:** [generate-build-info.ts](/E:/Citadel/CTRL/extension/scripts/generate-build-info.ts:10), [zip-source.ts](/E:/Citadel/CTRL/extension/scripts/zip-source.ts:14), existing source ZIP. + +**Finding / root cause:** Normal builds rewrite tracked timestamps. `wxt zip` bypasses that script. Automatic source ZIP includes backups/reports. The authored source script uses a Windows-sensitive root-string comparison, demands a clean tree, and archives HEAD rather than arbitrary build inputs. + +**Proof:** Source ZIP contains 183 backup files, two audit files, a build log, and a browser-report HTML. The loaded build displays a March build stamp. Canonical build metadata uses current time and locale. + +**Importance:** Reviewer reconstruction and release provenance are unreliable. + +**Falsification:** A clean source ZIP and documented repeatable command producing matching files would supersede the current evidence. + +**Treatment:** Minimum: one allowlisted source/package path, fixed build inputs, portable path handling, complete instructions. Ambitious: hermetic build tooling. **Preferred:** simple deterministic build from a frozen source revision. + +**Files:** Build/ZIP scripts, WXT config, release CI/docs. +**Dependencies:** Final source, versions, metadata. +**Acceptance / verification:** Rebuild the source ZIP in a clean environment; compare file sets/content with the submission package. +**Scope:** L. +**Deferral:** Unreviewable or unverifiable release. +**Release classification:** **BLOCKS FIREFOX** until reviewer rebuild evidence exists. + +## AST-PERF-001 — Distribution contains substantial duplicated font payload + +**Priority:** P2 +**Type:** Packaging / performance +**Confidence:** CONFIRMED +**Evidence class:** PACKAGE INSPECTION, BUILD ARTIFACT +**Browsers:** Both +**Evidence:** Chrome/Firefox unpacked assets and content hashes. + +**Finding / root cause:** 1,058 fonts occupy 37,816,320 bytes. Only 530 unique font contents exist; **18,868,316 bytes are duplicate font data**. + +**Importance:** Larger installs, updates, and review artifacts. Startup latency or memory regressions were not measured. + +**Falsification:** Hash comparison already establishes duplication; a different build can remove it. + +**Treatment:** Minimum: remove duplicate font-copy/import routes and retain required weights/scripts. Ambitious: replace styling systems. **Preferred:** asset deduplication; preserve framework/UI choices. + +**Files:** Font/CSS imports, Carbon/Plex integration, build asset handling. +**Dependencies:** Language scope. +**Acceptance / verification:** No duplicate-content font emission; visual checks for required glyphs; package-size baseline recorded. +**Scope:** M. +**Deferral:** Wasteful distribution, not an identified store size violation. +**Release classification:** **ACCEPTABLE POST-LAUNCH**, preferably addressed while packaging is repaired. + +## AST-TEST-001 — Existing verification misses demonstrated severe failure classes + +**Priority:** P2 +**Type:** Test / release assurance +**Confidence:** CONFIRMED +**Evidence class:** AUTOMATED TEST, SOURCE TRACE, RUNTIME REPRODUCTION +**Browsers:** Both +**Evidence:** 530 passing tests; tests, Playwright fixtures, CI workflow. + +**Finding / root cause:** Adapter mocks bypass browser request semantics; lifecycle/state/vault integration is sparse. Chromium E2E includes paths that skip when vault state is absent. Firefox runtime and source-rebuild gates are absent. + +**Importance:** Green CI currently coexists with the reproduced defects. + +**Falsification:** Existing tests that fail on those exact sequences would narrow the gap; the executed suite passed. + +**Treatment:** Minimum: targeted contracts for the demonstrated classes and both-browser smoke. Ambitious: broad integration farm. **Preferred:** a small failure-focused release suite. + +**Files:** Unit/integration/browser tests, CI, package validation. +**Dependencies:** Stable message/transport/vault contracts. +**Acceptance / verification:** Tests fail against current defects and pass after repairs; required scenarios cannot silently skip. +**Scope:** L, spread across repair waves. +**Deferral:** Regressions recur undetected. +**Release classification:** **BLOCKS RESPONSIBLE QUALITY BAR** for missing critical evidence. + +## AST-DEP-001 — Dependency advisories require triage, not indiscriminate upgrades + +**Priority:** P2 +**Type:** Supply-chain maintenance +**Confidence:** CONFIRMED advisory result; runtime exploitability NOT ESTABLISHED +**Evidence class:** STATIC CONFIGURATION, AUTOMATED TEST, REASONED INFERENCE +**Browsers:** Build/review environment primarily +**Evidence:** Fresh `npm audit`, lockfile classifications. + +**Finding / root cause:** The latest response reports 15 affected package nodes, including development tooling and propagated dependencies. Earlier cached/offline results were not reliable. + +**Importance:** Build tools process source, archives, and assets; development-only status does not make all advisories irrelevant. + +**Falsification:** Reachability analysis and patched/withdrawn advisories can resolve individual entries. + +**Treatment:** Minimum: advisory-by-advisory reachability and bounded fixes. Ambitious: broad upgrades. **Preferred:** patch compatible vulnerable tools and test overrides; do not apply `npm audit fix --force`. + +**Files:** Package/lockfile, overrides, build workflow. +**Dependencies:** Frozen supported toolchain. +**Acceptance / verification:** Each retained advisory has a documented disposition; patches preserve builds/tests/reproducibility. +**Scope:** M. +**Deferral:** Toolchain exposure and unresolved review questions. +**Release classification:** **ACCEPTABLE POST-LAUNCH only with explicit reachability disposition**; not evidence of a remotely exploitable shipped extension. + +# Networking and Supported Clients + +**No row below means “live verified working.”** + +| Client | Auth / transport / body | Current confidence | Browser verification | Main risk | v1 recommendation | +|---|---|---|---|---|---| +| qBittorrent | HTTP form login, browser SID cookie, JSON responses, multipart add | Plausible after repairs | Generic Chrome Fetch only | Older actions/states; CSRF/proxy behavior; auxiliary shared-client calls | Candidate core client after version-specific tests | +| Transmission | HTTP JSON RPC, explicit Basic, 409 session-header negotiation | Strongest static candidate | Explicit Basic header survived generic probe | Fixed root RPC path; numeric IDs; timeout/state races | First client to verify | +| Deluge | HTTP JSON RPC; password login plus `_session_id` | Not supportable as currently evidenced | Cookie restrictions confirmed | Shared credentials omission | Hide until session repair/live tests | +| ruTorrent/rTorrent | HTTP XML-RPC, optional Basic | Broken serialization confirmed | Exact body probe | JSON-encoded XML; endpoint convention also needs verification | Hide until protocol repair | +| Flood | HTTP JSON, Bearer when returned or cookie session | Unverified; bootstrap defect traced | Generic browser semantics only | Fresh adapter not authenticated; cookie fallback | Experimental | +| Aria2 | HTTP JSON-RPC, `token:` in parameters | Plausible basic support | No real client | Endpoint input; bounded list windows; shared timeout | Optional candidate after core tests | +| BiglyBT | Transmission-like JSON RPC; Basic; optional Simple API key | Unverified | None against client | Multiple protocol modes, query key, endpoint/timeouts | Experimental | +| uTorrent | Basic + token HTML + GUID cookie; query actions | Browser session mechanism defective | Set-Cookie/Cookie restriction confirmed | Token/cookie handling; `/gui/` construction | Hide | +| Vuze Remote UI | Transmission-derived implementation | Unverified inheritance, not independent compatibility evidence | None | Plugin/version assumptions | Hide until plugin-specific tests | + +Across clients: + +- Relative URL resolution means trailing slashes and leading-path choices are observable protocol behavior. +- Redirects, HTTPS downgrade, cross-origin redirects, self-signed certificates, reverse proxies, and authentication stripping were not live tested. +- Shared timeout is generally 10 seconds; some adapters request different limits or add outer timeout wrappers. Those wrappers do not consistently cancel underlying work. +- Retries must distinguish safe reads, authentication negotiation, and mutation outcomes. A timeout after submission may mean “outcome unknown,” not “safe to add again.” +- File-add adapter methods exist, but the visible core workflow is principally URL/magnet-based; do not advertise complete file upload without UI/browser acceptance. +- No live WebSocket transport consumer was found. +- A successful “Test connection” must not be treated as proof that subsequent freshly created clients authenticate or that all queue operations work. + +# Privacy, Security, and Permissions + +## Data inventory + +| Data | Source | Stored / location | Transmitted / destination | Purpose | User control | +|---|---|---|---|---|---| +| Client URL | Configuration/import | Encrypted vault, decrypted UI memory | Determines configured server destination | Connection | Edit/remove server | +| Username/password | Configuration/import | Encrypted vault; session-decrypted copies | Login/Basic auth to client | Authentication | Edit, lock, remove; full export | +| API keys | Client options/import | Encrypted vault; unsafe safe-export residue | Client RPC/query protocol | Authentication | Edit/remove; export needs repair | +| Master password | User entry | Not intentionally persisted | No transmission found | Derive encryption key | Unlock/reset | +| Derived key | WebCrypto | `storage.session` JWK | Extension storage boundary only | Decryption across contexts | Lock/browser-session end | +| Magnets/torrent URLs | Popup, selection, link, scan | Transient handling; client may retain | User-configured client | Add torrent | Explicit action; scan behavior needs clarification | +| Page links | Explicit main-document scan | Transient extracted list | Matching magnets sent to client | Bulk add | Context-menu invocation | +| Browsing history | Browser | No passive history collection found | None found | Not required | No history permission | +| Torrent names/status/paths | Client response | UI state and session torrent cache | Client requests/commands; no developer destination found | Queue display/control | Server selection; lock/cache behavior needs repair | +| Preferences | User settings | Local storage; backup JSON | No automatic developer transmission found | UI behavior | Settings/import/export | +| Diagnostics | Browser/runtime/errors | Console/local UI; manual exports where applicable | No automatic reporting endpoint found | Troubleshooting | Debug surfaces; redaction review needed | +| External resources | Utilities/About links | Packaged URLs | Browser navigation when clicked | Auxiliary resources/support | User click | +| Telemetry/crash reporting | — | No implementation found | No endpoint found | — | Preserve absence | + +Security boundaries are reasonably narrow: no external-message listener, no persistent content script, no web-accessible-resource surface, and sender IDs are checked. Page-derived strings become client inputs rather than executable extension code. + +AES-GCM with random IVs and PBKDF2-SHA-256 with 300,000 iterations is a sensible existing foundation. It does not protect unlocked UI memory, explicitly exported plaintext backups, or a compromised browser profile. The report’s vault findings concern lifecycle and integrity, not a demonstrated break of the cryptography. + +Logging still deserves targeted redaction: invalid factory configuration can be logged as an object, and qBittorrent logging includes endpoint/username information. No automatic remote log delivery was found. Historical secret revocation and every Git reference were not independently verified. + +## Permission matrix + +Warning descriptions are functional summaries, not claims of exact localized browser prompt text. + +| Permission/pattern | Required? | Actual feature/use | Narrower alternative | User warning / scrutiny | +|---|---|---|---|---| +| `storage` | Required | Preferences, vault, session cache | None for retained design | Storage handling must be disclosed | +| `contextMenus` | Required | Explicit link/selection/page actions | Remove feature | Generally low scrutiny when explained | +| `notifications` | Required | Add/error notifications | Optional permission or inline feedback | Notification capability | +| `activeTab` | Required | User-triggered page scan | Remove scan | Temporary active-page access | +| `scripting` | Required | Execute magnet-link scan | Remove scan | Explain exactly what page content is read | +| `alarms` | Required | Idle polling | Stop idle monitoring | Explain background operation | +| `http://*/*` | Optional host | Arbitrary user-configured HTTP clients | Request chosen origins only; already intended | Access to selected website/server | +| `https://*/*` | Optional host | Arbitrary HTTPS clients | Same | Defensible broad declaration, narrow runtime grants | +| `ws://*/*`, `wss://*/*` | Optional host | No live consumer found | Remove | Unjustified future capability | +| Required host permissions | None | — | Preserve | No blanket install-time all-host access found | +| Content-script matches | None | — | Preserve | No passive all-site injection | +| `cookies`, `tabs`, DNR, `webRequest` | Absent | Not granted | Preserve unless proven necessary | Do not add as speculative transport fixes | +| Web-accessible resources | Absent | — | Preserve | Smaller page boundary | + +Optional broad host patterns are understandable for arbitrary user-owned servers. They are not equivalent to granting access to every host at installation. Unused optional patterns still need removal under least privilege. + +# Browser and Store Requirements + +## Cross-browser assessment + +| Area | Chromium | Firefox | CTRL / evidence | Required action | +|---|---|---|---|---| +| Background | MV3 service worker | Background scripts/event-page model | WXT generates appropriate differences | Preserve; test independent restarts | +| Runtime namespace | `chrome` APIs | Compatibility namespace plus browser APIs | Mixed usage; basic Chrome surface works | Both-browser contracts, not namespace rewrite | +| Ports/lifecycle | Worker may stop; an idle open port is not a universal keepalive | Different background lifetime | Polling/ports/cache lack resync ownership | STATE-001–003 | +| Storage | Session memory supported | Session API version support matters | Current key avoids disk fallback | Set baseline; test browser restart | +| Alarms | Wake mechanism | Supported with lifecycle differences | One-minute idle polling | Verify recreate/wake/unlock | +| Host permissions | User-granted endpoint access | Version-dependent prompting/revocation behavior | Optional patterns present | Grant/deny/revoke tests | +| CSP | Local scripts only | Explicit policy affects insecure-request upgrading | No `unsafe-eval` allowance; explicit HTTP connectivity | Retain restrictive script policy | +| Fetch | Browser controls forbidden headers/cookies | Same fundamental Fetch constraints; extension exceptions differ | Generic Chrome proof only | Live client matrix | +| Packaging | Unpacked build available | ZIP fails ID validation | Genuine WXT targets | Complete release pipeline | +| Signing/declarations | Store dashboard requirements | Gecko ID/data declarations/reviewer source | Firefox incomplete | FF-001, BUILD-001 | + +CTRL is **a shared cross-browser implementation with a functioning Chrome surface and an unverified Firefox runtime**, not yet an evidenced dual-browser product. + +Chrome’s service-worker documentation does not justify “the worker always dies after 30 seconds”: API activity and messaging affect lifetime. Conversely, opening a port alone does not guarantee persistence. [Chrome lifecycle documentation](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle). + +Firefox’s explicit CSP can permit the intended HTTP connection behavior; automatic HTTPS upgrading must not be assumed from MV3 alone. Actual transport policy remains a separate question. [MDN extension CSP](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_Security_Policy). + +## Chrome Web Store + +**Automated validation blockers:** none established from a current CWS upload. The existing unpacked manifest loads in Chrome. This is not a completed store validation. + +**Human review risks:** unsupported client claims, inaccurate status/privacy language, unused WebSocket permissions, unclear scan behavior, incomplete reviewer setup, and unfinished surfaces. + +**Policy issue:** current privacy wording does not accurately describe the product. Chrome’s published privacy policy calls for an accurate explanation of handled data and recipients. [Chrome privacy requirements](https://developer.chrome.com/docs/webstore/program-policies/privacy). + +**Important exception:** Chrome’s FAQ explicitly discusses protocol clients connecting to user-specified servers and exempts same-computer native communication from its encryption requirement. Therefore, I do **not** classify every HTTP/LAN configuration as a proven Chrome violation. Whether all retained CTRL flows fit the protocol-client exception should be documented rather than assumed. [Chrome user-data FAQ, questions 14–16](https://developer.chrome.com/docs/webstore/program-policies/user-data-faq). + +**Defensible scrutiny:** user-triggered page-link access and optional endpoint host access support the stated purpose. Remote RPC responses are data, not remote-hosted executable code. No first-party remote-code loader was found. [MV3 requirements](https://developer.chrome.com/docs/webstore/program-policies/mv3-requirements). + +The dashboard needs a purpose statement, permission justifications, accurate data practices, and applicable certifications. Authentication information and website content are relevant to the observed flows; “no developer backend” is insufficient reasoning for blanket “no data” answers. Exact dashboard selections must be reconciled with the chosen scope and current form definitions. [Privacy dashboard](https://developer.chrome.com/docs/webstore/cws-dashboard-privacy). + +Publisher account/verification status is unknown. Two-step verification and trader/non-trader declarations must be checked by the operator; this audit cannot choose their legal status. [Two-step verification](https://developer.chrome.com/docs/webstore/program-policies/two-step-verification), [trader declarations](https://developer.chrome.com/docs/webstore/program-policies/trader-verification-faq). + +Torrent control is not itself evidence of prohibited content. Listings and auxiliary links should avoid promoting infringement; no categorical torrent-client prohibition was established. [CWS policies](https://developer.chrome.com/docs/webstore/program-policies/policies). + +## Firefox / AMO + +**Hard blocker:** the official validator rejects the missing MV3 add-on ID. + +**Data declarations:** new submissions since November 3, 2025 must use Mozilla’s built-in declaration system. Its taxonomy includes usernames/passwords as `authenticationInfo` and page links as `websiteContent`. Transmission includes handling outside the add-on/local browser; no general exemption for user-configured self-hosted servers was found. Local encrypted storage alone should not be conflated with remote transmission. Desktop Firefox 140 provides the built-in consent baseline. [Mozilla data-consent requirements](https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/). + +**HTTP policy:** Mozilla requires secure remote data transmission. Whether a specific local/LAN deployment qualifies for an exception remains unresolved; do not silently assume Chrome’s exception transfers to AMO. [Mozilla add-on policies](https://extensionworkshop.com/documentation/publish/add-on-policies/). + +**Source review:** bundled/minified code requires matching source and build instructions. Reviewers rebuild and compare generated content; the documented requirement is matching output, not necessarily byte-identical ZIP metadata. The current source/build paths do not establish this. [Source submission](https://extensionworkshop.com/documentation/publish/source-code-submission/). + +**Validator result** + +| Code | Count | Interpretation | +|---|---:|---| +| `ADDON_ID_REQUIRED` | 1 error | Hard metadata defect | +| `MISSING_DATA_COLLECTION_PERMISSIONS` | 1 warning | Required for the intended new-submission path | +| `DANGEROUS_EVAL` | 2 warnings | `reflect-metadata` global-object fallbacks | +| `UNSAFE_VAR_ASSIGNMENT` | 2 warnings | React DOM implementation | + +Modern browsers provide `globalThis`, bypassing the identified reflect fallback. React’s internal `innerHTML` code is not proof of an unsafe application data flow. Record provenance and reviewer explanations; do not loosen CSP or rewrite React merely to eliminate warning text. + +# Build, Packages, Dependencies, and Performance + +## Source-to-package chain + +| Operation | Current command/behavior | +|---|---| +| Canonical build | `npm run build`: clean → Chrome → Firefox → source backup | +| Chrome | `npm run build:chrome`: generate tracked build metadata → WXT | +| Firefox | `npm run build:firefox`: same, Firefox MV3 | +| Browser ZIP | `npm run zip:chrome` / `zip:firefox`: WXT ZIP path | +| Authored source ZIP | `npm run zip:source`: clean-tree HEAD archive with explicit paths | +| Version source | Package version `0.2.0-beta.1`; manifest numeric `0.2.0.1` | +| Build metadata | Current time plus locale-formatted time in tracked source | +| Source maps | Production disabled | +| Backup | Timestamped source copy; not a complete reviewer source recipe | + +The Unix-style `rm -rf` clean script also lacks an explicit portable Windows implementation. That is a development/release portability issue, not a reason to run it during this audit. + +## Inspected artifacts + +| Artifact | Size/content | Status | +|---|---|---| +| Chrome unpacked | 1,079 files; 40,670,016 bytes | Loads; no independent rebuild | +| Firefox unpacked | 1,079 files; 40,669,981 bytes | Inspected; runtime untested | +| Firefox ZIP | 38,371,285 bytes | Matches corresponding unpacked files; validator error | +| Automatic source ZIP | 922,683 bytes; 524 entries | Current source plus historical/process contamination | +| Chrome ZIP | Not present | Release artifact missing | + +Firefox ZIP SHA-256: + +```text +321e0e04d29ddb7cc29d533bffa1c405066c895893e757bbd4ea0fdd7e2e422e +``` + +Automatic source ZIP SHA-256: + +```text +44e9999830adcf1d747d3096941125405dfd48afa1428fe3e8fb5cf7551f988b +``` + +The 161 compared source/config/script entries in the automatic source archive matched the current files. That does **not** prove the generated extension is a reproducible build of them. + +No test/audit/source-map/environment filenames were found in the production ZIP scan. That is narrower than an exhaustive secret-free certification. The source ZIP, separately, contains historical backup and review material. + +**Reproducibility status** + +| Level | Result | +|---|---| +| Logical equivalence of repeated builds | Unknown | +| Same repeated-build file set | Unknown | +| Same repeated-build content | Unknown; timestamp generation creates a concrete obstacle | +| Byte-identical archives | Unknown; optional internal target unless specifically required | +| Existing Firefox ZIP vs unpacked content | Matched | +| Reviewer rebuild from submitted source ZIP | Not established | + +## Dependencies + +The lockfile contains 1,016 dependency entries besides the root, with registry URLs pointing to npm’s registry. Runtime dependencies include React, Carbon/Plex, Zustand, Zod, `txml`, virtual-list support, and decorator libraries. WXT, Vitest, Playwright, ESLint, Sharp, and build tooling are development dependencies. + +The latest advisory result identified: + +- High: `brace-expansion`, `browserslist`, `js-yaml`, `nanoid`, `sharp`, `undici`. +- Moderate: `@humanfs/node`, `@vitest/mocker`, `adm-zip`, `baseline-browser-mapping`, `firefox-profile`, `vitest`, `web-ext-run`, `wxt`. +- Low: `postcss-selector-parser`. + +These are affected package nodes, including dependency propagation—not fifteen independent shipped vulnerabilities. All inspected affected entries were marked development dependencies. Representative reports concern archive extraction, untrusted parsing, development-server mocks, and image processing. + +| Action class | Recommendation | +|---|---| +| RELEASE CRITICAL | Resolve any advisory proven reachable in shipped code or reviewer build with hostile inputs | +| SHOULD DO BEFORE RELEASE | Compatible targeted fixes; investigate overrides and archive/image tooling | +| POST-LAUNCH | Consolidate dependency duplication and unused development tools | +| DO NOT TOUCH FOR V1 | Framework/browser-abstraction major migrations solely for freshness | + +Install scripts include WXT preparation and native/build-tool setup. A complete script sandbox and third-party license inventory remain unverified. No arbitrary Git dependency sources were found in the inspected lockfile. + +## Performance + +**Measured/package evidence:** roughly 93% fonts, 18.9 MB duplicate fonts, approximately 2 MB combined major CSS files, and substantial adapter code. + +**Source-derived costs:** duplicate popup/background polling; two-second active polls can overlap slower requests; every full-list update schedules session persistence; hydration retains complete torrent metadata; sparse UI state can accumulate stale entries. + +**Predicted, not measured:** large-list memory pressure, session quota failures, startup cost, and excessive network/serialization load. No latency percentile, CPU profile, memory benchmark, or battery regression is claimed. + +No expensive page-wide observer was found. Scanning occurs only on explicit invocation. + +# Documentation, Feature Scope, and Accessibility + +## Documentation reconciliation + +| Claim | Actual behavior | Consequence | +|---|---|---| +| All listed clients fully supported/tested | Mocks plus multiple protocol defects; no current live matrix | Narrow support claims | +| Seven-language support | Mixed translated keys, hardcoded English, placeholder translation generation | English-first or complete language acceptance | +| Credentials never leave device | Authentication sent to configured remote clients | Rewrite both privacy versions | +| No torrent metadata storage | Session cache stores torrent records | Describe cache and retention | +| Only runs on explicit interaction | Idle alarms poll configured clients | Explain background behavior | +| Completion notifications | No completion-monitoring implementation established | Remove claim | +| Architecture describes site scripts/React Query | Current entrypoints have no persistent site scripts; React Query absent | Update architecture | +| API `testConnection(): Promise` | Current structured result contains connection/error data | Fix contributor guidance | +| Generic Node setup guidance | `.nvmrc`/CI use 22; audit environment used 24 | Pin reviewer environment | +| Security policy supports 0.1.x | Current package is 0.2.0 beta | Update support matrix/contact | +| Roadmap future functions | Many are explicitly aspirational | Do not classify every roadmap item as broken | +| Historical changelog features | Some no longer exist | Preserve history; distinguish current capabilities | + +Accurate documentation includes the core client-controller purpose, encrypted credential storage in the current implementation, removal of automatic site integrations, and absence of developer telemetry. + +## Feature scope + +| Feature | Classification | +|---|---| +| Vault setup/unlock/lock | CORE FOR V1 | +| Verified client setup and permission grant | CORE FOR V1 | +| Add magnet, list, pause/resume, remove without data deletion | CORE FOR V1 | +| Clear errors and active-server identity | CORE FOR V1 | +| Safe configuration backup/import | CORE FOR V1 if advertised | +| Multiple servers | OPTIONAL FOR V1; identity correctness remains mandatory | +| Explicit Add Paused | CORE FOR V1 if offered | +| Bulk page scanning | OPTIONAL; preferably hide initially | +| Label/path advanced dialog | OPTIONAL; capability-test retained fields | +| Unverified client implementations | EXPERIMENTAL; hide from public supported selector | +| Notification styling/levels without consumers | REMOVE/HIDE BEFORE RELEASE | +| Sidebar customization without runtime effect | REMOVE/HIDE BEFORE RELEASE | +| Locked performance selector | REMOVE/HIDE BEFORE RELEASE | +| WebSocket capability | DEAD/UNIMPLEMENTED in live transport | +| Full multi-language expansion | POST-LAUNCH unless verified | +| Resource directory and unrelated utilities | OPTIONAL; reduce for a focused release | +| Advanced adapter APIs without UI | POST-LAUNCH; no need to delete sound internal code | + +Accessibility is **likely acceptable in parts**, with named torrent-action buttons, focus-visible styling, text status labels, and Carbon form/dialog components. It is **likely deficient** in server-input association and virtual-list semantics. + +Contrast, text scaling, reduced motion, modal focus traps, live-region timing, and screen-reader behavior require a dedicated audit. No WCAG compliance claim is warranted. + +# What CTRL Already Gets Right + +1. **WXT provides useful browser-specific manifest generation.** Keep it. +2. **The current session-only key design removes the disk-key fallback.** Preserve this pre-existing work. +3. **Authenticated encryption and password derivation are appropriate foundations.** +4. **Privileged runtime messages validate the sender’s extension identity.** +5. **No persistent all-site content scripts or external messaging surface were found.** +6. **Host access is optional and intended to be requested for configured endpoints.** +7. **Adapters isolate protocol-specific concerns and often validate responses with Zod.** +8. **Connection testing returns structured error information instead of only a boolean.** +9. **Imports validate before mutation and attempt rollback.** Repair consistency rather than discard this work. +10. **Context-menu rebuild logic accounts for startup and locked/unconfigured states.** +11. **No developer telemetry or remote executable-code service was found.** +12. **The existing 530-test foundation and Chrome/Firefox build jobs are useful.** Expand the missing boundaries rather than replace the suite. + +# Things That Look Suspicious but Should NOT Be Treated as Release Problems + +| Concern | Evidence / why acceptable | When it becomes a problem | +|---|---|---| +| Minified third-party code | Normal bundling; Mozilla allows minification with matching source | Missing provenance or nonreproducible source | +| Validator eval warnings | Identified reflect global-object fallbacks | Reachable dynamic evaluation of untrusted strings | +| React `innerHTML` warning | Library implementation, no first-party unsafe flow found | User/server strings reach unsafe HTML rendering | +| 38 MB Firefox ZIP | Large but below documented submission limits | Unsupported assets/licenses or unacceptable measured cost | +| Broad optional HTTP(S) patterns | Necessary declaration for arbitrary user-configured endpoints | Blanket grants or unrelated host access | +| Plain HTTP to same-machine client | Explicit Chrome policy exception | Remote credentials without supported policy/security basis | +| Development-only advisories | Not automatically shipped browser vulnerabilities | Reachable hostile-input toolchain attack | +| Firefox background scripts | Correct WXT accommodation of browser differences | Lifecycle assumptions remain untested | +| Explicit Authorization with credentials omitted | Browser probe preserved supplied Authorization | Assuming this also preserves cookies | +| Extractable session AES key | Enables session JWK storage across extension contexts | Disk persistence or exposure to untrusted contexts | +| RPC commands received from server | Data/protocol responses, not remote code | Interpreting responses as executable logic | +| No file-deletion confirmation | Current UI explicitly sends `deleteData:false` | Adding data deletion or leaving wrong-server routing unfixed | + +# Negative-Evidence and Unknowns Ledgers + +## Negative evidence + +| Concern investigated | Evidence checked | Result | Confidence | +|---|---|---|---| +| Remote executable loading | Source, CSP, generated package | Not found | HIGH-CONFIDENCE | +| Developer telemetry | Network callsites/dependencies/resources | Not found | HIGH-CONFIDENCE | +| Unrestricted external messages | Manifest/listeners/sender checks | Not found | CONFIRMED within inspected surface | +| Passive all-site content scripts | Entrypoints/generated manifests | None | CONFIRMED | +| Arbitrary passive injection | Scan implementation | Explicit current-page action only | CONFIRMED | +| Current plaintext disk vault key | KeyManager and background purge | No active fallback; legacy purge exists | CONFIRMED source | +| Normal plaintext password persistence | Vault/save paths | Encrypted storage path; exports are separate | HIGH-CONFIDENCE | +| First-party unsafe eval | Source search; validator provenance | Not found | HIGH-CONFIDENCE | +| Production package includes audit files | ZIP filename inspection | Not found; source ZIP does include them | CONFIRMED | +| Production package entirely secret-free | Limited source/package checks | No secret identified; exhaustive certification not performed | UNKNOWN beyond checked scope | +| Automatic page-history collection | Manifest/source | Not found | HIGH-CONFIDENCE | +| Live data deletion during audit | Diagnostic traces | None | CONFIRMED | + +## Unknowns + +| Unknown | Why unresolved | Evidence needed | Blocks architecture? | Blocks release? | +|---|---|---|---|---| +| Exact supported client versions | No live matrix | Core-operation results per version | No | Yes | +| qBittorrent non-localhost CSRF | Generic probe lacked target deployment | Granted-host tests with default security | Possibly transport choice | Yes for qBittorrent | +| Firefox runtime | No installed environment | Signed/temporary install smoke and lifecycle tests | No | Yes | +| HTTP/LAN AMO interpretation | No clear applicable exception established | Official clarification or narrower transport scope | No | Yes if retained | +| Rebuild from source ZIP | Tracked mutation prevented safe run | Clean permitted environment and output comparison | No | Yes, Firefox | +| Private/incognito behavior | No runtime matrix | Permission/storage/scan isolation tests | No | Yes if supported | +| Large-list limits | No benchmark | 1k/10k synthetic list profiles and quota tests | Could affect optimization | Conditional | +| Historical secret remediation | Provider/remote evidence unavailable | Provider rotation records and authorized ref scan | No | If a live secret remains | +| Hosted privacy/support URLs | No verified publication | Public URL checks and operator control | No | Yes | +| Store account/trader status | Account unavailable | Dashboard/operator verification | No | Yes | +| Full accessibility | Static review only | Keyboard/screen-reader/zoom/contrast audit | No | Core workflows yes | +| Third-party notice completeness | No full license reconciliation | Dependency/assets notice inventory | No | Yes where obligations apply | + +# Architecture Judgment and Scorecard + +**Architecture verdict: MOSTLY sound; TARGETED REFACTORING required.** + +The framework, adapter split, and privilege boundary are workable. The foundational defects are **state ownership, server identity, authentication contracts, and vault consistency**. They are concentrated enough for bounded repair. + +| Structural class | Items | +|---|---| +| RELEASE BLOCKER | Wrong-server operations; Firefox metadata; unreproven release provenance | +| FOUNDATIONAL DEFECT | Multiple state owners; positional diffs; inconsistent transport/auth contracts | +| SIGNIFICANT MAINTENANCE ISSUE | Duplicate interfaces, broad adapter surface, stale docs, dependency overrides | +| NORMAL TECHNICAL DEBT | Mixed styling conventions, some `any`, inconsistent naming within implementation | +| NOT WORTH FIXING FOR V1 | Framework migration, full DI redesign, universal client capability system | + +Architecture dimensions: conceptual clarity **3/5**, maintainability **3**, correctness **1**, testability **3**, state ownership **1**, error isolation **2**, portability **2**, security boundaries **3**, protocol abstraction **2**, configuration **2**, build simplicity **2**, releaseability **1**. + +## Independent readiness scorecard + +| Category | Score / 5 | +|---|---:| +| Product clarity | 3 | +| Architecture | 3 | +| Networking/protocol correctness | 1 | +| State/concurrency correctness | 1 | +| Security | 2 | +| Privacy | 1 | +| Permissions | 3 | +| Chrome runtime readiness | 2 | +| Firefox runtime readiness | 1 | +| Chrome policy readiness | 2 | +| Mozilla policy readiness | 1 | +| Build/packaging | 1 | +| Reproducibility | 1 | +| Testing | 2 | +| CI/release engineering | 2 | +| Performance | 2 | +| UX | 2 | +| Accessibility | 2 | +| Documentation | 1 | +| Store assets | 1 | +| Maintainability | 3 | + +These are evidence/readiness scores, not a mechanical average. + +**Confidence in the NOT READY verdict: HIGH.** +**Confidence in successful operation across advertised deployments: LOW.** + +Gating dimensions are state integrity, live protocol verification, Firefox runtime/submission, privacy accuracy, and release reproduction. + +# Minimum Responsible v1 and Operator Decisions + +A defensible initial release would have: + +- **Browsers:** desktop Chrome 152+ as the conservative tested starting point, and Firefox 140+ only after minimum/current-version acceptance. Lower Chrome versions can be added with evidence; this is a support recommendation, not an API minimum claim. +- **Clients:** Transmission first, then qBittorrent after version/authentication repair. Neither is approved by this audit as already working. Aria2 may join after its smaller core matrix passes. +- **Core:** setup, permission grant/denial/revocation recovery, encrypted configuration, lock/unlock, truthful status, add magnet, add paused, list, pause/resume, remove without deleting data, reconnect, safe backup/import. +- **Languages:** English-first. +- **Scope removed/hidden:** unverified clients, nonfunctional settings, unverified file upload, automatic bulk scan, unused WebSocket permissions, broad “fully tested” claims. +- **Privacy:** no telemetry; direct client communication clearly disclosed; secrets excluded from safe exports; bounded and redacted session cache. +- **Evidence:** both-browser core scenarios, server-switch races, vault corruption/restart, transport contracts, source rebuild, official validator, listing assets and reviewer setup. + +Excluded clients are promoted by repairing their identified protocol defects and passing the same browser/client operation matrix. Adapter existence or inherited implementation is not promotion evidence. + +| Decision | Option A | Option B | Recommendation / evidence | What changes it | +|---|---|---|---|---| +| Client scope | Small verified set | All nine before release | A; current evidence is uneven and several paths are defective | Funded real-client matrix with passing results | +| HTTP | Explicit local/LAN support | HTTPS except loopback | Prefer B for remote use; resolve LAN policy before A | Official AMO clarification and deployment demand | +| Page scan | Hide initially | Retain repaired preview/bulk add | A reduces permissions and surprising transfers | Strong user need plus consent/default tests | +| Browser baseline | Narrow desktop baseline | Older versions/mobile | A reduces consent/lifecycle combinations | Verified demand and test capacity | +| Languages/settings | English core | Complete broad surface | A; visible claims exceed implementation | Finished translations and runtime consumers | +| Multi-server v1 | Retain after identity repair | Single configured server | Either is viable; retain only with STATE-001 acceptance | Product importance versus release schedule | +| Publisher identity | Existing verified identity | New permanent identity | Operator must choose, not infer | Actual account ownership | +| Quality bar | All mandatory gates | Ship with documented P2 debt | Permit bounded P2 debt, not unresolved wrong-target/security failures | New evidence that removes a gate | + +# Remediation Program and Candidate Execution Waves + +No work below is authorized for implementation by this audit request. + +## Phases + +| Phase | Objective / findings | Dependencies | Exit criterion | +|---|---|---|---| +| A — Decisions/evidence | Freeze clients, versions, transports, scan, languages; obtain client environments | Operator decisions | Written support matrix and reproducible test setup | +| B — Correctness | STATE-001–003, NET-005 | A identity contract | Stable command targets and restart-safe snapshots | +| C — Security/privacy | SEC-001–003, PRIV-001 | B ownership; A data scope | Corruption/lock/export tests and consistent disclosures | +| D — Browser/release | FF-001, BUILD-001 | A baseline; frozen source | Valid manifests and reviewer-rebuild proof | +| E — Scope reduction | SCOPE-001, unverified clients | A | Every visible control/permission justified | +| F — UX/accessibility | UX-001–004 | B/C/E | Core journey truthful and keyboard accessible | +| G — Tests/CI | TEST-001, client contracts, dependency dispositions | Repairs incrementally | Mandatory gates cannot silently skip | +| H — Submission preparation | Assets, notes, policy URLs, account prerequisites | All preceding release gates | Reviewable submission bundle | +| I — Post-launch | PERF-001 if deferred, optional APIs/localization/debt | Release acceptance | Measured improvements without scope creep | + +## Bounded waves + +**Safe to parallelize on this working tree: NO for every wave.** Separate worktrees could later isolate genuinely independent work, with explicit integration contracts. + +| Wave | Objective / issues / files | Non-goals and mutation boundary | Starting evidence / dependencies | Acceptance / verification | +|---|---|---|---|---| +| 1 | Stable server identity, response generation, snapshots; background/resolver/store/messages | No adapter feature expansion; only state contract and associated tests | Current race/diff probes; agreed ID migration | All controlled race/reorder/reconnect cases pass | +| 2 | Vault authority, corruption envelope, safe export; security/hooks/import/export | No new account system or crypto replacement | Wave 1 contract; migration fixtures | Multi-window lock, stale-save rejection, corruption and secret matrix | +| 3 | Selected-client transport contracts; Fetch and chosen adapters | No all-client rewrite; no speculative permissions | Supported versions and live endpoints; wave 1 cancellation interface | Exact wire tests and real operation matrix | +| 4 | Honest core UI and scope removal; dashboard/server/settings/context menu | Do not implement dead features | Waves 1–3 and frozen visible surface | Truthful states, paused defaults, URL round-trip, keyboard acceptance | +| 5 | Deterministic builds, source archive, metadata, dependencies/assets; scripts/config/CI | No publishing; no unrelated major upgrades | Frozen source/toolchain and publisher ID | Validator, clean source rebuild, content comparison, package inventory | +| 6 | Privacy/listing/reviewer package; docs/assets/declarations | No submission or legal-status choice | Final data inventory and manual evidence | Consistent reviewed bundle ready for operator submission decision | + +For every wave, **implemented, tested, committed, pushed, merged, released, and submitted remain separate statuses**. None of the latter actions is an automatic acceptance criterion. + +# Release Gates + +| Gate | Observable pass criteria | +|---|---| +| **1 — Product scope frozen** | Exact browser/client/version/transport/language/features matrix approved; unsupported surfaces hidden | +| **2 — Core correctness** | Wrong-server test impossible; stale responses rejected; reorders/replacements/removals correct; two-window/restart synchronization passes | +| **3 — Security/privacy** | Cross-window redaction; wrong-password/corrupt-state failure; safe-export secret matrix; accurate data declarations and policy | +| **4 — Chrome runtime** | Clean install and supported client matrix pass on declared Chrome baseline/current target; grant/deny/revoke tested | +| **5 — Firefox runtime** | Validator zero errors; warnings dispositioned; signed/installable package; core/lifecycle/consent matrix passes | +| **6 — Build/package reproduction** | Frozen source ZIP rebuilds matching output files/content in documented environment; forbidden files absent; checksums recorded | +| **7 — Automated verification** | Lint/typecheck/unit/contract/browser/package checks pass; required tests cannot skip; advisories dispositioned | +| **8 — Policy/listing readiness** | Accurate assets, descriptions, policy URL, permission/data forms, publisher prerequisites, reviewer notes/environment | +| **9 — Manual acceptance** | All required matrix rows pass with captured evidence and no unresolved critical defect | + +## CI gate prioritization + +**Required for first release:** lint, typecheck, targeted tests, both builds, Firefox validator, forbidden-file scan, source-archive rebuild, content comparison, dependency/secret disposition, Chrome E2E, Firefox smoke, artifact version/checksum consistency. + +**Recommended soon after:** package-size regression budget, larger-list benchmarks, broader client-version coverage, longer lifecycle soak tests, stronger action pinning. + +**Unnecessary for v1:** automatic store publication, an elaborate client farm for unadvertised clients, arbitrary coverage-percentage targets, custom telemetry infrastructure. + +# Manual Acceptance Matrix + +Use disposable profiles and dedicated client queues containing redistributable test content. “Both” means every declared desktop browser target. + +| Environment | Steps | Expected result | Evidence | +|---|---|---|---| +| Clean Chrome / Firefox | Install exact candidate | Correct metadata, no startup errors | Package hash, browser version, console | +| Both | First launch; create vault | Clear setup → configure-server flow | Screenshots/video | +| Both | Wrong password; correct password | Failure then successful unlock | UI and storage observations | +| Both | Restart browser | Vault locked; no disk key fallback | Before/after storage keys | +| Each supported client | Configure endpoint; test connection | Accurate connection/auth result | Server version, sanitized network trace | +| Both | Grant endpoint permission | Only intended origin granted | Permission inventory | +| Both | Deny grant | Explain denial; no false success | UI/error | +| Both | Revoke existing grant | Connection invalidated; recovery offered | Permission/UI trace | +| Each client | Add magnet | Exactly one intended queue entry | Client queue + request trace | +| Each entrypoint/client | Enable default paused; add | Added paused consistently | Client state | +| Both/client | List and change status remotely | Correct refresh and ordering | Before/after snapshots | +| Each client | Pause/resume | Acknowledged actual state change | Client and UI evidence | +| Each client | Remove | Intended server/torrent only; data retained | Queue and filesystem check | +| Both/client | Bad credentials | Actionable auth error; no false online state | Sanitized logs | +| Both/client | Stop server / go offline | Stale/offline state; retry recovery | Timing and UI capture | +| Both | Configure A/B with overlapping IDs | Commands bind to selected server | Request destinations | +| Both | Delay A response; switch to B | A response rejected | Controlled trace | +| Both | Open two options windows | Independent ranges, common canonical configuration | Video/messages | +| Both | Lock in one window | All windows redact | Video/storage event trace | +| Both | Reload extension / stop background | Resubscription restores correct snapshot | Lifecycle trace | +| Both | Export safe/full; import | Safe excludes all secrets; full clearly sensitive; import consistent | Synthetic file inspection | +| Both | Empty/malformed/truncated backup or vault | Explicit error; original data preserved | Storage diff | +| Both | Update from prior supported version | Migration retains servers; legacy key removed | Version/storage evidence | +| Both | Keyboard-only core journey | Labels, focus, dialogs, errors usable | Recording/accessibility tree | +| Both | Screen reader and 200% zoom | Operable configuration and queue | Audit notes | +| Both | Observe network while idle/active | Only documented endpoints and polling | Sanitized network capture | +| Both, if supported | Private/incognito scan/configuration | No inappropriate persistence/cross-context leakage | Storage/network evidence | +| Both | Bulk scan, if retained | Correct frame/scope, preview, deduplication, defaults | Controlled page/client records | + +# Release Artifact Checklist + +| Artifact | Status | +|---|---| +| Chrome submission ZIP | **MISSING** in inspected build directory | +| Chrome unpacked build | **EXISTS**, provenance incomplete | +| Firefox ZIP | **EXISTS BUT WRONG**: metadata error | +| Firefox source archive | **EXISTS BUT WRONG**: contamination/rebuild process | +| Exact build instructions | **INCOMPLETE** | +| Checksums | **EXISTS** in this audit for inspected ZIPs; release manifest incomplete | +| Version consistency | **EXISTS**, numeric mapping coherent; release policy needed | +| Changelog | **EXISTS**, current capability reconciliation needed | +| Release notes | **INCOMPLETE** for a public candidate | +| Privacy policy files | **EXISTS BUT WRONG** | +| Hosted privacy URL | **UNKNOWN** | +| Permission justifications | **INCOMPLETE** | +| Chrome data disclosures | **UNKNOWN / INCOMPLETE** | +| Firefox data declarations | **MISSING** | +| Reviewer instructions | **INCOMPLETE** | +| Reviewer client environment | **MISSING** | +| Extension icons | **EXISTS** | +| Release screenshots | **MISSING** from inspected release materials | +| Small promotional tile | **MISSING** | +| Marquee promotional asset | **NOT REQUIRED** | +| Promotional video | **UNKNOWN requirement applicability**; official page wording warrants dashboard confirmation | +| Support URL/contact | **INCOMPLETE**; GitHub links exist, private security contact unclear | +| Third-party notices | **INCOMPLETE verification** | +| Manual acceptance evidence | **INCOMPLETE** | +| Store-account prerequisites | **UNKNOWN** | + +For Chrome, prepare a 128×128 icon, at least one accurate 1280×800 screenshot, and a 440×280 small promotional tile. Confirm the actual dashboard’s video requirement rather than treating ambiguous documentation wording as a proven blocker. [Listing documentation](https://developer.chrome.com/docs/webstore/cws-dashboard-listing). + +# Risk and Technical Debt Registers + +## Risk register + +| Risk | Probability | Impact | Evidence | Mitigation | Residual risk | +|---|---|---|---|---|---| +| Wrong-server command | Demonstrated sequence | High | STATE-001 | Stable IDs/generations | Client-side ID behavior | +| Incorrect queue state | Demonstrated | High | STATE-002/003 | Snapshots/resync | Large-list scaling | +| Authentication failure | High for identified paths | High | NET findings | Browser/live contracts | Server/proxy variants | +| Vault divergence/corruption | Demonstrated conditions | High | SEC findings | Canonical state/envelope | Local profile compromise | +| Safe-export disclosure | Demonstrated | High | SEC-003 | Allowlist sanitization | Sensitive full exports | +| Firefox incompatibility | Unknown runtime; known submission defect | High | Validator/no live test | Metadata + Firefox matrix | Browser updates | +| Privacy rejection | High with current wording | High | Contradictory policy | Accurate declarations | LAN interpretation | +| Chrome review friction | Medium | Medium/high | Claims/permissions/assets | Narrow scope and reviewer notes | Reviewer judgment | +| Source rebuild failure | High | High for AMO | BUILD-001 | One deterministic pipeline | Toolchain/platform differences | +| Dependency/toolchain exposure | Conditional | Medium/high | Fresh advisories | Reachability and patches | Future advisories | +| Package overhead | Confirmed | Medium | Font hashes | Deduplicate | Glyph coverage | +| Support burden | High if nine clients advertised | High | Uneven evidence | Verified support matrix | Third-party changes | +| Maintainability drift | Medium | Medium | Duplicate owners/interfaces/docs | Targeted ownership repair | Optional API surface | + +## Debt register + +| Timing | Work | +|---|---| +| **MUST FIX FOR V1** | Identity/races, diff/resync, vault/export integrity, retained-client protocols, truthful core UI, submission metadata/privacy/source reproduction | +| **FIRST POST-LAUNCH** | Additional verified client versions, larger-list profiling, deferred font cleanup, broader accessibility/localization | +| **LATER** | Optional advanced APIs, richer capabilities, notification polish, code organization consolidation | +| **MAY NEVER BE WORTH FIXING** | Reimplementing every dead setting, universal DI redesign, custom browser abstraction, automated publishing | + +# What CTRL Should NOT Do Next + +| Temptation | Why attractive | Why defer | Reconsider when | +|---|---|---|---| +| Framework migration | Appears to modernize everything | Does not fix identity/auth/vault contracts | Framework blocks a proven requirement | +| Replace WXT/browser abstraction | Firefox problems suggest portability failure | WXT already handles manifest differences | Reproduced framework defect cannot be isolated | +| Broad dependency majors | Quickly reduces advisory counts | Adds uncontrolled compatibility changes | Targeted patches unavailable and reachability warrants it | +| Finish all existing settings | UI already exists | Expands scope instead of removing misleading controls | Verified demand | +| Implement all nine clients at once | Matches current README | Multiplies live-test/support matrix | Dedicated environments and capacity exist | +| Add telemetry | Makes failures observable | Adds privacy and operational surface before core correctness | Explicit product need and consent design | +| Complete seven languages now | Existing dictionaries suggest near completion | Runtime UI remains mixed | English core is stable and translators/testers available | +| Automatic store publication | Convenient CI milestone | Removes a useful human review boundary | Mature release controls and explicit authorization | +| Broad refactoring | Cleans accumulated inconsistencies | Obscures bounded repairs | Specific maintenance evidence justifies it | +| Performance rewrite | Package is large | Asset duplication can be repaired directly | Measured runtime bottleneck remains | + +# Final Independent Verdict + +**What is CTRL today?** +A substantial beta torrent-controller extension with useful adapter and security foundations, a working Chrome UI shell, and unresolved correctness, protocol, privacy, and release-engineering defects. + +**Is the architecture fundamentally sound?** +**MOSTLY.** Ownership boundaries need repair. + +**Does it require a rewrite?** +**TARGETED REFACTORING.** + +**Chrome readiness?** +**NOT READY.** + +**Firefox readiness?** +**NOT READY.** + +**Top five release blockers, ranked** + +1. Wrong-server routing and stale-response acceptance. +2. Incorrect queue diffs and synchronization. +3. Vault consistency, corruption handling, and safe-export secrets. +4. Unverified/broken advertised client protocols. +5. Firefox metadata plus privacy/source-rebuild readiness. + +**Top five things not to change before v1** + +1. WXT and the existing browser-target approach. +2. React and the basic UI framework. +3. AES-GCM/PBKDF2 primitives. +4. Current session-only vault key and legacy-key purge. +5. Adapter separation, response validation, and the existing useful tests. + +**Remove or hide:** unverified clients, nonfunctional preferences, unverified upload/advanced claims, unused WebSocket declarations, and bulk scanning unless repaired and accepted. + +**Can wait:** broad localization, advanced client APIs, cosmetic consolidation, framework upgrades, and measured noncritical optimization. + +**Largest technical unknown:** real-browser authentication and core operations against the exact supported client/server/proxy versions. + +**Largest policy unknown:** the defensible Mozilla treatment of unencrypted user-controlled LAN connections. + +**Minimum path to Chrome:** repair shared correctness/security, verify a narrow client set in Chrome, reconcile disclosures, produce a clean package and complete manual/listing gates. + +**Minimum path to Firefox:** the same shared repairs plus Gecko identity/data metadata, Firefox runtime acceptance, signed-package verification, and matching reviewer source rebuild. + +**Recommended first execution wave:** stable server identity, generation rejection, explicit command targets, and reliable snapshots, with the reproduced races/diffs converted into regression tests. + +# Comparison-Ready Summary Ledger + +Abbreviations: ST = SOURCE TRACE; RR = RUNTIME REPRODUCTION; LB = LIVE BROWSER TEST; SC = STATIC CONFIGURATION; PI = PACKAGE INSPECTION; BA = BUILD ARTIFACT; AT = AUTOMATED TEST; OP = OFFICIAL POLICY; OA = OFFICIAL API DOCUMENTATION; DC = DOCUMENTATION CLAIM; RI = REASONED INFERENCE. + +“Quality” denotes the audit’s responsible-release gate, not a store-specific rule. + +| AST ID | Priority | Category | Short finding | Confidence | Evidence class | Chrome gate | Firefox gate | Needs runtime verification? | Needs policy verification? | +|---|---|---|---|---|---|---|---|---|---| +| STATE-001 | P0 | Integrity | Wrong-server/stale-response routing | CONFIRMED | ST, RR | Quality | Quality | Live acceptance | No | +| STATE-002 | P1 | State | Incorrect positional diffs | CONFIRMED | ST, RR | Quality | Quality | Repair acceptance | No | +| STATE-003 | P1 | Lifecycle | Shared viewport/no initial resync | CONFIRMED | ST, RR | Quality | Quality | Both-browser acceptance | No | +| SEC-001 | P1 | Security | Cross-context vault divergence | CONFIRMED / HIGH-CONFIDENCE | ST, LB | Quality | Quality | Firefox and stale-save | No | +| SEC-002 | P1 | Integrity | Missing ciphertext falsely unlocks | CONFIRMED | ST, RR | Quality | Quality | Recovery acceptance | No | +| SEC-003 | P1 | Secrets | Safe export retains API key | CONFIRMED | ST, RR | Quality | Quality | Full secret matrix | No | +| NET-001 | P1 | Protocol | XML JSON-stringified | CONFIRMED | ST, RR | Conditional client | Conditional client | Real XML endpoint | No | +| NET-002 | P1 | Auth | Cookie/bootstrap contract broken | HIGH-CONFIDENCE | ST, LB, OA | Conditional client | Conditional client | Yes | No | +| NET-003 | P1 | Compatibility | qBittorrent old actions/states | HIGH-CONFIDENCE | ST, OA | Conditional client | Conditional client | Exact versions | No | +| NET-004 | P2 | Browser network | Forbidden-header assumptions | CONFIRMED limitation; impact PLAUSIBLE | ST, LB | Conditional client | Conditional client | Yes | No | +| NET-005 | P2 | Reliability | Cancellation/body timeout gaps | CONFIRMED | ST, RR | Quality | Quality | Slow-body acceptance | No | +| UX-001 | P1 | UX | False online/ignored outcomes | CONFIRMED | ST, LB | Quality | Quality | Repair acceptance | No | +| UX-002 | P1 | Behavior | Paused defaults bypassed | CONFIRMED | ST | Conditional feature | Conditional feature | Each add path | No | +| UX-003 | P2 | Setup | Endpoint URL handling | CONFIRMED limitation | ST | Conditional topology | Conditional topology | Yes | No | +| UX-004 | P2 | Accessibility | Core labels/list semantics | HIGH-CONFIDENCE | ST | Quality | Quality | Assistive technology | No | +| SCOPE-001 | P2 | Product | Dead controls/overclaims/unused WS | CONFIRMED | ST, SC, DC | Scope/permissions | Scope/permissions | Retained features | Final permission mapping | +| FF-001 | P0 | Submission | Missing ID/data metadata | CONFIRMED | SC, BA, PI, OP | — | Hard blocker | Install/consent | Data selections | +| PRIV-001 | P1 | Privacy | Contradictory disclosures | CONFIRMED | ST, DC, OP | Policy materials | Policy materials | Network reconciliation | LAN exception | +| BUILD-001 | P1 | Release | Source/rebuild pipeline inadequate | CONFIRMED defects | ST, BA, PI | Provenance quality | Reviewer rebuild | Rebuild | No | +| PERF-001 | P2 | Packaging | Duplicate fonts | CONFIRMED | PI, BA | Deferrable | Deferrable | Performance impact | No | +| TEST-001 | P2 | Assurance | Severe failures escape suite | CONFIRMED | AT, ST, RR | Quality evidence | Quality evidence | Yes | No | +| DEP-001 | P2 | Supply chain | Dev-tool advisories unresolved | CONFIRMED reports | SC, AT, RI | Disposition | Disposition | Reachability-dependent | No | + +All IDs in this table carry the `AST-` prefix; shortened cells avoid repetition. + +## Strongest five findings + +1. AST-FF-001: official validator rejects the actual Firefox ZIP. +2. AST-STATE-001: current background code reproduced stale publication and wrong-server routing with synthetic clients. +3. AST-STATE-002: current diff/application functions produce demonstrably wrong results. +4. AST-SEC-003: actual safe-export function retains the synthetic supported API key. +5. AST-NET-001: actual shared transport changes XML into JSON. + +## Weakest five findings or impact estimates + +1. AST-NET-004’s exact impact on privileged non-localhost qBittorrent deployments. +2. AST-SEC-001’s complete import/concurrent-save loss sequence beyond the confirmed lock divergence. +3. AST-UX-004’s severity across real assistive technologies. +4. AST-DEP-001’s exploitability under this project’s actual build inputs. +5. AST-PERF-001’s user-perceived startup/memory impact; duplication itself is certain. + +## Five likely false-positive traps + +1. Treating all validator warnings as submission errors. +2. Calling all development advisories shipped remote vulnerabilities. +3. Assuming optional HTTP(S) patterns mean blanket installed host access. +4. Declaring all localhost/LAN HTTP a Chrome policy violation. +5. Treating Firefox’s generated background scripts as a broken MV3 conversion. + +## Five areas another independent reviewer should challenge + +1. Reproduce the state-routing sequence in actual browser windows against disposable servers. +2. Test Fetch/cookie/CSRF behavior with granted host permissions in both browsers. +3. Rebuild the proposed source archive in Mozilla’s documented environment. +4. Challenge the exact data-category and LAN-policy interpretation with current official guidance. +5. Inspect concurrency/migration/export behavior using realistic multi-window workflows, while accounting for this audit’s disclosed blindness limitation. \ No newline at end of file diff --git a/docs/release/v1/audits/FABLE_RELEASE_READINESS_AUDIT_2026-09-09.md b/docs/release/v1/audits/FABLE_RELEASE_READINESS_AUDIT_2026-09-09.md new file mode 100644 index 0000000..5503b91 --- /dev/null +++ b/docs/release/v1/audits/FABLE_RELEASE_READINESS_AUDIT_2026-09-09.md @@ -0,0 +1,1048 @@ +--- +artifact: CTRL Fable Release Readiness Audit +audit_date: 2026-09-09 +role: independent repository/release assessment +status: historical assessment evidence; findings require reconciliation against current repository state before implementation +source_agent: Fable +--- + +# CTRL — Full Repository, Architecture, Product, and Release Readiness Assessment + +Assessment date: 2026-09-09 (evidence gathered 2026-09-08 to 2026-09-09) +Mode: REVIEW / ASSESSMENT / RECONCILIATION ONLY. No files, commits, pushes, releases, or submissions. + +--- + +# Decisions Made + +Recommendations the evidence supports without further operator input: + +1. **Treat the shared HTTP transport as the first foundational fix.** One `FetchHttpClient` policy (`credentials: 'omit'`, manual `Origin`/`Referer`/`Cookie` headers that browsers silently drop, JSON coercion of every non-form body) breaks Deluge, Flood, uTorrent and ruTorrent outright and breaks qBittorrent against any non-localhost host with default CSRF protection. Per-adapter transport policy (credentials mode, raw-body passthrough, header strategy) must be introduced before any adapter-level symptom work. +2. **Ship v1 with a verified client subset.** Publicly advertise only adapters that pass a live browser test against a real client (today's candidates: qBittorrent, Transmission, BiglyBT, Aria2). Keep the other adapters in code but mark them "experimental" in the UI and listing, or hide them, until they pass. +3. **Add a Firefox-only manifest block** (`browser_specific_settings.gecko.id`, `strict_min_version` ≥ 128, `data_collection_permissions`) generated from `wxt.config.ts` by browser target. addons-linter currently reports one error (`ADDON_ID_REQUIRED`) and this is a hard AMO blocker. +4. **Fix packaging before any store work.** The production package is 40.67 MB with 1,058 font files, two ~1 MB CSS bundles containing the full Carbon stylesheet twice, 105 remote `s81c.com` font URLs in shipped CSS, non-deterministic font asset names between consecutive builds, and a build script that rewrites a tracked source file. Target: < 3 MB, byte-identical rebuilds, no remote URLs. +5. **Use `scripts/zip-source.ts` (git archive of a clean tree), never the WXT auto-generated `-sources.zip`, for AMO source upload.** The WXT sources zip sweeps in `backups/` (old torrent-site content scripts for 1337x, RARBG, TPB, Nyaa etc.), `build_log.txt`, `audit_extension*.txt`, and `playwright-report/`. +6. **Remove or hide every settings control that has no runtime consumer** (theme selector, layout/sidebar, notification level and style, "Open Web UI" context toggle, performance mode, enhanced diagnostics, command palette, "Storage Health" card, popup Debug tab) rather than implementing them for v1. +7. **Remove the Utilities external-link surface** (torrent cache sites, "IKnowWhatYouDownload", WebTorrent checker) from the shipped product. It is outside the single purpose and is the most likely reviewer trigger under both stores' copyright-facilitation language. +8. **Disarm `auto-localize.yml`** before any further push that touches the English locale, and downgrade the "7 languages" claim to "English, with partial translations" or ship English-only for v1. +9. **Commit the in-flight OL-012 vault-key remediation** (already in the working tree, tested by the new `KeyManager.test.ts`) as its own commit after review; it is a genuine security improvement and a prerequisite for the Firefox build to be credible. +10. **Reproducible-build gate for Firefox**: pin Node/npm versions in the source README, eliminate `generate-build-info.ts` timestamp rewriting, make font asset emission deterministic (or stop bundling Plex), and add a CI job that builds twice and diffs. +11. **Do not add `declarativeNetRequest` back** to solve qBittorrent CSRF unless a live test proves `credentials: 'include'` plus documented server-side settings cannot work. Prefer the least-permission path first. +12. **Keep WXT + single codebase + per-browser manifest generation.** No browser abstraction layer, no framework migration, no vite 8 / Babel 8 migration before v1. + +# Decisions Deferred + +Only matters that need more evidence or operator choice: + +1. **Live runtime verification of the four broken adapters and the qBittorrent CSRF finding.** These are static/spec-derived conclusions (Confirmed by code trace, High confidence) but were not executed against real clients in this review; the project's own OL-013 records the same gap. A one-day live matrix (Chrome + Firefox × each client, non-localhost host) decides which adapters ship in v1. +2. **Firefox `data_collection_permissions` value** (`["none"]` with reviewer note vs `["authenticationInfo"]`). Policy text supports both; reviewer practice is unverified. +3. **Whether to keep plain-HTTP LAN support without an in-UI warning.** Both stores' "use encryption when transporting data" language is a gray area for user-owned LAN servers. +4. **Master-password vault as a mandatory first-run step.** It is the largest onboarding friction and the source of the "not configured" confusion, but changing it changes the security posture and privacy statements. +5. **Whether the public GitHub repository should keep `.raiden/`, `.serena/`, and `docs/reference/` tracked.** They are operator process artefacts (including a written map to a formerly exposed key) that will be read by store reviewers who follow the homepage link. +6. **Trader/non-trader (EU DSA) self-declaration** for the Chrome Web Store. + +# Critical Issues + +P0 (release stop) and the most consequential P1s: + +| ID | Title | Gate | +|---|---|---| +| FND-01 / ADP-01..04 | Deluge, Flood, uTorrent, ruTorrent cannot authenticate or speak their protocol through the shared HTTP client | BLOCKS BOTH STORES (minimum functionality / false advertising) | +| FF-01 | No `browser_specific_settings.gecko` (id, min version, data-collection consent); addons-linter error | BLOCKS FIREFOX | +| PKG-01 / BUILD-01 | 40.7 MB package, remote font URLs, duplicated CSS, non-reproducible builds, tracked-file rewrite during build | BLOCKS FIREFOX (reproducibility), BLOCKS QUALITY BAR (Chrome) | +| REL-01 | WXT sources zip contains legacy torrent-site content scripts, build logs, audit notes | BLOCKS FIREFOX if that archive is uploaded | +| ADP-05 | qBittorrent default CSRF protection rejects extension `Origin`; works only on localhost | BLOCKS QUALITY BAR (flagship client) | +| PRIV-01 / PRIV-02 | Privacy policy and README claim no external requests, completion notifications, 7 languages, "fully tested" Firefox; implementation disagrees; no in-UI disclosure at credential entry | BLOCKS BOTH STORES (disclosure accuracy) | +| PROD-01 | Utilities links to torrent-cache and IP-tracking sites | BLOCKS QUALITY BAR / high policy risk | +| UX-01 | Popup shows "Extension not configured / Setup Now" when the vault is merely locked; no unlock path in popup | BLOCKS QUALITY BAR | +| ARCH-01 / ARCH-02 | Poll results and row commands not bound to a server; positional diff patches corrupt row identity on reorder (destructive actions can hit the wrong torrent) | BLOCKS QUALITY BAR | +| CI-01 | `auto-localize.yml` armed with `contents: write`, never run, would overwrite real translations with placeholders | BLOCKS QUALITY BAR | + +# Changes and Verification + +- Repository inspected: `E:\Citadel\CTRL` (git root), remote `origin = git@github.com:StarlightDaemon/CTRL.git`, public. +- Ref inspected: branch `main` at `f088c5f857d4f229534562f8c87c92bf4ba66df9` (2026-07-15), 0 ahead / 0 behind `origin/main`, 105 commits, single author. Working tree dirty: 19 entries (RAIDEN/Serena state, `extension/package.json` + lockfile bumps, uncommitted OL-012 vault-key remediation in `background.ts`, `ContextMenuService.ts`, `KeyManager.ts`, and a new untracked `tests/unit/KeyManager.test.ts`). One stash (`buildInfo.ts` regenerated stamp). One local tag `pre-dependabot-delete-backup`. +- Commands run (all read-only with respect to tracked state; outputs only under ignored `builds/`, `.wxt/`, `test-results/`, npx cache, and the session scratchpad): + - `npm run compile` → pass (tsc, 0 errors) + - `npm run lint` → 0 errors, 31 warnings + - `npx vitest run` → 16 files, 530 tests passed (13.9 s) + - `npx wxt build -b chrome` (×3) and `npx wxt build -b firefox --mv3` (×2) → success; 40.67 MB each; consecutive Chrome builds differ in `assets/style.css` (font asset numbering) + - `npx wxt zip -b firefox --mv3` → `ctrl-extension-0.2.0.1-firefox.zip` 38.4 MB (1,091 files) + `-sources.zip` 923 KB (524 files incl. `backups/`) + - `npx --yes addons-linter builds/firefox-mv3` → 1 error (`ADDON_ID_REQUIRED`), 5 warnings (`MISSING_DATA_COLLECTION_PERMISSIONS`, 2× `DANGEROUS_EVAL` in `background.js`, 2× `UNSAFE_VAR_ASSIGNMENT` innerHTML in React chunk) + - `npm audit` → 0 vulnerabilities in production deps; 12 in dev deps (6 high: sharp < 0.35.4, undici 7.x, nanoid) + - `CI=true npx playwright test --grep-invert @integration` → 6/6 failed: Chromium binary not installed locally. Not installed by me. E2E status relies on the CI record: `gh run list` shows the last 6 CI runs on `main` succeeded (latest 2026-07-16 for `f088c5f`). + - `gh run list --workflow=auto-localize.yml` → no runs ever. + - `git ls-files`, `git diff`, `git stash list`, `git show`, `gh repo view` (visibility PUBLIC). +- Policy sources checked (fetched 2026-09-08/09): developer.chrome.com program policies (quality guidelines, minimum functionality, privacy, user-data FAQ, limited use, disclosure requirements, 2026 policy update, data handling, dashboard privacy tab, MV3 requirements, permissions, deceptive installation, code readability, malicious/prohibited, IP, listing requirements, images, register/set-up-account, trader disclosure, review process, LNA blog), Chrome extension references (manifest version, minimum_chrome_version, CSP, alarms, storage, permissions list, activeTab, SW lifecycle, browser namespace), MDN (background, browser_specific_settings, host_permissions, optional_permissions, permissions.request, user actions, CSP, storage.session, alarms, menus, action, scripting, notifications, getBrowserInfo, Firefox 115/128 release notes, Chrome incompatibilities), extensionworkshop.com (add-on policies 2026-04-30, source code submission, third-party libraries, submitting an add-on, listing, MV3 migration guide, built-in data consent, signing overview, web-ext), blog.mozilla.org/addons (data-collection consent 2025-10-23; API changes 149–152), github.com/mozilla/addons-linter rules (secondary). +- Files changed: **none**. Commits created: **none**. Pushes: **none**. Releases / deployments / submissions: **none**. +- Not performed: browser runtime verification against live torrent clients; Playwright e2e locally; Firefox runtime CSP/upgrade-insecure-requests behaviour; Chrome ≥144 LNA behaviour. + +--- + +# Full Detailed Report + +## 2. Repository Verification + +| Item | Value | +|---|---| +| Project | CTRL ("CTRL - Torrent Control"), `extension/package.json` name `ctrl-extension`, version `0.2.0-beta.1` | +| Working directory / root | `E:\Citadel\CTRL` (extension workspace at `E:\Citadel\CTRL\extension`) | +| Branch / HEAD | `main` @ `f088c5f` "chore: ignore AppleDouble and .DS_Store files" (2026-07-15) | +| Remote | `origin git@github.com:StarlightDaemon/CTRL.git`, PUBLIC, no releases, no open issues/PRs | +| Working tree | Dirty: 19 entries (see Changes and Verification). Pre-existing; preserved. | +| Instruction files | `AGENTS.md` (RAIDEN control plane; forbids `Co-Authored-By`, enforced by `.git/hooks/commit-msg`), `.raiden/` (21 tracked files: state, decisions, open loops), `.serena/` (8 tracked), no `CLAUDE.md` | +| State/decision docs | `.raiden/state/CURRENT_STATE.md`, `OPEN_LOOPS.md` (OL-001..OL-016), `DECISIONS.md` (D-001..D-006), `WORK_LOG.md` | +| Build config | `extension/wxt.config.ts` (WXT 0.20.27, Vite 7.3.5, React 18.2), `tsconfig.json`, `vitest.config.ts`, `playwright.config.ts`, `eslint.config.js`, `tailwind.config.js` | +| Prior audits (secondary evidence) | `.audits/CTRL_ADVERSARIAL_ENGINEERING_AUDIT_2026-09-08.md` and five earlier audits (untracked, ignored); ~200 historical reports under `reports/`, `docs/reports/` (ignored) | + +No ambiguity about the target repository. + +## 3. Evidence Standard Applied + +Findings are tagged **CONFIRMED** (traced in current source or reproduced by command), **HIGH-CONFIDENCE** (traced, depends on documented browser behaviour not executed here), **PLAUSIBLE — VERIFY**, or **UNKNOWN**. The 2026-09-08 adversarial audit and RAIDEN open loops were used as leads only; every claim carried forward was re-traced in the current tree. + +## 4. External Requirements Checked (2026-09-08/09) + +Summarised here; per-item citations live with the findings. + +Chrome: MV3 mandatory; single narrow purpose; minimum functionality; privacy policy mandatory when handling any user data including "authentication information… even when… stored locally"; in-UI prominent disclosure (a privacy-policy link alone is insufficient); 2026 policy update (enforced from 2026-08-01) requires data collection to be strictly necessary and prominently disclosed; no remotely hosted code; narrowest permissions; per-permission justification fields in the dashboard; `version` = 1–4 integers 0–65535; CSP `script-src`/`object-src`/`worker-src` limited to `'self' 'none' 'wasm-unsafe-eval'` (`connect-src` not restricted in text); alarms ≥ 0.5 min; SW idle 30 s, kept alive by API calls / ports (114+) / WebSocket traffic (116+); `storage.session` 102+, trusted contexts by default; icons 128 px; screenshots 1280×800 or 640×400 (1–5); small promo tile 440×280 required; manifest `description` ≤ 132 chars; broad `*://*/*` host patterns (even optional) trigger deeper review; developer registration fee + email verification + trader/non-trader declaration; policies prohibit facilitating unauthorized access to copyrighted content (no BitTorrent-specific text). Chrome LNA (142+) restricts web origins; extension exemption tied to granted host permission (secondary source only). + +Firefox/AMO: `background.service_worker` unsupported — `background.scripts` event page required (both keys allowed for cross-browser); `browser_specific_settings.gecko.id` mandatory for MV3 signing; `strict_min_version` ≥ 115/128 for signature validity; `gecko.data_collection_permissions` mandatory for all new AMO submissions since 2025-11-03 (`required: ["none"]` or specific categories such as `authenticationInfo`); MV3 host permissions optional-by-default, `optional_host_permissions` from Firefox 128, `permissions.request` must be called synchronously inside a user-action handler; default MV3 CSP adds `upgrade-insecure-requests` (a custom `extension_pages` CSP replaces it); Add-on Policies (2026-04-30): no surprises, data transmission minimal with consent, must use encryption when transporting data remotely, no remote code, unmodified release third-party libraries, minified OK / obfuscated not, **source code submission required for bundled/minified code** with OS + tool versions + exact commands + lockfile, reviewer rebuilds and diffs — must be identical (default reviewer env Ubuntu 24.04.4, Node 24.14.0, npm 11.9.0); listing: summary ≤ 250 chars, icons 32/64 (+128), screenshots 1280×800, notes-to-reviewer with test credentials when login is needed; unsigned extensions cannot install in release Firefox; `web-ext lint`/addons-linter is the validator. + +Cross-browser conflicts that a single manifest cannot paper over: background key shape; gecko block only for Firefox; CSP default differences; host-permission grant model; `chrome.*` promise support (Firefox yes; Chrome `browser.*` only 148+); data-consent surfaces (dashboard form vs manifest key); source-code submission (AMO only). + +## 5. Reconstructed Product Model + +### 5.1 Purpose + +CTRL is a browser-action extension that lets a user who already runs a BitTorrent client with a web API (qBittorrent, Transmission, Deluge, Flood, ruTorrent, µTorrent, BiglyBT, Vuze, Aria2) add magnet links / torrent URLs to that client and see or control its queue from the browser toolbar and right-click menu. It stores the client's URL and credentials locally, encrypted under a user-chosen master password, and talks only to the user-configured server. + +- Primary user: self-hoster / seedbox user with one or more torrent clients. +- Primary workflow: right-click a magnet link → "Add to Torrent Control" (or paste a link in the popup) → torrent appears in the client. +- Secondary workflows: pick active server; view active torrents / speeds (popup mini-list, options-page virtualized list with pause/resume/remove); badge with count or speed; "Scan Page for Magnets"; backup/import settings; open the client's Web UI. +- Entry points: toolbar popup (400×600), options page (full tab), context menu items on links/selection/page, browser notifications, action badge. No content scripts, no side panel, no commands/shortcuts, no override pages. +- What it observes/transmits: only on user action; sends magnet/URL plus credentials to the configured server; reads `a[href^="magnet:"]` from the current tab only via the "Scan Page" menu item (activeTab + scripting). Background polls the configured server (every 2 s while a UI port is open; every 60 s via alarm otherwise) to refresh badge/state. +- What makes it useful: one-click hand-off of links to a self-hosted client without opening its Web UI; multi-server switching. + +**One-sentence reviewer description (draft):** "CTRL lets you send magnet links to, and monitor, the BitTorrent client you already run (qBittorrent, Transmission, and others) directly from your browser; your client address and login are stored encrypted on your device and nothing is sent anywhere else." + +Multiple purposes? The core is single-purpose. Two surfaces stray: the Utilities page's links to third-party torrent-cache / privacy-check websites (`src/shared/lib/resources.ts`, `Utilities.tsx`), and the popup "Debug" tab. Both should be removed (PROD-01, UX-02). + +### 5.2 User journey (as implemented) + +1. **Install** — no `onInstalled` onboarding; only context-menu rebuild (`ContextMenuService.ts:66-69`). Chrome shows "Display notifications" as the only install warning; no host access granted. +2. **First popup** — `features/torrent-control/ui/Dashboard.tsx:124,199-224` shows "Extension not configured." + **Setup Now** because `useSettings` only loads servers when the vault is initialised and unlocked. Popup tabs: Control / Settings (placeholder that opens options) / Debug (no-op in production, `Popup.tsx:81-103`, `wxt.config.ts:91`). +3. **Setup Now → options** — `VaultGuard` → `SetupVault` (master password ≥ 8 chars, warns it cannot be recovered). No explanation of *why* a master password is needed before the first server. After creation, landing view is the torrent Dashboard with placeholder stat cards ("Storage Health: Unknown", "Connection: Online" hard-coded) rather than the Servers tab. +4. **Add server** (`ServerConfigPanel.tsx`) — name, client type, protocol/host/port (path is stripped on edit; breaks Aria2 `/jsonrpc` and reverse-proxy sub-paths), username, password. Test Connection and Save are disabled until the per-origin host permission is granted through the "Grant Local Access / Grant Permission" button (`permissions.ts:36-40`, called synchronously in the click handler — correct for both browsers). Vault must be unlocked to save. Remove uses `window.confirm`. +5. **Normal use** — popup shows server tile, status line, quick-add, first 3 torrents (count badge shows all), Web UI / Test / Open Settings. Context menu: Add, Add Paused, Scan Page, per-server / per-label / per-path submenus (English only). Options Dashboard: virtualized list with hover actions, global speeds. +6. **Error states** — popup Test collapses every failure to "Failed"; options Test shows the adapter's user message. Adapter failures surface as text; no retry guidance. +7. **Settings** — many controls persist values nothing reads (theme, layout, notification level/style, Open Web UI toggle, performance, enhanced diagnostics). +8. **Locked vault / missing permission** — after any browser restart the session key is gone, so the popup reverts to "Extension not configured" with no unlock control (UX-01). If host permission is later revoked, background fetches fail with a generic error; there is no `permissions.onRemoved` handling. +9. **Update** — `purgeLegacyFallbackKey` runs on every SW wake (dirty tree); no other migration. Settings deep-merge with defaults on load (`useSettings.ts:82-90`). +10. **Uninstall** — storage removed by the browser; no cleanup needed. Export/backup exists but "safe" export leaks `clientOptions.simpleApiKey` (SEC-03). + +Unclear/surprising points: master password before any value; "not configured" when locked; Storage Health placeholder; Debug tab; settings that do nothing; English-only context menus and most UI on non-English locales; count badge vs 3-row list; `alert()`/`confirm()` dialogs; page reload after import. + +### 5.3 Architecture + +``` + Toolbar popup (React, popup.html) Options page (React, options.html, opened in tab) + Dashboard.tsx ──GET_TORRENTS/ADD/TEST──┐ App.tsx > VaultGuard > Dashboard.tsx + polls every 2 s via runtime.sendMessage │ useTorrentPoller: runtime.connect('ctrl-active-session') + │ + UPDATE_VIEWPORT / VIEWPORT_UPDATE / VIEWPORT_DIFF / STATS_UPDATE + ▼ │ + ┌───────────────── background.ts (Chrome SW / Firefox event page) ─────────────────┐ + │ isTrustedSender(sender.id === runtime.id) │ + │ ServerResolver.resolve() → VaultService (AES-GCM, PBKDF2-300k, key in storage.session) + │ ClientFactory.create(config) → dynamic import of 1 of 9 adapters (ITorrentClient) │ + │ performCheck(): setInterval 2 s while ports open; alarm 'packet_beat' every 1 min │ + │ ViewportManager → TorrentDiffer (RFC6902 patches) → StateHydrator (storage.session) │ + │ ContextMenuService (@singleton): rebuild on storage watches; onClicked → addTorrentUrl + │ 'scan-page' → scripting.executeScript (activeTab) → a[href^=magnet:] │ + │ Badge (count/speed), notifications │ + └──────────────────────────────┬────────────────────────────────────────────────────┘ + ▼ + FetchHttpClient (credentials:'omit', 10 s abort, JSON coercion) + JsonRpcClient (aria2) │ adapter-private fetch (qBittorrent, BiglyBT simple API) + ▼ + User's torrent client Web API (http/https, LAN or remote) +Storage: local:options (settings), local:vaultSalt, local:vaultData (ciphertext), session:encryptionKey, session:torrent_state +``` + +Components: manifest generated by `wxt.config.ts` (single config; WXT emits `service_worker` for Chrome, `scripts` for Firefox); no content scripts; no side panel; no commands; DI via `tsyringe` decorators but no container (`@injectable`/`@singleton` are decorative); state: React hooks + one Zustand store (`useTorrentStore`) for the virtualized list; i18n via `browser.i18n.getMessage` in 7 of 41 UI files; tests: Vitest (jsdom + `@webext-core/fake-browser`) 16 files / 530 tests, Playwright 6 CI tests + 3 `@integration`; CI: lint → typecheck+unit → build both → Chrome e2e; no release automation; docs: README, ROADMAP, docs/* (several stale); no telemetry, no error reporting, no analytics (confirmed by grep and bundle scan: only `s81c.com` font URLs, `wxt.dev` doc strings, and the Utilities links). + +## 6. Repository Inventory (high-signal) + +| Area | Role | Notes | +|---|---|---| +| `extension/src/entrypoints/` | `background.ts` (451 lines), `popup/`, `options/`, `style.css` | Only three entrypoints; `style.css` imports full Carbon + Plex CSS | +| `extension/src/shared/api/clients/` | 9 adapters (~7.2k lines) + `shared/` AdapterError | Largest area; Transmission 1,068 lines, BiglyBT 1,032 | +| `extension/src/shared/api/network/` | `FetchHttpClient`, `JsonRpcClient`, `HttpError`, `HeaderRewriter` (dead no-op) | Single transport policy — foundational defect | +| `extension/src/shared/api/security/` | `SecurityService`, `VaultService`, `KeyManager` | OL-012 remediation uncommitted | +| `extension/src/shared/api/server/ServerResolver.ts` | Vault → active server resolution | Used by background + context menu | +| `extension/src/features/torrent-control/` | `model/` hooks (`useSettings` 419 lines, `useVault`, `useTorrentPoller`), `model/services/ContextMenuService.ts` (484), `services/` (Lifecycle, Hydrator, Viewport), `ui/` (13 components + settings/) | Feature-sliced naming but `services/` vs `model/services/` split is arbitrary | +| `extension/src/entities/` | `Torrent`, `ServerConfig`, `ITorrentClient`, `ClientFactory`, `TorrentRow` | Second `ITorrentClient.ts` copy in `features/.../model/types/` — duplicate | +| `extension/src/shared/lib/` | constants (CLIENT_LIST, DEFAULT_OPTIONS), diff, retry (two retry helpers), websocket (unused), i18n (three unused helper modules), permissions, network (`isPrivateIP`), `buildInfo.ts` (generated, tracked) | | +| `extension/src/shared/ui/` | Carbon-based layouts, vault screens, `CommandPalette` (empty), `DebugOverlay`, `Toast` (unused), `PlaceholderPage`/`PageHeader`/`Card` (unused) | | +| `extension/src/public/` | `_locales/{de,en,es,fi,fr,ru,zh_CN}`, icons 16–128, fonts Inter/JetBrains (unused) | | +| `extension/scripts/` | `generate-build-info.ts` (rewrites tracked file), `zip-source.ts` (git-archive source package, requires clean tree), `backup.ts` (copies src to `../backups/`), icon generators, `translator/index.js` (placeholder injector), `launch-setup.mjs` | | +| `extension/tests/unit/` | 10 adapter suites, ContextMenuService (24 it), KeyManager (new), TorrentDiffer, withRetry, LifecycleAdapter.parseDOM, sanity | No VaultService/SecurityService/background/useSettings tests | +| `extension/tests/e2e/` | fixtures + 5 specs (6 CI tests, 3 integration) | Chrome only | +| `.github/workflows/` | `ci.yml`, `auto-localize.yml` | No release, no lint of build, no Firefox e2e | +| `docs/` | 11 top-level docs + 31 `reference/` prompt/architecture files + 4 archived Synology docs tracked; `docs/reports/`, `docs/archive/` ignored | Heavy process artefacts in public repo | +| `.raiden/`, `.serena/` | Agent control-plane state (tracked, public) | Contains operational history incl. exposed-key map (D-005) | +| `extension/backups/`, `build_log.txt`, `audit_extension*.txt`, `playwright-report/`, `test-results/` | Local artefacts (ignored) | Swept into WXT sources zip | +| `.audits/`, `reports/`, `logs/`, `audit-reports/` | Ignored local audit outputs | | + +Flags: duplicated `ITorrentClient` interface; two retry helpers with different semantics; three i18n helper modules unused; `HeaderRewriter` dead; `WebSocketKeepalive`/`ServiceWorkerKeepalive`/`parseDOM`/`XmlRpcHelper` no callers; qBittorrent File/Tracker/Transfer/Sync/RSS/Search services exported but unused (~1k lines + tests); `tsyringe`+`reflect-metadata`+legacy decorators+`emitDecoratorMetadata` for zero DI benefit (and the source of `DANGEROUS_EVAL` in `background.js`); `babel-plugin-react-component-data-attribute` applied in production builds; hidden coupling: background, context menu, popup and options each re-resolve the vault independently; implicit global state: `activeClient`, `activePorts`, `pollingInterval` module variables in the SW. + +## 7. Code and Correctness Audit + +Static baseline: `tsc --noEmit` clean; ESLint 0 errors / 31 warnings (unused symbols, `any`); 530 unit tests green. The green suite does not exercise browser transport semantics, cross-context lifecycle, or background concurrency, which is where the material defects sit. + +### Confirmed defects + +| ID | Location | Defect | +|---|---|---| +| ADP-01 | `clients/deluge/DelugeAdapter.ts:45-105,159-221`; `network/FetchHttpClient.ts:39-44` | `auth.login` succeeds but the `_session_id` cookie is never stored (`credentials:'omit'`) nor forwarded; every later RPC returns "Not authenticated"; `ensureAuth` re-logs-in and fails again. | +| ADP-02 | `clients/rutorrent/RuTorrentAdapter.ts:53-64`; `FetchHttpClient.ts:154-169` | XML-RPC string body is `JSON.stringify`'d and `Content-Type` overwritten to `application/json`. Reproduced by the 2026-09-08 audit harness; re-traced here. | +| ADP-03 | `clients/flood/FloodAdapter.ts:133-151,565-571` | Flood authenticates via httpOnly `jwt` cookie only; the `token`/Bearer branch never executes; cookie discarded → 401 on `api/auth/verify`. HIGH-CONFIDENCE. | +| ADP-04 | `clients/utorrent/UTorrentAdapter.ts:48-58,389-394`, `UTorrentRssService.ts:66-71`, `UTorrentSettingsService.ts:75-80` | Reads `Set-Cookie` (forbidden response header, always null) and sets `Cookie` (forbidden request header, dropped); GUID never established → HTTP 400 loop → misreported as AUTH_FAILED. HIGH-CONFIDENCE. | +| ADP-05 | `FetchHttpClient.ts:31-33,105-107`; `QBittorrentAdapter.ts:326-330`; `HeaderRewriter.ts` (no-op) | `Origin`/`Referer` are forbidden request headers; the browser sends `Origin: chrome-extension://`; qBittorrent's default CSRF check rejects it → 401 → adapter enters 16 s cooldown / `IP_BANNED`. Localhost is exempt by qBittorrent default, which is why prior testing passed. HIGH-CONFIDENCE (OL-013 records the same gap). | +| ADP-06 | `TransmissionAdapter.ts:66-67,223` | Absolute `/transmission/rpc` discards a reverse-proxy sub-path; BiglyBT uses the relative form correctly. | +| ADP-07 | `Aria2Adapter.ts:32-33`, `ServerConfigPanel.tsx:230-275`, `constants.ts:97-101` | `/jsonrpc` is not appended by the adapter and is stripped by the host/port form; saved endpoint points at `/`. | +| ADP-08 | `QBittorrentAdapter.ts:445-458,209-221` | qBittorrent 5.x `stoppedDL/stoppedUP` unmapped → `unknown`; `torrents/pause|resume` vs 5.x `stop|start` unverified. | +| ADP-09 | `DelugeAdapter.ts:792`, `UTorrentAdapter.ts:457-474` | `addedDate` in seconds; entity and other adapters use ms. | +| ADP-10 | `FetchHttpClient.ts:39-44` vs `DelugeAdapter.ts:76-100`, `BiglyBTAdapter.ts:774-786`; `Aria2Adapter.ts:478-550`; `withAdapterRetry.ts` | Caller `AbortSignal` overwritten by the 10 s client signal (dead timeouts); aria2 timeout classified retryable → `addUri` may execute 2–5 times; `withAdapterRetry` retries auth/validation failures. | +| ADP-11 | ruTorrent / aria2 / Deluge / uTorrent | Unsupported operations (`removeTorrent(id,true)`, `paused`, `path`, tags) silently succeed. | +| ADP-12 | `RuTorrentAdapter.ts:53-77,197-200` | `testConnection` reports true for any HTTP 200 (login page, proxy HTML). | +| ARCH-01 | `background.ts:46,152-178,282-286,306-396`; `TorrentRow.tsx:20-33` | One mutable `activeClient`; poll results and `PAUSE/RESUME/REMOVE` messages carry no server id or generation; a slow poll from server A lands in server B's UI; a remove can hit the wrong server. | +| ARCH-02 | `TorrentDiffer.ts:40-87,98-132` | Patches keyed by ID at compute time but applied by array index; reorder yields cross-torrent value assignment (reproduced by the 2026-09-08 audit). | +| ARCH-03 | `useVault.ts:11-67`, `VaultGuard.tsx` | Lock is component-local; a second options window keeps decrypted servers and can export them. | +| ARCH-04 | `background.ts:197-205,224-236`; popup `Dashboard.tsx:69-92` | `performCheck` has no in-flight guard; popup polls `GET_TORRENTS` every 2 s independently of the port/viewport pipeline; alarm polls every minute even when badge = none. | +| ARCH-05 | `ViewportManager.ts:35-81`, `VirtualizedTorrentList.tsx:32-40` | No initial snapshot for a second subscriber with the same range; total only delivered with viewport messages; inclusive virtualizer end vs exclusive `slice`. | +| ARCH-06 | `useSettings.ts:15-28,167-206,315-384`; `App.tsx:32-63` | Full backup uses a stale closure; empty-server import "succeeds" without writing; import accepts any `type` / `application` / URL scheme. | +| SEC-01 | `VaultService.ts:15-17,54-88` | Salt present + ciphertext absent → `unlock()` succeeds with any password (fail-open on corrupted or partial state). | +| BUG-01 | popup `Dashboard.tsx:332` | ProgressBar checks `'Downloading'`; statuses are lowercase → always "finished". | +| BUG-02 | `background.ts:238`, `useTorrentPoller.ts` | `if (activePorts > 0)` at init is always false; after SW restart with the options page open, polling never resumes and the poller does not reconnect. | +| BUG-03 | `background.ts:311-317` | "Vault Locked" notification ignores `enableNotifications`. | + +### Lifecycle observations (Chrome SW / Firefox event page) + +- Fast polling uses `setInterval` inside the SW; it survives only because each tick calls extension APIs (badge, `sendMessage`) that reset the 30 s idle timer. A fetch that hangs longer than 30 s without API activity can let the SW die mid-poll; state is rehydrated from `storage.session` on wake, but `activeClient` session objects (Transmission session id, qBittorrent SID) are rebuilt from scratch, re-logging-in every wake. +- Alarm `packet_beat` (1 min) is recreated on every SW start (fine, above the 0.5 min floor). +- Context menus: rebuilt on install / startup / every storage watch event with `removeAll()` then `create()`; debounce coalesces; acceptable. +- Session key in `storage.session` (default trusted-contexts) is the correct MV3 pattern for both browsers once the dirty-tree change lands. +- `chrome.*` promise style is used throughout; works in Firefox; WXT `browser` alias used for storage/session. No SW-only globals in shared code (grep: none). +- Incognito: not declared (Chrome default "spanning"). AMO's "data from private browsing must not be stored" is satisfied because no page data is stored. + +### Probable defects / risks + +- `toMatchPattern` uses `origin + '/*'`; for `ws://` hosts the pattern would be `ws://host/*`, which is not a valid Chrome match-pattern scheme; `optional_host_permissions` lists `ws://*/*` which Chrome ignores with a warning. PLAUSIBLE — VERIFY, moot once `ws`/`wss` are removed. +- `info.selectionText` is passed verbatim to `addTorrentUrl` (`ContextMenuService.ts:353`), so arbitrary selected text reaches the client API (low risk; the client rejects it). +- `btoa()` throws on non-Latin-1 passwords for all Basic-auth adapters. + +## 8. Browser Extension Architecture Audit + +- **MV3 suitability:** appropriate. No persistent-background assumptions beyond the polling interval noted above; hydration from `storage.session` is the right pattern. +- **Portable core:** yes. One codebase; WXT generates `service_worker` for Chrome and `scripts` for Firefox. Browser branching is by `navigator.userAgent.includes('Firefox')` (`ContextMenuService.ts:10`) and `getBrowserInfo` feature detection (`LifecycleAdapter.ts`); acceptable. +- **Missing per-browser manifest keys:** `browser_specific_settings.gecko.{id,strict_min_version,data_collection_permissions}` (Firefox) and `minimum_chrome_version` (Chrome). WXT supports `manifest: (env) => ...` branching on `env.browser`; this is the cleanest fix. No adapter layer needed. +- **CSP:** `script-src 'self'; object-src 'self'; connect-src http: https: ws: wss:` is within both browsers' constrained-directive rules. Because a custom `extension_pages` CSP replaces Firefox's default (which includes `upgrade-insecure-requests`), plain-HTTP LAN targets should work in Firefox. PLAUSIBLE — VERIFY at runtime. Consider adding `default-src 'self'` once remote font URLs are removed so the "no external requests" claim is enforced by policy. +- **Host permissions:** none required at install; `optional_host_permissions` `http://*/*`, `https://*/*` (+ `ws`/`wss`, unused); granted per-origin from a click handler (synchronous, which satisfies Firefox's user-action rule). Chrome's review process flags broad optional patterns; justification text needed. +- **Declarative vs imperative:** DNR removed (good); `scripting.executeScript` under `activeTab` for the one-shot scan is the documented pattern. +- **Namespace:** `chrome.*` everywhere, `browser` from WXT for storage. Fine for Chrome ≥ 102 and Firefox ≥ 115/128. +- **Firefox-specific behaviour:** `getBrowserInfo` detection, longer menu debounce, `background.scripts`. `storage.session` requires Firefox ≥ 115 → `strict_min_version` should be `128.0` (optional_host_permissions) or `140.0` (built-in consent UI). +- **Dual-store safety:** unsafe today only because of the missing gecko block, reproducibility, and the transport defects; the architecture itself does not need restructuring. + +**Recommended strategy:** keep the unified codebase and unified `wxt.config.ts`; add `env.browser` branches for the gecko block and `minimum_chrome_version`; keep the two build targets. No adapters, no polyfill library, no separate manifests. + +## 9. Permissions Audit + +| Permission | Browser | Req/Opt | Feature | Evidence | Necessary? | Narrower alternative | Store risk | +|---|---|---|---|---|---|---|---| +| `storage` | both | required | settings, vault, session key, hydration | `useSettings.ts`, `VaultService.ts`, `KeyManager.ts`, `StateHydrator.ts` | Yes | none | none (no warning) | +| `contextMenus` | both | required | Add / Scan / Server / Label / Path menu items | `ContextMenuService.ts` | Yes (core workflow) | none | none | +| `notifications` | both | required | result/error/vault-locked toasts | `ContextMenuService.ts:471-483`, `background.ts:311` | Marginal — two call sites; completion alerts claimed in docs are not implemented | make optional and request when the user enables notifications | Chrome shows "Display notifications" warning at install; the only install-time warning today | +| `activeTab` | both | required | one-shot magnet scan on the current tab | `ContextMenuService.ts:373-401` | Yes for Scan Page | drop the feature → drop both `activeTab` and `scripting` | low; must be justified together with `scripting` | +| `scripting` | both | required | `executeScript` for Scan Page | same | Yes if Scan Page ships | see above | medium — reviewers ask why an extension with no content scripts needs `scripting`; justification must name the single function | +| `alarms` | both | required | 1-minute background poll for badge | `background.ts:224-236` | Yes if badge/background polling ships | gate the alarm on `badgeInfo !== 'none'`, or drop background polling | none | +| `optional_host_permissions` `http://*/*`, `https://*/*` | both | optional, per-origin at runtime | fetch to the user's client | `permissions.ts`, `ServerConfigPanel.tsx:58-67` | Yes — user-defined origins | nothing narrower is possible; keep runtime per-origin grants | Chrome: broad pattern → deeper review even though optional; Firefox 128+: fine | +| `optional_host_permissions` `ws://*/*`, `wss://*/*` | both | optional | nothing (WebSocket keepalive has no callers) | grep: no `new WebSocket` outside the unused module | **No** | remove | unnecessary permission = release defect (PERM-01, P1) | +| `externally_connectable` | — | absent | — | — | correct | — | — | +| `web_accessible_resources` | — | absent | — | — | correct | — | — | +| `declarativeNetRequest` | — | removed 2026-07-02 | — | `HeaderRewriter.ts` no-op | correctly absent | — | do not re-add without live evidence | + +Permission-related defects: PERM-01 unused `ws`/`wss` optional hosts (P1, CONFIRMED); PERM-02 `notifications` should be optional or its use expanded to match documentation (P2); PERM-03 no per-permission justification text exists anywhere in the repo for the Chrome dashboard or AMO notes (P1, documentation). + +## 10. Security Audit + +- **Message passing:** `onMessage` / `onConnect` reject senders whose `id !== runtime.id` (`background.ts:206-215,290-296`); no `externally_connectable`; no content scripts → web pages cannot reach the background. CONFIRMED sound. Message payloads (`message.config`, `message.url`, `serverIndex`) are trusted from own UI; `serverIndex` is bounds-checked. +- **Injected script:** `scan-page` injects a fixed function reading `href` attributes only; results are strings passed to `addTorrentUrl`. No page-controlled code execution. A page can plant fake magnet links, which the user explicitly asked to scan; acceptable, but the loop adds *all* found magnets with no confirmation and no cap (SEC-06, P2: a hostile page can enqueue hundreds of torrents in one click). +- **DOM / HTML / eval:** no `innerHTML`, `dangerouslySetInnerHTML`, `eval`, or `new Function` in `src` (grep). addons-linter warnings come from React DOM (`innerHTML` in the SVG namespace path) and `reflect-metadata`'s `Function("return this")` in `background.js` (DEP-01). Both are third-party and unmodified, therefore acceptable, but removing `tsyringe` / `reflect-metadata` removes the `DANGEROUS_EVAL` warnings entirely. +- **Remote code / config:** none. Remote *resources*: 105 `https://1.www.s81c.com/...` font URLs in shipped CSS (PRIV-01). +- **URL handling:** `openWebUI` prepends `http://` and calls `tabs.create` with the stored hostname (own config) — fine. `ClientFactory.validate` accepts any parseable scheme; a `javascript:` hostname from an imported backup could reach `tabs.create` (SEC-08, P3: restrict schemes to http/https at validation and import). +- **Secrets at rest:** credentials AES-GCM-256 under PBKDF2-SHA256 300k with 16-byte salt and a fresh 12-byte IV per write; session key in `storage.session` only (after the dirty-tree change). Prior builds mirrored the key to `storage.local` on Firefox (OL-012); the uncommitted remediation purges it on every wake. No secrets in source. The public Chromium omnibox key in a PR ref (OL-011) is not a CTRL credential. +- **Secrets in transit / logs:** BiglyBT Simple API key in the query string (`BiglyBTSchema.ts:370-419`) → server logs; "safe" export leaks `clientOptions.simpleApiKey` (SEC-03, P1); `QBittorrentAdapter.ts:97` logs the login response body; no password logging found. +- **Vault integrity:** SEC-01 fail-open on salt-only state (P2); no versioning of the vault record; no lockout on wrong master password (PBKDF2 cost is the only brake — acceptable). +- **Cross-window lock:** ARCH-03. +- **Dependency supply chain:** see §12; lockfile tracked; overrides pin transitive advisories; `postinstall: wxt prepare` is the only install script of note. +- **Build process:** `generate-build-info.ts` writes into `src/` during build; `backup.ts` copies `src` outside the repo. Neither is malicious; both surprise reviewers. + +Abuse scenarios: (1) a page with 500 hidden magnet links + one "Scan Page" click → 500 torrents added, no confirmation (SEC-06). (2) A profile-directory reader on an old Firefox build recovers the vault key from `storage.local` (mitigated by the pending purge). (3) A shared "safe" export reveals a BiglyBT API key (SEC-03). + +## 11. Privacy and Data-Flow Audit + +| Data | Source | Purpose | Stored? | Location | Transmitted? | Destination | Retention | User control | Disclosure required | +|---|---|---|---|---|---|---|---|---|---| +| Client URL, username, password, HTTP-auth creds, `clientOptions` (may hold BiglyBT API key) | user input / import | connect to client | Yes, AES-GCM | `local:vaultData` | Yes, to the configured client only | user's server | until removed / uninstall | edit, delete, export | Chrome: authentication information (privacy policy + dashboard form + in-UI disclosure); AMO: `data_collection_permissions` value + policy | +| Master password | user input | derive key | No (derived key as exportable JWK in `storage.session`) | memory / session | No | — | browser session | — | describe in policy | +| Settings | user | preferences | Yes | `local:options` | No | — | until uninstall | yes | minimal | +| Torrent list snapshots (names, sizes, paths, speeds) | client API | UI, badge | Yes (session) | `session:torrent_state` | No | — | browser session | none | mention (torrent names are user content) | +| Magnet links / URLs | click, selection text, page scan | add to client | No | — | Yes | user's server | — | user-initiated | yes; page-content read via `activeTab` must be disclosed (Chrome "website content" category likely) | +| Current tab page | Scan Page only | find magnets | No | — | No (only hrefs extracted) | — | — | user-initiated | yes | +| Fonts | IBM CDN (`s81c.com`) if any `@font-face` resolves remotely | rendering | browser cache | — | request reveals IP/UA to IBM | third party | — | none | contradicts "no external servers" (PRIV-01, PLAUSIBLE — VERIFY) | +| User agent, platform, language | `navigator` | Self Test display | No | — | No | — | — | — | none | +| Backup files | export | user backup | user's disk | — | — | — | — | — | "safe" export must actually be safe (SEC-03) | +| Analytics / crash / telemetry | — | — | none | — | none | — | — | — | truthful today | + +Disclosure gaps (PRIV-02, P1): `docs/PRIVACY_POLICY.md` says "notify you when downloads complete" (not implemented), lists `storage / contextMenus / notifications / activeTab / optional_host_permissions` but not `scripting` or `alarms`, says "No network requests to external servers" (see fonts), and there is no in-UI disclosure when credentials are entered (Chrome requires in-UI disclosure, not policy-only). README claims "Translated into 7 languages"; BETA_TESTING claims "Firefox: fully tested", "Completion alerts", and a theme list — none supported by the code. `docs/privacy.html` exists but no hosted URL is referenced anywhere. + +Proportionality: collection is minimal and appropriate. Background polling every minute contacts the user's server continuously while the vault is unlocked, even with the badge disabled; disclose or gate it. + +## 12. Dependency and Supply-Chain Audit + +Runtime deps (15): React 18.2, `@carbon/react` 1.100, `@carbon/styles`, `@carbon/icons-react`, `@ibm/plex` 6.4 (entire family → 1,058 font files), `lucide-react` (second icon set), `@tanstack/react-virtual`, `zustand`, `zod` 3.23, `tsyringe` + `reflect-metadata` (decorators only, no container → unnecessary; brings `Function()` into `background.js`), `txml` (XML for ruTorrent; no XXE surface), `clsx`, `tailwind-merge`. + +Dev deps: WXT 0.20.27, Vite 7.3.5 (transitive), Vitest 4.1.9, Playwright 1.57, TypeScript 5.7, ESLint 9, Babel legacy-decorators plugin, `babel-plugin-react-component-data-attribute` (applied in production builds — adds `data-component` attributes and bundle weight), `vite-plugin-react-inspector` (dev-only, correctly gated), `sharp` (icon generation). + +- `npm audit --omit=dev`: 0 vulnerabilities (the production package is clean). +- `npm audit` (all): 12 (6 high) — `sharp < 0.35.4` (dirty tree bumps to 0.35.3, still vulnerable), `undici 7.x`, `nanoid`, `postcss-selector-parser`, `js-yaml`. All dev-only; none enters the store package. +- Lockfile: tracked, `npm ci` in CI, `npm ls` consistent with the installed tree. The working tree's `package.json` / lockfile edits (sharp, postcss, three new overrides) are uncommitted. +- Pinning: exact pins except `@carbon/*`, `@ibm/plex`, `postcss` (caret). Node: `.nvmrc` 22, CI 22, docs say 20+, local 24.18 (works). No `engines` field. +- Held migrations (OL-005 Babel 8, OL-006 Vite 8): correct to hold. + +Classification: release-critical — none in production. Advisable before release — remove `tsyringe` / `reflect-metadata` / legacy decorators (DEP-01, also clears the linter warnings); stop bundling all of `@ibm/plex` and the full `@carbon/styles` CSS (PKG-01); gate `babel-plugin-react-component-data-attribute` to dev (DEP-03). Safe to postpone — `sharp` 0.35.4, `undici` / `nanoid` transitive fixes. Unnecessary — Vite 8, Babel 8, React 19, Carbon majors. + +## 13. Build and Reproducibility Audit + +- Build: `npm run build:chrome` = `tsx scripts/generate-build-info.ts && wxt build -b chrome`; `build:firefox` the same with `-b firefox --mv3`. `npm run build` also runs `clean` (`rm -rf`) and `backup` (copies `src` to `../backups/`). +- Package: `wxt zip -b chrome` / `wxt zip -b firefox --mv3` → `builds/*.zip` (+ WXT auto `-sources.zip` for Firefox). AMO source: `scripts/zip-source.ts` (git archive of HEAD; refuses a dirty tree, so it cannot run today). +- Determinism: JS chunks identical across three builds; `assets/style.css` differs between consecutive builds because Vite's font asset numbering (`style.woff` vs `style2.woff`) is not stable → the Firefox reviewer diff would fail (BUILD-01, CONFIRMED). `generate-build-info.ts` rewrites tracked `src/shared/lib/buildInfo.ts` with a wall-clock timestamp (shown in `VersionOverlay`) → every build dirties the tree and differs (a stash of exactly this exists). +- Included: manifest, 4 JS, 2 CSS (~1 MB each), 7 locales, 5 icons, 2 local fonts, 1,058 Plex woff/woff2 → 40.67 MB. No source maps (`sourcemap: isDev`), no tests, no `.env`, no dev files. +- Per-browser difference: only the `background` key and `version_name`. No `browser_specific_settings`. +- Versioning: manual in `package.json`; manifest `version` normalised `0.2.0-beta.1 → 0.2.0.1`; manifest `name` embeds the version ("CTRL v0.2.0-beta.1"), so the store listing name would change per release (STORE-01). +- Environment documentation: `docs/DEVELOPMENT.md` (Node 20+, commands) — no exact versions, no AMO build instructions, no OS statement. +- Reproduction on another machine: partially — lockfile + `npm ci` + WXT works (CI proves it), but output is not byte-identical (fonts, buildInfo). + +Missing for Firefox: deterministic asset naming or removal of bundled Plex; removal of timestamp generation from the build; a `BUILD.md` in the source archive with OS / Node / npm versions and exact commands; a CI "build twice and diff" gate; documented checksums linking source archive to uploaded XPI. + +## 14. Package Inspection (performed) + +Chrome build (`builds/chrome-mv3`, 1,079 files, 41 MB) and Firefox build (`builds/firefox-mv3`, same content, `background.scripts`): + +| Check | Result | +|---|---| +| Manifest | MV3, valid; `name` includes version; `description` generic ("Manage your torrents from the browser"); no `minimum_chrome_version`; no gecko block; `version_name` dropped for Firefox (correct) | +| Assets | icons 16/32/48/64/128 present; `_locales` 7; Plex fonts 1,058 files ≈ 38 MB; unused Inter/JetBrains fonts 80 KB | +| Unwanted files | none of test/dev/env type; but the font payload is unwanted | +| Source maps | none | +| Secrets | none | +| Debug code | 77 `console.log/debug/info` call sites in `src` remain in the production bundle; `VersionOverlay` build stamp visible; `SELF_TEST` exposes UA/platform in the UI | +| Remote references | 105 `s81c.com` font URLs (CSS), a `carbondesignsystem.com` docs string, a `wxt.dev` string, 6 Utilities links | +| Duplicate bundles | Carbon stylesheet emitted twice (`style.css` 984 KB and `global-*.css` 1,000 KB) because `style.css` imports `global.css` and each entry imports both | +| Licenses | no third-party notices in package or repo (Carbon Apache-2.0, IBM Plex OFL-1.1, React MIT, lucide ISC); OFL redistribution requires the licence text (DOC-02) | +| Reviewer readability | JS minified (allowed); AMO warnings from React / reflect-metadata; the 40 MB font payload dominates any reviewer's download and diff | + +WXT `-sources.zip` (Firefox): 524 files including `backups/site-integrations-v0.2.0-2026-01-11/entrypoints/{1337x,rarbg,tpb,nyaa,fitgirl,audiobookbay,tgx}.content.tsx`, `backups/src-v0.1.28-*`, `build_log.txt`, `audit_extension*.txt`, `playwright-report/`. It excludes `node_modules` and `.persistent-data`. **It must not be uploaded** (REL-01). + +## 15. Performance and Resource Audit + +No content scripts run on web pages, so page-load and page-interaction impact is nil (CONFIRMED by manifest). Remaining risks are code-level predictions; nothing was measured: + +- **Popup open cost:** the popup loads ~2 MB of CSS (Carbon twice) plus a 352 KB shared React/Carbon chunk and 48 KB popup chunk; Plex fonts load on demand. Expect a visible flash and hundreds of ms parse time on modest hardware. PERF-01, P2. +- **Polling volume:** while the options page is open, the background polls every 2 s; while the popup is open, the popup separately requests the full torrent list every 2 s and checks vault status every 2 s; with both open the server sees two full-list requests every 2 s (ARCH-04). Alarm poll every 60 s forever while unlocked, even with the badge off. For a 5k-torrent client, each poll transfers the full list and re-serialises it into `storage.session` (1 s debounce). +- **Message traffic:** viewport diffs are computed; the Zustand store is sparse-indexed. Fine at 50-row viewports. +- **Memory:** `ViewportManager.fullTorrents` + `previousSlice` + `storage.session` copy; acceptable. No MutationObservers, no listeners on pages. +- **Startup:** SW runs `purgeLegacyFallbackKey`, hydration, `ServerResolver.resolve()` (PBKDF2 is not re-run; only AES decrypt), context-menu rebuild. Acceptable. +- **Battery:** the 60 s alarm wakes the SW and performs a network request continuously; on laptops this is the only persistent cost. Gate it on badge setting (PERF-02, P2). + +## 16. UX and Product Quality Audit + +Could a new user obtain value without developer knowledge? Partially. A user who already knows their client's URL and credentials can succeed in about five screens, but several points create abandonment or bad reviews: + +| ID | Sev | Finding | Evidence | +|---|---|---|---| +| UX-01 | P1 | Popup shows "Extension not configured / Setup Now" whenever the vault is locked (every browser restart), with no unlock control in the popup; the vault badge that would explain is only rendered in the configured branch | `features/torrent-control/ui/Dashboard.tsx:124,199-239`, `useSettings.ts:96-98` | +| UX-02 | P1 | Popup "Debug" tab ships in production; its only control dispatches an event nothing listens to | `Popup.tsx:81-103`, `wxt.config.ts:91` | +| UX-03 | P1 | Settings that persist but do nothing: theme (8 options; both roots hard-code Carbon `g100`), layout sidebar, notification level and style, "Open Web UI" context toggle, performance mode ("locked for testing" banner), enhanced diagnostics, `CommandPalette` (Ctrl+K opens an empty palette with "Project Prism v0.1.23" footer), "Storage Health" card permanently "Unknown", "Connection: Online" hard-coded | `ThemeSettings.tsx`, `popup/main.tsx:16`, `options/main.tsx:15`, `LayoutSettings.tsx`, `NotificationSettings.tsx:73-85`, `ContextMenuSettings.tsx:106-111`, `PerformanceSettings.tsx:19-32`, `SystemSettings.tsx:37-42`, `CommandPalette.tsx`, `TorrentDashboard.tsx:62-93` | +| UX-04 | P2 | Master password is demanded before any value is shown; no explanation of why; "cannot be recovered" warning without an escape hatch (no "reset vault" path except System settings) | `SetupVault.tsx` | +| UX-05 | P2 | Landing view after setup is the empty Dashboard; nothing routes the user to "Servers" | `options/Dashboard.tsx:39` | +| UX-06 | P2 | Popup mini-list caps at 3 rows while the count badge shows all; no link to full list; popup Test collapses every failure to "Failed" | `Dashboard.tsx:314-318,359-371` | +| UX-07 | P2 | `window.confirm` / `alert` for destructive and error flows; import triggers a full page reload | `ServerConfigPanel.tsx:128,141,153,469-476`, `DataManagement.tsx:17-37` | +| UX-08 | P2 | Server form strips URL paths (breaks Aria2 and reverse-proxy sub-paths); changing client type does not apply the client's default port/path | `ServerConfigPanel.tsx:230-275` | +| UX-09 | P2 | Context-menu preview in settings does not match the real menu (labels, missing "Scan Page", "Pause/Resume" vs "Add Paused") | `ContextMenuSettings.tsx` vs `ContextMenuService.ts:179-283` | +| UX-10 | P2 | Naming inconsistency: "CTRL", "Torrent Control", "CTRL - Torrent Control", "Project Prism" across manifest, popup title, HTML titles, palette | manifest, `index.html` titles, `CommandPalette.tsx:127` | +| UX-11 | P3 | Build stamp overlay visible on both pages, overlapping toast position; empty 56 px popup header | `VersionOverlay.tsx`, `MainLayout.tsx:11-13` | +| UX-12 | P2 | No `permissions.onRemoved` handling; revoked host access appears as generic connection failure | grep: none | + +Store-review UX risks: Debug tab and "locked for testing" copy read as unfinished software; Utilities links read as piracy-adjacent (PROD-01). + +## 17. Accessibility Audit + +No WCAG conformance is claimed. Status by surface (inspected, not tested with AT): + +| Surface | Status | Notes | +|---|---|---| +| Carbon components (Tabs, Modal, PasswordInput, Select, Toggle with labels) | likely compliant | Carbon provides names, focus, keyboard | +| `ServerConfigPanel` form | likely non-compliant (A11Y-01, P1) | `