Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
853666a
docs(release): preserve v1 release-readiness audit evidence
StarlightDaemon Sep 9, 2026
50e54c4
fix(security): keep the vault session key out of disk-backed storage …
StarlightDaemon Sep 9, 2026
ea87b39
chore(deps): reconcile pending dev-dependency advisory bumps
StarlightDaemon Sep 9, 2026
d16b7df
feat(core): stable server identity, generation-checked polling, fail-…
StarlightDaemon Sep 9, 2026
d13123f
feat(transport): browser-correct HTTP transport, endpoint handling, a…
StarlightDaemon Sep 9, 2026
2621e86
build(packaging): store-ready manifests, local-only styling, determin…
StarlightDaemon Sep 9, 2026
7377bc2
docs(release): v1 scope definition and store-policy research notes
StarlightDaemon Sep 9, 2026
09e6677
docs(release): checkpoint packaging wave
StarlightDaemon Sep 9, 2026
9191c15
refactor(product): reduce CTRL to the v1 public surface
StarlightDaemon Sep 9, 2026
7c2f019
docs(release): checkpoint product-surface reduction
StarlightDaemon Sep 9, 2026
8959576
feat(ui): accessible server configuration, truthful connection recove…
StarlightDaemon Sep 9, 2026
0542209
docs(release): checkpoint server configuration and accessibility wave
StarlightDaemon Sep 9, 2026
ad73f7d
test(live): disposable torrent-client environment and real-browser ha…
StarlightDaemon Sep 9, 2026
b945317
docs(release): checkpoint disposable runtime environment
StarlightDaemon Sep 9, 2026
18b3069
fix(firefox): grant port-less host patterns so fetches are CORS-exemp…
StarlightDaemon Sep 9, 2026
a8f489a
fix(clients): live-verified repairs for qBittorrent, aria2 and truthf…
StarlightDaemon Sep 9, 2026
b2c54a8
docs(release): checkpoint live verification and repair loop
StarlightDaemon Sep 9, 2026
8d51232
test(live): runtime validation of state-integrity and vault invariant…
StarlightDaemon Sep 9, 2026
a3e4148
docs(release): checkpoint state and vault runtime validation
StarlightDaemon Sep 9, 2026
4998d1a
build(release): CI package gates, reviewer BUILD.md, source archive c…
StarlightDaemon Sep 9, 2026
d4617d1
build(release): make zip:source workspace check path-separator indepe…
StarlightDaemon Sep 9, 2026
a534ec6
docs: reconcile README, privacy policy, guides and changelog with the…
StarlightDaemon Sep 9, 2026
fa687e0
build(release): reproducible source archive bytes; store dossier draft
StarlightDaemon Sep 9, 2026
efef03d
docs(release): store screenshots captured from the actual UI, with th…
StarlightDaemon Sep 9, 2026
0e6742b
fix(popup): remove the empty header band and stop marking paused torr…
StarlightDaemon Sep 9, 2026
06c8c65
docs(release): checkpoint CI, documentation and store-dossier waves
StarlightDaemon Sep 9, 2026
3fa44f1
test(release): reconcile pre-push CI assertions
StarlightDaemon Sep 10, 2026
01f57ee
chore(raiden): reconcile v1 release state before publication
StarlightDaemon Sep 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Files copied verbatim into the extension package must not depend on the checkout platform:
# reviewers rebuild from the git archive (LF), so the working tree must match it byte for byte.
extension/src/public/**/*.json text eol=lf
*.png binary
34 changes: 0 additions & 34 deletions .github/workflows/auto-localize.yml

This file was deleted.

209 changes: 119 additions & 90 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,147 +2,176 @@ name: CI

on:
push:
branches: [main, master]
branches: [main]
pull_request:
branches: [main, master]
branches: [main]

# Every job runs in the extension workspace from the tracked lockfile.
# Release-gate jobs (package) check what the store submissions depend on:
# a clean, deterministic, remote-free package with the expected version and
# recorded checksums. Store submission itself is never automated.

env:
NODE_VERSION: '24'

jobs:
# Job 1: Lint
lint:
runs-on: ubuntu-latest

defaults:
run:
working-directory: extension

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: extension/package-lock.json
- run: npm ci
- run: npm run lint

- name: Install dependencies
run: npm ci

- name: Run ESLint
run: npm run lint

# Job 2: Type Check, Unit Tests
test:
runs-on: ubuntu-latest
needs: lint

defaults:
run:
working-directory: extension

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: extension/package-lock.json

- name: Install dependencies
run: npm ci

- run: npm ci
- name: Type check
run: npm run compile
- name: Unit and component tests
run: npm test

