2026-07-29 13:54:34 +02:00
|
|
|
|
const { contextBridge, ipcRenderer } = require("electron");
|
|
|
|
|
|
contextBridge.exposeInMainWorld("cfg", {
|
|
|
|
|
|
get: () => ipcRenderer.invoke("settings-get"),
|
|
|
|
|
|
set: (key, value) => ipcRenderer.invoke("settings-set", key, value),
|
2026-07-30 19:29:32 +02:00
|
|
|
|
engines: () => ipcRenderer.invoke("search-engines"),
|
2026-07-30 20:58:45 +02:00
|
|
|
|
addEngine: (eng) => ipcRenderer.invoke("add-engine", eng),
|
|
|
|
|
|
removeEngine: (id) => ipcRenderer.invoke("remove-engine", id),
|
2026-07-30 22:55:52 +02:00
|
|
|
|
setEngineEnabled: (id, on) => ipcRenderer.invoke("set-engine-enabled", id, on),
|
2026-07-30 23:26:00 +02:00
|
|
|
|
setEngineOrder: (ids) => ipcRenderer.invoke("set-engine-order", ids),
|
2026-08-06 01:41:31 +02:00
|
|
|
|
removeFromList: (id) => ipcRenderer.invoke("remove-from-list", id),
|
Snapshot in-progress work: Ariadne mobile, Theseus password manager, Hephaestus
Several concurrent workstreams committed together as a checkpoint:
- Ariadne mobile resolver — BchFetcher/Bns/MainActivity resolution logic,
AndroidManifest + build.ps1
- Theseus password manager — settings.html/chrome.html/settings-preload.js UI +
main.js wiring + package.json resource; Argus password-vault.js, record-picker.js
(+ tests) and resolver-web.d.ts
- Hephaestus — new BCH-wallet OIDC auth-proxy + Forgejo docker-compose and
restic/S3 scripts (secrets referenced via env only; Hephaestus/.env is gitignored)
- Argus public-gateway.mjs updates
- Docs — root README, Email README/RUNBOOK, VPS access runbooks (Checkers/Deviant),
site/hermes, WebsiteDev registry + faster-blocks, Failures/ AAAA-mangle writeup,
Decentralized Storage map, coordination notes
- .gitignore — exclude /.keys/ and Hephaestus/.env
2026-08-14 23:17:18 +02:00
|
|
|
|
// Storage: wipe browsing data on demand. Pass any subset of
|
|
|
|
|
|
// { cookies, cache, storage, history }.
|
|
|
|
|
|
clearBrowsingData: (opts) => ipcRenderer.invoke("clear-browsing-data", opts),
|
|
|
|
|
|
// Password vault. All calls return { ok, ... } | { ok: false, err }.
|
|
|
|
|
|
// Renderers never see the seed / vault key / master password past setup/
|
|
|
|
|
|
// unlock; get() returns plaintext only in explicit response to a user click.
|
|
|
|
|
|
pwStatus: () => ipcRenderer.invoke("password-status"),
|
|
|
|
|
|
pwSetup: (masterPassword, seedSource) => ipcRenderer.invoke("password-setup", { masterPassword, seedSource }),
|
|
|
|
|
|
pwUnlock: (masterPassword) => ipcRenderer.invoke("password-unlock", masterPassword),
|
|
|
|
|
|
pwLock: () => ipcRenderer.invoke("password-lock"),
|
|
|
|
|
|
pwList: () => ipcRenderer.invoke("password-list"),
|
|
|
|
|
|
pwGet: (id) => ipcRenderer.invoke("password-get", id),
|
|
|
|
|
|
pwAdd: (entry) => ipcRenderer.invoke("password-add", entry),
|
|
|
|
|
|
pwUpdate: (id, patch) => ipcRenderer.invoke("password-update", id, patch),
|
|
|
|
|
|
pwRemove: (id) => ipcRenderer.invoke("password-remove", id),
|
|
|
|
|
|
pwGenerate: (spec) => ipcRenderer.invoke("password-generate", spec),
|
2026-08-06 01:41:31 +02:00
|
|
|
|
// Main asks settings to jump to a specific sidebar section (e.g. from the
|
|
|
|
|
|
// engine picker's "Search settings…" click). Emits the section id string.
|
|
|
|
|
|
onFocusSection: (cb) => ipcRenderer.on("focus-section", (_e, section) => cb(section)),
|
2026-08-02 11:39:00 +02:00
|
|
|
|
// Collision-mode: BCNR/ICANN policy + per-name/per-TLD overrides
|
|
|
|
|
|
collisionState: () => ipcRenderer.invoke("collision-state"),
|
|
|
|
|
|
setCollisionPolicy: (p) => ipcRenderer.invoke("collision-set-policy", p),
|
|
|
|
|
|
resetCollisions: () => ipcRenderer.invoke("collision-reset"),
|
2026-09-07 23:03:57 +02:00
|
|
|
|
ariadneState: () => ipcRenderer.invoke("ariadne-state"),
|
|
|
|
|
|
ariadneToggle: (on) => ipcRenderer.invoke("ariadne-toggle", !!on),
|
|
|
|
|
|
ariadneInstall: () => ipcRenderer.invoke("ariadne-install"),
|
|
|
|
|
|
ariadneUpdate: () => ipcRenderer.invoke("ariadne-update"),
|
|
|
|
|
|
ariadneUninstall: () => ipcRenderer.invoke("ariadne-uninstall"),
|
2026-09-08 00:43:26 +02:00
|
|
|
|
// Manual "Check for updates" — un-dismisses any existing chip and re-
|
|
|
|
|
|
// fetches the release manifest. Returns { updateAvailable, currentVersion }.
|
|
|
|
|
|
recheckUpdate: () => ipcRenderer.invoke("recheck-update"),
|
2026-09-08 12:36:21 +02:00
|
|
|
|
appVersion: () => ipcRenderer.invoke("app-version"),
|
2026-09-08 18:09:55 +02:00
|
|
|
|
restartApp: () => ipcRenderer.invoke("app-restart"),
|
Theseus: add-on framework MVP + Notepad reference add-on
New subsystem for extending Theseus with folders on disk. Each add-on
lives at <userData>/addons/<id>/ with an addon.json manifest and a
CommonJS entry that exports activate(api). Nothing about a private
add-on ships in the public installer - drop the folder, restart, it's
live. Bundled reference add-ons ride in the packaged app under
resources/bundled-addons/ and are seeded into <userData>/addons/ on
first boot; the framework treats seeded and drop-in add-ons the same.
Files:
- addons-host.js Loader + api.registerSidebarPanel() + per-
addon storage on <userData>/addons-data/.
Kept at the CommonJS-scoped top level (lib/
is ESM-scoped via its own package.json).
- sidebar-preload.js Runs in every sidebar panel. Exposes
window.silentmode.storage.{get,set,all} +
onVisibility. Main-side handlers derive the
add-on id from the sender file:// URL, so a
panel can only touch its own store.
- bundled-addons/notepad/ Reference add-on: addon.json, index.js,
note.html. Autosaving textarea with char /
word count.
main.js:
- Extension point: sidebar-panel. One right-anchored WebContentsView
(SIDEBAR_W=340) hosts the current panel; layout() shrinks the tab
views by the sidebar width when visible. First registered panel
wins for MVP; picker for multiple panels lands later.
- initAddons() at app.whenReady(): seedBundledAddons, then
AddonHost.discoverAndActivate.
- IPC surface: sidebar-toggle / sidebar-open / sidebar-close /
sidebar-state, addons-list / addons-set-enabled / addons-reveal /
addons-open-dir / addons-reload, and origin-gated
addon-storage-get/set/all.
- Settings gains `disabledAddons: []` — off-toggled ids persist and
the loader honours them without a restart (discoverAndActivate
runs again on toggle).
chrome.html: toolbar sidebar-toggle button, hidden until at least one
add-on has registered a sidebar panel.
settings.html: new "Add-ons" section under privacy. Lists installed
add-ons with icon / name / version / description / capabilities;
per-add-on enable/disable toggle + Show folder button; page-level
Reload and Open add-ons folder buttons; warning note about the trust
model.
package.json: build.files gains sidebar-preload.js + addons-host.js.
extraResources gains bundled-addons/ so the packaged app carries the
reference notepad for the first-boot seed.
Verified: `npm start` boots, addons-host discovers the notepad,
activates it, registers one sidebar panel. Log confirms
"1 installed, 1 enabled, 1 sidebar panels". Actual sidebar rendering
+ notepad UI need clicked-through validation on a real install.
Not shipped yet - deploy still blocked on the fail2ban VPS SSH ban.
Ships as 0.2.0 once SSH clears (this is a new subsystem, not a fix).
2026-08-31 13:51:08 +02:00
|
|
|
|
// Add-ons management (Settings > Add-ons tab).
|
|
|
|
|
|
listAddons: () => ipcRenderer.invoke("addons-list"),
|
|
|
|
|
|
setAddonEnabled: (id, enabled) => ipcRenderer.invoke("addons-set-enabled", id, !!enabled),
|
|
|
|
|
|
revealAddon: (folder) => ipcRenderer.invoke("addons-reveal", folder),
|
feat(theseus/settings): Extensions as compact rows with a detail view
The Extensions list was a stack of tall cards — description, author,
capabilities and buttons on every one — so seven add-ons filled the
page before the user found the toggle. Rows are now one line each,
Firefox-style: icon, name, built-in badge, version, a short update
status, the on/off switch and a ⋯ menu, grouped Enabled / Disabled /
Failed to load. Clicking a row opens the detail view in place: back
arrow, description, update status, author, version, type, folder with
Show folder, and a Permissions block that explains each declared
capability in plain words. The ⋯ menu (and right-click) offers Turn
on/off, Details, Show folder and, for non-bundled extensions, Remove —
a new addons-remove IPC that deletes the folder under the extensions
directory, refuses bundled add-ons (they would only be reseeded), and
clears the dock prefs it left behind.
2026-09-22 20:43:50 +02:00
|
|
|
|
// Delete a non-bundled extension's folder (Settings › Extensions › ⋯ › Remove).
|
|
|
|
|
|
removeAddon: (id) => ipcRenderer.invoke("addons-remove", id),
|
fix(theseus): extension updates re-check, announce and install in place
Updates published after launch never showed: the add-on channel was
checked once, 30 s after boot, and staged copies only applied on the
next launch with nothing telling the user. On 2026-09-22 Aegis 0.8.3
and VPN 0.1.3 landed minutes after the app's only check and stayed
invisible through manual scans made earlier and a restart made before
they were published.
Now: the check repeats every 4 hours; every check (boot, timer, manual,
add-on-driven) reports what is staged to the chrome, which shows a chip
for staged extensions; clicking it, or the "Update to vX" button that
appears on the extension's row and detail in Settings, promotes the
staged folder over the installed one and rebuilds the add-on host, so
the new version runs without a restart. Plug-ins (Aegis) are excluded
from the chip and the hot swap — a wallet updates from its own panel
and applies on the next launch.
Also from the same review: the new-tab button follows the last tab and
parks after the scroll arrow only when the strip overflows; Tor sits
left of the Aegis chip; plug-ins no longer appear in Settings ›
Extensions (they have Plug-ins); the extension detail view has a
labelled Back button, a close button and a Check now button; the
promotion helper returns what it promoted and accepts a filter.
2026-09-22 21:25:33 +02:00
|
|
|
|
// Install a staged signed update now (Settings › Extensions › "Update to vX"); no id = every staged extension.
|
|
|
|
|
|
applyStagedAddons: (id) => ipcRenderer.invoke("addons-apply-staged", typeof id === "string" ? id : undefined),
|
Theseus: add-on framework MVP + Notepad reference add-on
New subsystem for extending Theseus with folders on disk. Each add-on
lives at <userData>/addons/<id>/ with an addon.json manifest and a
CommonJS entry that exports activate(api). Nothing about a private
add-on ships in the public installer - drop the folder, restart, it's
live. Bundled reference add-ons ride in the packaged app under
resources/bundled-addons/ and are seeded into <userData>/addons/ on
first boot; the framework treats seeded and drop-in add-ons the same.
Files:
- addons-host.js Loader + api.registerSidebarPanel() + per-
addon storage on <userData>/addons-data/.
Kept at the CommonJS-scoped top level (lib/
is ESM-scoped via its own package.json).
- sidebar-preload.js Runs in every sidebar panel. Exposes
window.silentmode.storage.{get,set,all} +
onVisibility. Main-side handlers derive the
add-on id from the sender file:// URL, so a
panel can only touch its own store.
- bundled-addons/notepad/ Reference add-on: addon.json, index.js,
note.html. Autosaving textarea with char /
word count.
main.js:
- Extension point: sidebar-panel. One right-anchored WebContentsView
(SIDEBAR_W=340) hosts the current panel; layout() shrinks the tab
views by the sidebar width when visible. First registered panel
wins for MVP; picker for multiple panels lands later.
- initAddons() at app.whenReady(): seedBundledAddons, then
AddonHost.discoverAndActivate.
- IPC surface: sidebar-toggle / sidebar-open / sidebar-close /
sidebar-state, addons-list / addons-set-enabled / addons-reveal /
addons-open-dir / addons-reload, and origin-gated
addon-storage-get/set/all.
- Settings gains `disabledAddons: []` — off-toggled ids persist and
the loader honours them without a restart (discoverAndActivate
runs again on toggle).
chrome.html: toolbar sidebar-toggle button, hidden until at least one
add-on has registered a sidebar panel.
settings.html: new "Add-ons" section under privacy. Lists installed
add-ons with icon / name / version / description / capabilities;
per-add-on enable/disable toggle + Show folder button; page-level
Reload and Open add-ons folder buttons; warning note about the trust
model.
package.json: build.files gains sidebar-preload.js + addons-host.js.
extraResources gains bundled-addons/ so the packaged app carries the
reference notepad for the first-boot seed.
Verified: `npm start` boots, addons-host discovers the notepad,
activates it, registers one sidebar panel. Log confirms
"1 installed, 1 enabled, 1 sidebar panels". Actual sidebar rendering
+ notepad UI need clicked-through validation on a real install.
Not shipped yet - deploy still blocked on the fail2ban VPS SSH ban.
Ships as 0.2.0 once SSH clears (this is a new subsystem, not a fix).
2026-08-31 13:51:08 +02:00
|
|
|
|
openAddonsDir: () => ipcRenderer.invoke("addons-open-dir"),
|
|
|
|
|
|
reloadAddons: () => ipcRenderer.invoke("addons-reload"),
|
feat: community extensions — publish with a BCDN name, install from Settings, theseus.x catalog
Anyone who owns a BCDN name can now publish a Theseus extension, and every
Theseus can install it with the publisher's signature verified locally.
Gateway (Argus/src/gateway/public-gateway.mjs):
PUT /api/ext/<name>/<id>/<version> takes the gzipped tar, checks two BCH
message signatures against the name's current NFT owner (one authorises
the upload, one is stored in the channel), inspects the package
(addon.json at the root, id/version/main match, 8 MB cap), enforces
first-publisher ownership of an id and monotonic versions, and writes the
tarball, the extension's updates.json and community/catalog.json to Sia.
GET /api/ext/catalog reads the catalog back with CORS.
Theseus:
lib/publisher-sig.mjs recovers the signer of a channel entry; main.js
compares it with the publisher name's owner from Theseus's own chain
index before installing or updating, so neither the relay nor a tampered
catalog can pass off code under a trusted name. addon-updater.js gains
installCommunity() and accepts publisher-signed entries in the regular
update check (operator Ed25519 entries unchanged). Settings › Extensions
shows the community catalog with Install / Update; Settings › Plug-ins
links to theseus.x/plug-ins.
theseus.x:
/plug-ins/ is a separate page for the first-party plug-ins (Aegis,
Ariadne's Thread) with live versions and hashes; /extensions/ lists the
bundled extensions, the community catalog, and how to build and publish;
/extensions/publish/ signs and uploads a package in the browser with the
wallet that holds the publisher's name (session helper + wallet bundle
copied alongside).
2026-09-20 15:26:30 +02:00
|
|
|
|
// Community extensions from theseus.x/extensions: the catalog (with what
|
|
|
|
|
|
// is installed already) and a verified install/update of one entry.
|
|
|
|
|
|
communityCatalog: () => ipcRenderer.invoke("addons-community-catalog"),
|
|
|
|
|
|
installCommunity: (id) => ipcRenderer.invoke("addons-install-community", id),
|
2026-09-08 02:27:36 +02:00
|
|
|
|
checkAddonUpdates: () => ipcRenderer.invoke("addons-check-updates"),
|
|
|
|
|
|
listStagedAddonUpdates: () => ipcRenderer.invoke("addons-list-staged"),
|
2026-07-29 13:54:34 +02:00
|
|
|
|
});
|