Commit graph

5 commits

Author SHA1 Message Date
Local Dev
65ef59f5c8 Theseus: add-on stores live in memory — no more 16 s "Not Responding" at launch
An add-on's storage.get read and parsed its whole store file on every call,
and storage.set read, parsed and rewrote it — synchronously, on the main
thread. Traced on a real profile (installed 0.3.70): with a 7.5 MB Aegis
store, 30 of the first 35 s of main-thread time went to storage.get, the
window sat in "Not Responding" from 3 s to 19 s, and the first page showed at
19 s. One get cost ~73 ms; Aegis does dozens per state update.

lib/addon-store.cjs keeps one in-memory copy per store, shared by the
add-on's api.storage (addons-host.js) and its pages (addon-storage-* IPC in
main.js). After a one-time load a get costs microseconds; values are copied
in and out (structuredClone), so callers keep the old semantics. Writes are
coalesced (100 ms) and land as temp-file + rename, and are flushed on quit;
a store that doesn't parse is moved aside instead of being replaced by {}.

Measured on copies of the same profile, dev build:
  first page 16.9-17.6 s -> 1.5-1.7 s; main thread blocked 24.7-25.8 s of
  30 -> 1.0-1.1 s; longest freeze 13.7-15.0 s -> 0.6 s.

The Aegis side (capping its unbounded txCache) ships separately through
Aegis's own update channel. The boot tracer gains total/longest block columns.
2026-10-03 16:08:22 +02:00
Local Dev
b531c89f82 Theseus boot tracer: measure a working launch, and say when it did not
With a script as the Electron entry, the app path is the script's folder;
main.js loads chrome.html, home.html and every overlay by relative name, so
all of them failed with ERR_FILE_NOT_FOUND and every traced run measured a
broken launch (it also left a window showing the error page). The tracer now
sets app.setAppPath to TheseusNavigator, records failed main-frame loads, and
run.mjs marks such a run INVALID.

Re-measured (warm runs, fresh profile): the per-overlay lazy loading in
e524c12 saves 5 processes and ~100 MB private memory at 15 s — not ~30 MB
with an unchanged process count as its message says; that figure came from
the broken runs. The BNS indexer utilityProcess costs ~60-75 MB.
2026-10-03 14:14:46 +02:00
Local Dev
a57d20751b Theseus: boot tracer under scripts/boot-trace
Measures dev-tree launches against a throwaway profile, without changing
main.js: time to app ready, toolbar and first page; main-thread blocks; add-on
activation; overlay pages loaded; main-process fetches; process count and
private memory at 15 s. One row per run, so a startup change can be compared
with the numbers before it.

  node scripts/boot-trace/run.mjs [--runs 3] [--seconds 25] [--fresh] [--profile <dir>]
2026-10-03 13:26:44 +02:00
Local Dev
95d199c2f2 fix(theseus/addons): windows-tar fixes for the addon updater, verified end-to-end
An end-to-end drive of the update flow against a local HTTP server hit
two Windows-only tar quirks that a first-cut MVP wouldn't catch:

1. Git-Bash tar (MSYS2), which comes first on PATH when Git-for-Windows
   is installed, treats drive-letter paths as `host:file` remote-archive
   syntax. Sidestepped with --force-local (also silently accepted by
   Win10's built-in bsdtar and by GNU tar).

2. Even with --force-local, MSYS2's argv-conversion layer mangles
   backslashes in Windows paths, so `C:\Users\...\tmp\dir` arrives at
   tar as `C:\Users...\dir` and it can't open the path. Passing
   forward-slash paths (`C:/Users/.../tmp/dir`) dodges the mangler;
   bsdtar and GNU tar accept them as-is.

3. sign-addon-update.mjs was tar'ing the addon directory as a subfolder
   (`screenshot/addon.json` inside the archive), so the client
   extracted to `<tmp>/screenshot/` and then failed the id+version
   re-check because addon.json wasn't at the root. Now the signer
   tars the CONTENTS of the addon dir via `tar -C <addon-dir> .`, so
   entries live at the archive root where the client expects them.

All three surfaced from `scratchpad/decoupling-test/run-test.mjs`, which
now walks the full path — sign, serve, fetch, verify, download,
extract, stage, promote, backup — plus three signature-tamper negatives
and the empty-pubkey short-circuit. 15/15 checks pass.
2026-09-07 22:28:55 +02:00
Local Dev
cffb956a4c feat(theseus/addons): signed add-on update endpoint, à la Firefox XPI
Decouples bundled-add-on updates from Theseus releases. An add-on
whose addon.json declares an updateURL can be republished at any time
without shipping a new Theseus installer; existing installs pick it up
on the next boot's +30 s background check.

Client flow (main-process only, no UI touchpoints in this commit):

    initAddons()
    ├── promoteStagedUpdates()   # promote signed stage if newer
    ├── seedBundledAddons()      # bundle wins over on-disk if newer
    └── AddonHost.discoverAndActivate()
    30 s later:
    └── checkAndStageUpdates()   # fetch, verify, download, stage

Signature: Ed25519 over
"silentmode.addon-update-v1|<id>|<version>|<tarball-sha256>",
verified against a hardcoded set of operator pubkeys living in
addon-update-pubkeys.js. Domain-separated so the operator key can't
be tricked into signing a message with a different purpose. Empty
pubkey array is the shipping default — checkAndStageUpdates() then
short-circuits and no outbound requests are made, which is the safe
posture until the operator ceremonies a key in.

Payload: gzipped tar, extracted with the system tar (present on
Win10 1803+, macOS, Linux). Path traversal defended by tar's default
refusal of `..` entries; the extracted manifest's id + version are
re-checked against the signed values before staging.

Staged updates go to <userData>/addons-updates-staged/<id>-<version>/.
Promotion into <userData>/addons/<id>/ reuses seedBundledAddons's
backup dance: existing folder moves to
<userData>/addons-backups/<id>-<oldver>-<timestamp>/ so any local
edits survive.

New files:
- addon-updater.js — client
- addon-update-pubkeys.js — hardcoded pubkeys (empty; edit + rebuild to rotate)
- scripts/generate-update-keypair.mjs — one-time keygen
- scripts/sign-addon-update.mjs — operator packager+signer
- docs/ADDON-UPDATES.md — operator brief + threat model

Wired into main.js at boot; screenshot add-on's addon.json advertises
the reference updateURL for when the endpoint goes live.
2026-09-07 21:58:30 +02:00