- name: Run unit tests
run: npm run test

# Job 2: Build Extensions
build:
package:
runs-on: ubuntu-latest
needs: test

defaults:
run:
working-directory: extension

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: extension/package-lock.json

- name: Install dependencies
run: npm ci

- name: Build Chrome extension
run: npm run build:chrome

- name: Build Firefox extension
run: npm run build:firefox

- name: Upload Chrome build
uses: actions/upload-artifact@v4
with:
name: chrome-extension
path: extension/builds/chrome-mv3/
retention-days: 7

- name: Upload Firefox build
uses: actions/upload-artifact@v4
- run: npm ci

- name: Build Chrome and Firefox packages
run: |
npm run zip:chrome
npm run zip:firefox

- name: Build twice and require byte-identical output
run: |
set -e
(cd builds/chrome-mv3 && find . -type f | sort | xargs sha256sum) > /tmp/chrome-1.txt
(cd builds/firefox-mv3 && find . -type f | sort | xargs sha256sum) > /tmp/firefox-1.txt
npm run build:chrome
npm run build:firefox
(cd builds/chrome-mv3 && find . -type f | sort | xargs sha256sum) > /tmp/chrome-2.txt
(cd builds/firefox-mv3 && find . -type f | sort | xargs sha256sum) > /tmp/firefox-2.txt
diff /tmp/chrome-1.txt /tmp/chrome-2.txt
diff /tmp/firefox-1.txt /tmp/firefox-2.txt
echo "deterministic: $(wc -l < /tmp/firefox-1.txt) files"

- name: No tracked source mutated by the build
run: |
cd "$(git rev-parse --show-toplevel)"
git status --porcelain
test -z "$(git status --porcelain)"

- name: Mozilla validator (addons-linter, errors fail the build)
run: npx --yes addons-linter builds/firefox-mv3

- name: Package content gates
run: |
set -e
for dir in builds/chrome-mv3 builds/firefox-mv3; do
echo "== $dir"
# Only the English locale is shipped.
test "$(ls $dir/_locales | tr '\n' ' ' | xargs)" = "en"
# No bundled fonts and no remote font or CDN references.
test "$(find $dir -iname '*.woff*' -o -iname '*.ttf' -o -iname '*.otf' | wc -l)" = "0"
! grep -rEl 's81c\.com|fonts\.googleapis|fonts\.gstatic|@font-face' "$dir"
# Strings from removed features must not reappear.
! grep -rEl 'iknowwhatyoudownload|btcache|torrage|itorrents|openwebtorrent' "$dir"
# Only the expected set of files.
test "$(find $dir -type f | wc -l)" = "14"
done

- name: Version consistency
run: |
set -e
PKG=$(node -p "require('./package.json').version")
MAN_CHROME=$(node -p "require('./builds/chrome-mv3/manifest.json').version")
MAN_FIREFOX=$(node -p "require('./builds/firefox-mv3/manifest.json').version")
# Same rule as toManifestVersion() in wxt.config.ts: a pre-release tag becomes a fourth integer.
EXPECTED=$(node -p "const [b,p]=process.argv[1].split('-'); if(!p) b; else { const s=p.split('.').pop(); /^\d+$/.test(s)? b+'.'+s : b+'.0' }" "$PKG")
echo "package $PKG → manifest $EXPECTED; chrome $MAN_CHROME; firefox $MAN_FIREFOX"
test "$MAN_CHROME" = "$EXPECTED"
test "$MAN_FIREFOX" = "$EXPECTED"
test "$(node -p "require('./builds/chrome-mv3/manifest.json').version_name")" = "$PKG"
ls builds/ctrl-extension-$PKG-chrome.zip builds/ctrl-extension-$EXPECTED-firefox.zip
# Permissions must stay the reviewed set.
for m in builds/chrome-mv3/manifest.json builds/firefox-mv3/manifest.json; do
test "$(node -p "JSON.stringify(require('./$m').permissions)")" = '["storage","contextMenus","notifications","alarms","declarativeNetRequestWithHostAccess"]'
test "$(node -p "JSON.stringify(require('./$m').optional_host_permissions)")" = '["http://*/*","https://*/*"]'
done

