Commit graph

4 commits

Author SHA1 Message Date
Local Dev
0d583fb749 fix(theseus/addons): sidebar auto-restore on tab switch + more forgiving tar
Two follow-ups from user reports on the 0.5.x screenshot rollout:

- When the screenshot editor sidebar is maximized (fills the window) and
  the user hits New Tab / Settings / any address-bar nav that opens a
  different tab, the incoming tab was left invisible behind the sidebar.
  setActive now auto-restores the sidebar to its pre-max width whenever
  it detects a tab switch — the user can hit the maximize button again
  on the way back. Pure additive change, no other setActive semantics
  touched.

- The signed-add-on update pipeline failed the 0.5.0 tarball extract on
  a Windows 10 install with the built-in bsdtar: `tar --force-local -x
  -z -f …` — bsdtar doesn't recognise --force-local and errors out
  before it opens the archive. Try the extraction WITHOUT the flag
  first (safe with the posix-slash paths we already pass on every tar
  we care about — bsdtar, GNU tar, MSYS2 tar) and fall back to WITH
  --force-local only if the first invocation exits non-zero (MSYS2
  path where a bare `C:/…` gets parsed as a `host:` prefix). Original
  error message is surfaced on total failure so we can still tell what
  went wrong.
2026-09-09 02:38:32 +02:00
Local Dev
8055d39a9d feat(theseus/addons): per-add-on diagnostic report from checkAndStageUpdates
The Settings > Extensions "Check for updates" button used to report
one of two lines: "N updates staged; restart to apply" or "All
extensions are up to date". The second collapsed several distinct
outcomes into one indistinguishable line, so a user seeing "up to
date" couldn't tell whether the check actually reached the endpoint
or the fetch had silently failed.

checkAndStageUpdates now returns { report, skipped? } with one entry
per installed add-on and a status of:
  no-update-url  — addon.json doesn't declare updateURL
  fetch-failed   — DNS / connection / HTTP error on updates.json or the tarball (detail carries the message)
  up-to-date     — endpoint reached, no version strictly newer than installed
  signature-invalid — offered version's sig didn't verify against any baked-in pubkey
  sha256-mismatch — downloaded tarball's hash didn't match the signed one
  extract-failed  — tar could not extract (detail carries the message)
  manifest-mismatch — extracted addon.json didn't match signed id/version
  staged / already-staged — success

The Settings UI now renders one row per add-on with that status, so
a "no update" outcome is never mistaken for a silent fetch failure.
Return shape is back-compat: if a caller expects a bare array, the
UI normalizes.
2026-09-08 18:14:07 +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