theseus/GOTCHAS.md
Local Dev 434f6d35d0 Theseus: hand-off for moving Aegis to on-demand activation
Aegis is most of the remaining launch cost: ~165 ms of synchronous
activation, then ~610 ms of main-thread work loading its crypto and
WizardConnect deps. It was left on startup because another session
owns bundled-addons/aegis. NOTE-aegis-on-demand.md says exactly what it
needs, measured against 0.27.1:

- addon.json: "activation": "on-demand", plus panels [{id:"main",
  title:"Wallet", page:"panel.html"}].
- activate() must return a promise that resolves once deps are loaded
  and, only if the vault is already unlocked, wallets are mounted.
  vault.derive never rejects on a locked vault, so awaiting
  mountAllWallets there would stall every first call for the full 5 s
  host wait.
- Persisted WizardConnect pairings (wc/<walletId>/uris) need a relay
  listener: call api.startAtLaunch(true) while any exist, false when
  none are left.
- Bridge calls, panel open, wiz:// links and Settings calls already wake
  it. The dapp globals are injected from the manifest before it runs.

GOTCHAS: an enabled add-on may not be running, so code in main reaches
add-ons through dispatch() rather than checking isActive()/hasHandler().
2026-10-03 16:05:41 +02:00

158 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Theseus gotchas
Non-obvious things sessions keep re-deriving. If you found this file because
something broke in a way that felt spooky, the answer is probably here.
## `build.files` in [package.json](package.json) is EXPLICIT
electron-builder ships **only what's listed** in `build.files`. Every runtime-
loaded HTML/preload/module MUST be there. A missing entry causes the
`loadFile("foo.html")` in a WebContentsView to silently open blank in the
packaged app — visually indistinguishable from "the button does nothing".
`npm start` (dev) never catches this because it reads from the source tree
directly. Only a real `npm run dist` + install exposes it.
Every time you add a new `WebContentsView` or `loadFile`, add its HTML and
preload to the `files` array. Sanity check after building:
```
node -e "const b = require('fs').readFileSync('dist-public/win-unpacked/resources/app.asar'); for (const n of ['engine-picker.html','popover.html','downloads.html','engine-picker-preload.js','popover-preload.js','downloads-preload.js']) console.log(n, b.indexOf(Buffer.from(n)) >= 0 ? 'OK' : 'MISSING');"
```
This has bit us once (search-picker dropdown, shipped `6bbccf9c`, blank
picker). Do not let it bite twice.
## Building — admin terminal the first time
`npm run dist` needs an **admin** terminal on the machine's first Theseus
build so `electron-builder`'s `winCodeSign` package can extract its
`darwin/` symlinks (requires `SeCreateSymbolicLink`). Subsequent builds
reuse the cache under `$LOCALAPPDATA/electron-builder/Cache/winCodeSign/`
and don't need admin.
If DNS is misbehaving on the machine, pin these hosts in
`C:\Windows\System32\drivers\etc\hosts` before building so electron-builder
can fetch: `github.com`, `codeload.github.com`,
`objects.githubusercontent.com`, `release-assets.githubusercontent.com`.
Strip them after.
## Reproducible builds
Set both before `npm run dist`:
- `SOURCE_DATE_EPOCH` — freezes timestamps so a rebuild with no content
changes produces the same hash. Bump per release, not per rebuild.
- `CSC_IDENTITY_AUTO_DISCOVERY=false` — stops electron-builder searching
for a signing cert (we don't sign; SECURITY.md rule 2).
## Native `<select>` popup theme
Chromium's native `<select>` popup uses the page's `color-scheme`. When it's
`"light dark"` (both accepted), Chromium picks by the OS scheme — so a
dark-themed app on a light OS shows a light popup and vice versa.
Fix, currently in [settings.html](settings.html):
```css
:root { color-scheme: dark; }
@media (prefers-color-scheme: light) { :root { color-scheme: light; } }
```
`nativeTheme.themeSource` (driven by `settings.theme`) sets
`prefers-color-scheme`, so this stays in sync automatically. Do not remove.
Chromium also respects `select option { background; color }` in the popup
on Windows — a belt-and-braces override alongside `color-scheme`.
## A preload belongs to the WebContents, not the page
A view's preload runs for every document that view ever loads. Navigating
a Settings tab to a website used to hand that site `window.cfg`, which
covers the vault (`pwGet`), settings and extension installs. Two rules
follow:
- Settings and add-on-file tabs never load anything else. `navigateTab`
and `will-navigate` open the target in a fresh tab instead.
- A new IPC channel exposed through `settings-preload.js` **must** be added
to `SETTINGS_ONLY` (or `SETTINGS_SHARED` if the toolbar's `preload.js`
calls it too) in [main.js](main.js). The wrapper around `ipcMain.handle`
only checks the sender for the channels in those sets. Any other channel
is callable by whatever page ends up in the view.
The same applies to `home-preload.js`, which is in *every* tab. Handlers
behind it check `isHomePageSender` / `isErrorPageSender`, which compare
the sender against the exact shipped file:// URL.
## An enabled add-on is not necessarily running
An add-on whose `addon.json` says `"activation": "on-demand"` is listed at
launch but not started. Its declared `panels`, `toolbar-menu`,
`context-menu-items` and `page-inject` bridge are live anyway, and
`AddonHost.dispatch()` starts it on the first call (see `ensureActive` in
[addons-host.js](addons-host.js)). So in main:
- Reach an add-on through `addonHost.dispatch(...)`, never by checking
`isActive()` / `hasHandler()` first. Both are false for a waiting
add-on, and the call would never wake it (`routeWizUri` shows the
pattern for when you really need to check).
- "Enabled" in the snapshot means running or waiting; `running` tells
them apart.
- Settings › Performance › "Start extensions when first used" off makes
every add-on start at launch again, which is the quickest way to rule
this out when an add-on "does nothing".
## The resolver in Theseus is `resolver-web.mjs`, not `.js`
Packaged builds ship `Argus/src/lib/resolver-web.js` as `resolver-web.mjs`
(see `extraResources` in [package.json](package.json)). Renamed because
`resources/` has no adjacent `package.json` so a `.js` gets treated as
CommonJS and its `export`s fail. Dev reads the engine copy directly (Argus
is `type: module` so `.js` works there).
## The resolver's electrum WS `directIP` fallback
[main.js](main.js) passes `directIP: true` to `resolveHost` so the engine
can dial the chipnet electrum servers by pinned IP if system DNS is dead.
Do not remove — this is what keeps `.bch` resolution working when the
adapter DNS is broken (a real failure mode we've hit).
## Publishing new hashes end-to-end
1. Build (see above).
2. Update `../site/releases-manifest.json` AND
`../site/tools/index.html` AND `../site/releases/index.html` with the
fresh SHA-256s + release date. All three must match.
3. Deploy:
```
scp dist-public/*.exe ../site/releases-manifest.json silentmode:/opt/silent-mode/dl/
cd ../Argus && node src/lib/sia-upload.js ../site bns/silentmode
```
4. On-chain (wallet spend, user's action):
`releases.silentmode.bch` currently publishes
`{"u":"https://dl.silentmode.st/releases-manifest.json"}` — a compact
`u` record because on-chain hashes wouldn't fit the 200-byte
OP_RETURN. Only re-publish if the URL changes or if we ever put full
hashes on-chain.
## Sia upload from this machine fails without a `hosts` pin
`node src/lib/sia-upload.js` fails with `getaddrinfo EAI_FAIL
coinspectrum.duckdns.org` unless the host is pinned in
`C:\Windows\System32\drivers\etc\hosts`:
`195.184.247.106 coinspectrum.duckdns.org`. This is a DNS quirk of the
machine, not a code bug — related to the strict-resolver behavior the
Ariadne daemon's EDNS fix addresses.
## Deploy directory quirks
- `/opt/silent-mode/dl/` is served as `dl.silentmode.st` — installers +
manifest live here.
- `silentmode.st` proxies to Sia (`bns/silentmode/`) — the HTML pages
live there.
- The `bns` gateway does NOT auto-index directories. `silentmode.st/apps/`
is a 404 (`NoSuchKey`); users must hit `/apps/index.html`. Fix upstream
in `bnsd.js` if it ever matters.
## Firefox needs a restart after Ariadne CA install/rotation
Firefox only reads root CAs at startup. The Ariadne installer offers a
`-CloseFirefox` task by default when Firefox is running so this doesn't
bite. Chrome/Edge pick up the new root immediately.