- name: Package size (internal regression threshold)
run: |
set -e
for z in builds/*.zip; do
SIZE=$(stat -c %s "$z"); echo "$z: $SIZE bytes"
# Well above the current ~327 KB; a jump past this means something unintended was bundled.
test "$SIZE" -lt 600000
done

- name: Checksums
run: |
(cd builds && sha256sum *.zip | tee SHA256SUMS.txt)

- uses: actions/upload-artifact@v4
with:
name: firefox-extension
path: extension/builds/firefox-mv3/
retention-days: 7
name: packages
path: |
extension/builds/*.zip
extension/builds/SHA256SUMS.txt
extension/builds/chrome-mv3/
extension/builds/firefox-mv3/
retention-days: 14

# Job 3: E2E Tests (Chrome only)
e2e:
runs-on: ubuntu-latest
needs: build

needs: test
defaults:
run:
working-directory: extension

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
cache-dependency-path: extension/package-lock.json

- name: Install dependencies
run: npm ci

- name: Install Playwright browsers
run: npx playwright install chromium --with-deps

- name: Build Chrome extension
run: npm run build:chrome

- name: Run E2E tests
- run: npm ci
- run: npx playwright install chromium --with-deps
- run: npm run build:chrome
- name: E2E smoke (Playwright Chromium)
run: npm run test:e2e -- --grep-invert "@integration"
env:
CI: true

- name: Upload Playwright report
uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: extension/playwright-report/
retention-days: 7

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,6 @@ extension/tests/e2e/.persistent-data/
*.pma
*.db
*.db-journal

# Live-verification screenshots (regenerated by extension/tests/live/verify.mjs)
docs/release/v1/evidence/screens/
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
24
8 changes: 5 additions & 3 deletions .raiden/state/CURRENT_STATE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Current State

**Active branch:** `main` @ `247f7b0` (PR #4 merged `next/main-rebuild`; in sync with `origin/main`, 0 ahead / 0 behind)
**CI:** passing on `main` (GitHub CI: lint, test, build, e2e)
**Active branch:** `main` (PR #4 merged `next/main-rebuild`). As of 2026-09-10 the v1 release lineage (see `docs/release/v1/EXECUTION_STATE.md`) is committed locally on top of `origin/main` and has **not been published**; the publication candidate is the local branch `release/v1-publication-candidate` (the release lineage plus the state reconciliation that records this entry). Publication and sync status are deliberately not recorded here — verify them from live git evidence (`git fetch origin`, then `git rev-list --left-right --count origin/main...<branch>` and `git ls-remote origin`) before acting. This file is not authoritative for ahead/behind counts.
**CI:** last verified passing on the published `origin/main` (GitHub CI run `29472393892` for `f088c5f`; see OPEN_LOOPS.md OL-010). Remote CI has **not** run against the unpublished v1 release lineage; release Gate H stays PARTIAL until the first remote run of `lint`, `test`, `package` and `e2e` completes green.

---

Expand Down Expand Up @@ -32,7 +32,8 @@ CTRL is a browser extension for managing BitTorrent clients. Built with WXT, Rea
- External repository audit performed 2026-07-02 (report at `.audits/CTRL_AUDIT_2026-07-02.md`, untracked); remediation pass conducted: dependency vulnerability fixes (3196b3c), extension attack-surface hardening (0e90490), CI Node version alignment and third-party action pinning (4adf272), governance/license-year sync (e909644).
- LifecycleAdapter `parseDOM` serialization fixed and validated (2026-07-03, commit aaa99b3).
- Synology Download Station support removed — breaking change (2026-07-03, commit f2e4a62); research documents archived (247f7b0).
- `main` HEAD = `247f7b0`, in sync with `origin/main`. Full unit suite re-verified 2026-07-03: **512 passed / 0 failed** (15 test files).
- As of 2026-07-03, `main` HEAD was `247f7b0` and in sync with `origin/main` (historical; for current sync status see the header). Full unit suite re-verified 2026-07-03: **512 passed / 0 failed** (15 test files).
- v1 release program (2026-09-09): release lineage prepared locally — not published, not released, no store submission (version remains `0.2.0-beta.1`). Program state, release gates and evidence: `docs/release/v1/EXECUTION_STATE.md`, `docs/release/v1/CLIENT_VERIFICATION.md`. Gate H (remote CI) is PARTIAL and Gate K (operator acceptance) is not yet evaluated. OL-012, OL-013, OL-014 and OL-015 are closed against that lineage's evidence; OL-011 remains open (see OPEN_LOOPS.md).

## In Progress
- 2026-06-13 — hook exec-bit fixed, .gitignore e2e noise cleared.
Expand All @@ -42,6 +43,7 @@ CTRL is a browser extension for managing BitTorrent clients. Built with WXT, Rea
## Non-Blocking Open Items

- Secret scanning alert #1: RESOLVED (2026-06-16) — see Confirmed Current State. Flagged value was the public Chromium omnibox key in committed e2e cache, not a real credential; GitHub alert already resolved-as-revoked; stale Dependabot branch carrying the only reachable copy was deleted. Sole residual is GitHub's immutable `refs/pull/3/head` (PR #3), which cannot be removed client-side — acceptable: the key is public and the alert is already closed. No further action available or needed.
- Superseded in part (2026-07-26): the "acceptable … no further action" judgement above predates DECISIONS.md D-005, which records that `refs/pull/3/head` is publicly reachable because the repository is PUBLIC. Provider-side revocation remains unverified and is tracked as OPEN_LOOPS.md OL-011 (Open, external gate).

## Not Yet Done

Expand Down
16 changes: 16 additions & 0 deletions .raiden/state/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,19 @@
## D-004

| D-004 | 2026-06-14 | Maintenance pass: hook exec-bit fixed, .persistent-data/ untracked and ignored, .raiden/ exec-bit drift normalized, npm audit fix applied (vite 7.3.3→7.3.5, esbuild 0.27.2→0.27.7, shell-quote 1.8.3→1.8.4), stale VPN doc references removed. |

## D-005

- Date: 2026-07-26
- Status: Active
- Decision: record as established fact that the exposed Google/Chromium API key is reachable from the **public** repository, not only from the local clone. The 2026-07-26 audit's first pass characterized the exposure as local-only, resting on the claim that the `pre-dependabot-delete-backup` tag was never pushed to `origin`. That claim is technically true — `git ls-remote --tags origin` returns no tags — but materially incomplete: `git ls-remote origin` resolves `refs/pull/3/head` to `15ffee9534d732e44727ad370458217340798122`, the identical commit object as the local tag, and `gh repo view` reports the repository visibility is PUBLIC. The audit's adversarial second pass established this and corrected the finding in place.
- Rationale: GitHub retains `refs/pull/<n>/head` permanently and immutably, independent of whether the source branch was deleted. Because the remote ref names the same commit object as the local tag, it carries the same tree and the same 16 cache blobs under `extension/tests/e2e/.persistent-data/Default/Cache/Cache_Data/`. Two consequences follow and are the reason this is recorded rather than left in the audit report alone: deleting the local tag remediates nothing, and no client-side git operation can remove the remote copy. The blast radius on record is therefore internet-exposed, not single-machine.
- Note: this entry records a fact established by audit, not a remediation decision. No decision has been taken on verifying provider-side rotation or on pursuing a GitHub Support purge of the PR-3 ref; both are tracked as open items in OPEN_LOOPS.md OL-011. Mitigating context recorded but not adjudicated: the flagged value is documented as the public Chromium omnibox suggest key, and D-003 declares it revoked on 2026-06-14 — a declared date, which no repository-bound audit can verify. Related: the tracked and publicly readable `.raiden/state/CURRENT_STATE.md` records the key fragment, its exact in-tree path, the commit, and the residual ref.

## D-006

- Date: 2026-07-26
- Status: Active
- Decision: record as a correction to the record that the technical justification documented for the `@vitejs/plugin-react` hold (OPEN_LOOPS.md OL-006, written 2026-07-04) was factually inaccurate as of this date. The 2026-07-26 audit checked its premises against installed and live upstream state; three failed. It recorded the installed vite as "4.x" when the installed version is `7.3.5`. It named a conflicting `@wxt-dev/module-react@1.1.5` peer range of `^4.4.1 || ^5.0.0` when that package declares `{wxt: '>=0.19.16'}` and no vite peer at all, so the named conflict did not exist. And it stated the WXT ecosystem had not moved to support vite 8 when `@wxt-dev/module-react@1.2.2` declares `vite: ^5.4.19 || ^6.3.4 || ^7.0.0 || ^8.0.0-0` and the installed `wxt@0.20.27`'s own vite dependency range already includes `^8.0.0-0`.
- Rationale: the hold's conclusion remains correct — `@vitejs/plugin-react@6.0.4` requires `vite@^8.0.0` plus new `@rolldown/plugin-babel` and `babel-plugin-react-compiler` peers, while the project runs vite `7.3.5` — but the recorded reason pointed at an upstream gap that has since been satisfied. A planner reading OL-006 as originally written would wait for something that has already happened, and would be working from a vite version three majors out of date. The corrected justification, and the gate change from `upstream` to `local`, are recorded in OL-006 itself.
- Note: this entry records a correction to the record, not a decision to act. OL-006 remains Open and no decision has been taken on performing the vite 7 → 8 migration. By contrast the Babel 8 hold (OL-005) was re-verified in the same pass as fully accurate and required no correction.
Loading
Loading