theseus/bundled-addons/hermes/index.js

85 lines
3.6 KiB
JavaScript
Raw Normal View History

// Hermes bundled add-on — thin proxy to the host's panel API.
Hermes: Ermis shortcut (Ctrl+Shift+E), sidebar add-on, vCard import, brand kit Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as 'switch profile' and E matches the Greek spelling (Ermis). Sidebar discoverability: new bundled add-on at bundled-addons/hermes/ registers a left-strip icon + launcher panel. The panel is a thin chrome (mark + 'Open Messages' + shortcut hint) that forwards clicks through api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow. All real messaging code stays in main.js — add-on is purely for discovery. Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME / X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in NOTE fields. Candidates are previewed per-contact with checkboxes; nothing is added until the user confirms. Non-matching vCards are returned too (so the user can still add manually) rather than silently dropped. Nothing is uploaded; parsing is local. site/hermes: status badge flipped from 'designed - not yet built' to 'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by name, address book, local history (all live) and mobile + Tor (next / planned). Hero carries the new Ctrl+Shift+E shortcut and a link to /hermes/branding. site/hermes/branding: new page. Mark SVG (winged envelope, two variants for 16-20px and 24-160px display sizes), palette (reuses Silent Mode tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange — so it never collides with Hermès the fashion house), name explanation (English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0 attribution. Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a screen inside Ariadne. Reuses the wallet seed, aligns with the design doc's 'one seed many purposes' architecture, lowest total cost.
2026-10-21 00:45:25 +03:00
//
// All the real messaging code (NIP-17 wrap/unwrap, relay subscriptions,
// persistent inbox, contacts) lives in Theseus main.js. Too much raw network
// + crypto to run under the add-on sandbox, and the vault seed must never
// cross the host/add-on boundary.
Hermes: Ermis shortcut (Ctrl+Shift+E), sidebar add-on, vCard import, brand kit Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as 'switch profile' and E matches the Greek spelling (Ermis). Sidebar discoverability: new bundled add-on at bundled-addons/hermes/ registers a left-strip icon + launcher panel. The panel is a thin chrome (mark + 'Open Messages' + shortcut hint) that forwards clicks through api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow. All real messaging code stays in main.js — add-on is purely for discovery. Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME / X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in NOTE fields. Candidates are previewed per-contact with checkboxes; nothing is added until the user confirms. Non-matching vCards are returned too (so the user can still add manually) rather than silently dropped. Nothing is uploaded; parsing is local. site/hermes: status badge flipped from 'designed - not yet built' to 'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by name, address book, local history (all live) and mobile + Tor (next / planned). Hero carries the new Ctrl+Shift+E shortcut and a link to /hermes/branding. site/hermes/branding: new page. Mark SVG (winged envelope, two variants for 16-20px and 24-160px display sizes), palette (reuses Silent Mode tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange — so it never collides with Hermès the fashion house), name explanation (English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0 attribution. Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a screen inside Ariadne. Reuses the wallet seed, aligns with the design doc's 'one seed many purposes' architecture, lowest total cost.
2026-10-21 00:45:25 +03:00
//
// This file does two things:
// 1. Register the left-strip sidebar entry (so Hermes is discoverable
// without pressing Ctrl+Shift+E).
// 2. Proxy a small, allow-listed set of panel operations between the
// panel's silentmode.invoke() calls and main.js's `global._hermesAPI`.
// The panel never sees ipcRenderer — only this named handler surface.
//
// Events coming the other way (inbox updates, relay status changes) are
// fanned out via a shared ipcMain event bus `__hermes_bus`: main.js's
// hermesBusEmit publishes, this file forwards each payload to the panel.
Hermes: Ermis shortcut (Ctrl+Shift+E), sidebar add-on, vCard import, brand kit Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as 'switch profile' and E matches the Greek spelling (Ermis). Sidebar discoverability: new bundled add-on at bundled-addons/hermes/ registers a left-strip icon + launcher panel. The panel is a thin chrome (mark + 'Open Messages' + shortcut hint) that forwards clicks through api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow. All real messaging code stays in main.js — add-on is purely for discovery. Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME / X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in NOTE fields. Candidates are previewed per-contact with checkboxes; nothing is added until the user confirms. Non-matching vCards are returned too (so the user can still add manually) rather than silently dropped. Nothing is uploaded; parsing is local. site/hermes: status badge flipped from 'designed - not yet built' to 'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by name, address book, local history (all live) and mobile + Tor (next / planned). Hero carries the new Ctrl+Shift+E shortcut and a link to /hermes/branding. site/hermes/branding: new page. Mark SVG (winged envelope, two variants for 16-20px and 24-160px display sizes), palette (reuses Silent Mode tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange — so it never collides with Hermès the fashion house), name explanation (English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0 attribution. Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a screen inside Ariadne. Reuses the wallet seed, aligns with the design doc's 'one seed many purposes' architecture, lowest total cost.
2026-10-21 00:45:25 +03:00
const { ipcMain } = require("electron");
const PANEL_METHODS = [
hermes: name+password signup, Settings panel, seed backup gated by password Third signin option on the panel: enter a bare name + password, Hermes mints a 12-word BIP-39 mnemonic locally, derives the Nostr key, password-wraps the mnemonic for later Settings → Backup, and persists the sealed sk the same way the other paths do. Save-forever still handles routine signin — the password is only used to reveal the seed phrase and only from Settings. - main.js: hermesWrapMnemonic / hermesUnwrapMnemonic via scrypt (N=2^15) + aes-256-gcm. Wrapped blob at <userData>/hermes-identity/mnemonic.wrapped.json, plaintext JSON — the wrap itself is the protection. On sign-out the blob is nuked alongside the sealed sk. - main.js: init() now takes { create: true, handle, password }. Handle is validated (1-32 chars, kebab-friendly) and stored as `<name>.erm` so a mainnet free-manifest reservation lands on the same canonical form later. Mnemonic-path init optionally takes a password too — "promote this seed-phrase session to a password-protected backup" in one step. - main.js: new _hermesAPI.getBackup({password}) and .hasBackup. status() now returns handle + source so the panel can label the restored session. - panel.html: "Create a new account" button on signin reveals handle + password + confirm. ⋯ menu adds Settings… showing account info, display mode picker (hermes:alice / alice.erm / alice@hermes.tel — persisted via silentmode.storage), and the gated seed-phrase reveal. Reveal lays words out 12-across with 1-indexed numbering so a user copying to paper doesn't miscount. Backup section hides for vault / mnemonic sessions. Still off-chain only — handles created this way are local labels until the mainnet mint of the free manifest lets us register them against the `hermes.erm` signed-records store (see [[hermes-name-scheme-plan]]).
2026-10-21 17:14:26 +03:00
"status", "canUseVault", "close", "init", "signOut", "getBackup", "hasBackup",
"publishCloudBackup", "fetchCloudBackup", "hasCloudBackup", "removeCloudBackup",
"getProfile", "updateProfile",
hermes: phone discovery (opt-in), lookup contacts by number Users can link one or more phone numbers to their Hermes identity by publishing a kind:30078 claim event whose d-tag is a SHA-256 hash of the E.164 number. The number itself never leaves the device. Add-contact and message-send now also accept a +phone input directly — the client hashes it and queries relays for the matching Hermes identity. - lib/hermes.js: normalisePhone(str) strips separators + a leading 00 into E.164, rejects anything that doesn't fit +[country][7-15 digits]. hashPhone(str) → base64url-truncated-to-32 SHA-256 digest, used as the d-tag suffix under PHONE_DTAG_PREFIX. buildPhoneClaimEvent / buildPhoneRetractEvent emit the signed kind:30078 / NIP-09 kind:5. - main.js: _hermesAPI.getPhone / setPhone / clearPhone / findByPhones. Plaintext phone persists in the identity meta (locally only) so the user can see what's linked and retract it; event ids are kept so a retract can reference the exact wire event. hermesResolveTo takes a phone-shaped input and does the discovery lookup transparently, so contactsAdd and send accept phones without a new entry point. - panel.html: Settings → Phone discovery (link / unlink + privacy note), add-contact placeholder now lists +phone alongside the other forms. Privacy stance stated in the UI: "anyone who already knows your number can find you — same tradeoff as number-based messengers." No PSI tricks yet; plain hash. Users who don't want it just don't link, which is the default. Future slice can add a pepper if operators want to narrow the brute-force window.
2026-10-21 17:21:16 +03:00
"getPhone", "setPhone", "clearPhone", "findByPhones",
hermes: name+password signup, Settings panel, seed backup gated by password Third signin option on the panel: enter a bare name + password, Hermes mints a 12-word BIP-39 mnemonic locally, derives the Nostr key, password-wraps the mnemonic for later Settings → Backup, and persists the sealed sk the same way the other paths do. Save-forever still handles routine signin — the password is only used to reveal the seed phrase and only from Settings. - main.js: hermesWrapMnemonic / hermesUnwrapMnemonic via scrypt (N=2^15) + aes-256-gcm. Wrapped blob at <userData>/hermes-identity/mnemonic.wrapped.json, plaintext JSON — the wrap itself is the protection. On sign-out the blob is nuked alongside the sealed sk. - main.js: init() now takes { create: true, handle, password }. Handle is validated (1-32 chars, kebab-friendly) and stored as `<name>.erm` so a mainnet free-manifest reservation lands on the same canonical form later. Mnemonic-path init optionally takes a password too — "promote this seed-phrase session to a password-protected backup" in one step. - main.js: new _hermesAPI.getBackup({password}) and .hasBackup. status() now returns handle + source so the panel can label the restored session. - panel.html: "Create a new account" button on signin reveals handle + password + confirm. ⋯ menu adds Settings… showing account info, display mode picker (hermes:alice / alice.erm / alice@hermes.tel — persisted via silentmode.storage), and the gated seed-phrase reveal. Reveal lays words out 12-across with 1-indexed numbering so a user copying to paper doesn't miscount. Backup section hides for vault / mnemonic sessions. Still off-chain only — handles created this way are local labels until the mainnet mint of the free manifest lets us register them against the `hermes.erm` signed-records store (see [[hermes-name-scheme-plan]]).
2026-10-21 17:14:26 +03:00
"inbox", "send", "markThreadRead",
"contactsList", "contactsAdd", "contactsUpdate", "contactsRemove",
];
Hermes: Ermis shortcut (Ctrl+Shift+E), sidebar add-on, vCard import, brand kit Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as 'switch profile' and E matches the Greek spelling (Ermis). Sidebar discoverability: new bundled add-on at bundled-addons/hermes/ registers a left-strip icon + launcher panel. The panel is a thin chrome (mark + 'Open Messages' + shortcut hint) that forwards clicks through api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow. All real messaging code stays in main.js — add-on is purely for discovery. Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME / X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in NOTE fields. Candidates are previewed per-contact with checkboxes; nothing is added until the user confirms. Non-matching vCards are returned too (so the user can still add manually) rather than silently dropped. Nothing is uploaded; parsing is local. site/hermes: status badge flipped from 'designed - not yet built' to 'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by name, address book, local history (all live) and mobile + Tor (next / planned). Hero carries the new Ctrl+Shift+E shortcut and a link to /hermes/branding. site/hermes/branding: new page. Mark SVG (winged envelope, two variants for 16-20px and 24-160px display sizes), palette (reuses Silent Mode tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange — so it never collides with Hermès the fashion house), name explanation (English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0 attribution. Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a screen inside Ariadne. Reuses the wallet seed, aligns with the design doc's 'one seed many purposes' architecture, lowest total cost.
2026-10-21 00:45:25 +03:00
module.exports = {
activate(api) {
api.registerSidebarPanel({
id: "main",
title: "Hermes",
page: "panel.html",
// Dock on the LEFT strip (alongside mail/pithos/telegram etc.), not
// the right-hand add-on dock. The host checks `side` on both the
// manifest's panels[] entry and the registerSidebarPanel call —
// declaring it in both keeps dormant vs active renders consistent.
side: "left",
Hermes: Ermis shortcut (Ctrl+Shift+E), sidebar add-on, vCard import, brand kit Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as 'switch profile' and E matches the Greek spelling (Ermis). Sidebar discoverability: new bundled add-on at bundled-addons/hermes/ registers a left-strip icon + launcher panel. The panel is a thin chrome (mark + 'Open Messages' + shortcut hint) that forwards clicks through api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow. All real messaging code stays in main.js — add-on is purely for discovery. Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME / X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in NOTE fields. Candidates are previewed per-contact with checkboxes; nothing is added until the user confirms. Non-matching vCards are returned too (so the user can still add manually) rather than silently dropped. Nothing is uploaded; parsing is local. site/hermes: status badge flipped from 'designed - not yet built' to 'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by name, address book, local history (all live) and mobile + Tor (next / planned). Hero carries the new Ctrl+Shift+E shortcut and a link to /hermes/branding. site/hermes/branding: new page. Mark SVG (winged envelope, two variants for 16-20px and 24-160px display sizes), palette (reuses Silent Mode tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange — so it never collides with Hermès the fashion house), name explanation (English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0 attribution. Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a screen inside Ariadne. Reuses the wallet seed, aligns with the design doc's 'one seed many purposes' architecture, lowest total cost.
2026-10-21 00:45:25 +03:00
});
// Legacy launcher behavior: panel.html can still ask to open the
// standalone Messages window. Kept because Ctrl+Shift+E users have
// muscle memory, and because a few flows (debug, multi-window)
// benefit from a free-floating window.
Hermes: Ermis shortcut (Ctrl+Shift+E), sidebar add-on, vCard import, brand kit Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as 'switch profile' and E matches the Greek spelling (Ermis). Sidebar discoverability: new bundled add-on at bundled-addons/hermes/ registers a left-strip icon + launcher panel. The panel is a thin chrome (mark + 'Open Messages' + shortcut hint) that forwards clicks through api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow. All real messaging code stays in main.js — add-on is purely for discovery. Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME / X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in NOTE fields. Candidates are previewed per-contact with checkboxes; nothing is added until the user confirms. Non-matching vCards are returned too (so the user can still add manually) rather than silently dropped. Nothing is uploaded; parsing is local. site/hermes: status badge flipped from 'designed - not yet built' to 'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by name, address book, local history (all live) and mobile + Tor (next / planned). Hero carries the new Ctrl+Shift+E shortcut and a link to /hermes/branding. site/hermes/branding: new page. Mark SVG (winged envelope, two variants for 16-20px and 24-160px display sizes), palette (reuses Silent Mode tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange — so it never collides with Hermès the fashion house), name explanation (English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0 attribution. Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a screen inside Ariadne. Reuses the wallet seed, aligns with the design doc's 'one seed many purposes' architecture, lowest total cost.
2026-10-21 00:45:25 +03:00
api.onMessage("open-messages", () => {
ipcMain.emit("hermes-open-request");
return { ok: true };
});
// Panel API proxies. Every method listed in PANEL_METHODS invokes the
// same-named function on global._hermesAPI. The main.js side defines
// that object once hermes is initialised; before that, status/canUseVault
// are the only safe methods and will return a stub-ok.
for (const name of PANEL_METHODS) {
api.onMessage(name, async (payload) => {
const hermes = global._hermesAPI;
if (!hermes || typeof hermes[name] !== "function") {
// Host not ready. Treat status/canUseVault as "no identity yet"
// so the panel can render its signin screen without a toast.
if (name === "status") return { ok: true, ready: false };
if (name === "canUseVault") return { ok: true, available: false };
return { ok: false, error: "hermes host not ready" };
}
return await hermes[name](payload);
});
}
// Main-side → panel event bus. hermesBusEmit in main.js fires events on
// ipcMain under __hermes_bus; forward each to the panel via api.emit so
// panel-side `silentmode.on("hermes-message", cb)` works identically to
// the ipcRenderer path used by the standalone window.
const busForward = (channel, payload) => {
try { api.emit(channel, payload); } catch {}
};
ipcMain.on("__hermes_bus", busForward);
api.onDeactivate && api.onDeactivate(() => {
try { ipcMain.off("__hermes_bus", busForward); } catch {}
});
api.log("hermes sidebar panel registered (proxy mode)");
Hermes: Ermis shortcut (Ctrl+Shift+E), sidebar add-on, vCard import, brand kit Shortcut: Ctrl+Shift+M -> Ctrl+Shift+E. Chrome owns Ctrl+Shift+M as 'switch profile' and E matches the Greek spelling (Ermis). Sidebar discoverability: new bundled add-on at bundled-addons/hermes/ registers a left-strip icon + launcher panel. The panel is a thin chrome (mark + 'Open Messages' + shortcut hint) that forwards clicks through api.onMessage -> ipcMain.emit('hermes-open-request') -> openHermesWindow. All real messaging code stays in main.js — add-on is purely for discovery. Address book: opt-in vCard (.vcf) import. Dialog in the Add Contact modal lets the user pick a file; main.js parses BEGIN:VCARD blocks for X-BNS-NAME / X-NOSTR-NPUB / X-NOSTR-PUBKEY plus .bch names and npubs mentioned in NOTE fields. Candidates are previewed per-contact with checkboxes; nothing is added until the user confirms. Non-matching vCards are returned too (so the user can still add manually) rather than silently dropped. Nothing is uploaded; parsing is local. site/hermes: status badge flipped from 'designed - not yet built' to 'MVP - desktop'. New 'What ships today' grid covers threaded chat, send by name, address book, local history (all live) and mobile + Tor (next / planned). Hero carries the new Ctrl+Shift+E shortcut and a link to /hermes/branding. site/hermes/branding: new page. Mark SVG (winged envelope, two variants for 16-20px and 24-160px display sizes), palette (reuses Silent Mode tokens + an amber accent that's distinctly warm-yellow, not Hermes-orange — so it never collides with Hermès the fashion house), name explanation (English Hermes / Greek Ermis), voice notes, do/don't table, MPL-2.0 attribution. Hermes/MOBILE-RECOMMENDATION.md: analysis of three mobile paths (inside Ariadne / standalone / hybrid). Recommendation is Option 1: ship as a screen inside Ariadne. Reuses the wallet seed, aligns with the design doc's 'one seed many purposes' architecture, lowest total cost.
2026-10-21 00:45:25 +03:00
},
};