Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< title > Messages — Theseus< / title >
< style >
:root{
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
--bg:#0b0e14; --panel:#141a24; --panel2:#18202c; --panel3:#1d2634;
--line:rgba(255,255,255,.09); --line2:rgba(255,255,255,.14);
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
--ok:#4fd1a5; --err:#f6768a; --warn:#ffc75f;
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
--me:#2a3a1e; /* outgoing bubble */
--them:#1d2634; /* incoming bubble */
fix(theseus/light): darker --acid in light mode so brand green stays legible
#d6ff3d on a #ffffff / #f6f8fb background sits at ~1.3:1 contrast — the
acid green went almost invisible any time the user flipped Settings >
Theme to light. Override --acid to #4d7300 in every chrome page's
prefers-color-scheme: light block. Same hue family, ~5.5:1 contrast
on white, still reads as the same brand color.
Also: chrome.html was missing --acid: #d6ff3d in :root entirely (every
site used var(--acid, #d6ff3d) fallbacks). Adding the real declaration
means the light override can actually take effect.
Files touched: chrome.html, home.html, settings.html, error.html,
approval.html, messages.html. downloads.html is a dark-only overlay
(hardcoded), collision.html already had a proper light-mode --bcdn.
Tint fills (rgba(214,255,61,X) at low alpha) stay as-is — the specific
hue barely matters through 8% opacity.
Verified live via CDP: getComputedStyle(--acid) returned #d6ff3d in
dark, #4d7300 after cfg.set('theme','light').
2026-09-06 21:12:45 +02:00
}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0; background:var(--bg); color:var(--ink);
font:14px/1.5 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
display:flex; flex-direction:column; overflow:hidden;
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
}
header{
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
padding:10px 14px; border-bottom:1px solid var(--line);
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
background:rgba(11,14,20,.9); display:flex; align-items:center; gap:12px;
flex-wrap:wrap;
}
header .mark{font-size:20px}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
header h1{font-size:14px;font-weight:600;margin:0;letter-spacing:.2px}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
#ident{
color:var(--mut); font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
font-size:12px; margin-left:auto; user-select:text;
}
#ident b{color:var(--acid); font-weight:500}
#status{
font-size:11px; padding:2px 8px; border-radius:999px;
background:var(--panel); color:var(--mut);
}
#status.ok{color:var(--ok); background:rgba(79,209,165,.12)}
#status.err{color:var(--err); background:rgba(246,118,138,.12)}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
main{flex:1; min-height:0}
#setup, #app{padding:0; overflow:hidden; height:100%; display:none}
#setup{display:none; padding:16px; overflow:auto}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
body.mode-setup #setup{display:block}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
body.mode-app #app{display:grid; grid-template-columns:260px 1fr}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
/* ---------- SETUP ---------- */
#setup{max-width:540px; margin:0 auto; align-self:start}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
.lede{color:var(--mut); font-size:13px; margin:0 0 12px}
.warn{
font-size:12px; color:var(--warn); padding:8px 10px;
background:rgba(255,199,95,.08); border:1px solid rgba(255,199,95,.24);
border-radius:8px; margin-bottom:12px;
}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
.row{display:flex; gap:8px; align-items:center}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
code.inline{
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
background:rgba(255,255,255,.05); padding:1px 5px; border-radius:4px; font-size:12px;
}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
input, textarea, button, select{
font:inherit; color:var(--ink); background:var(--panel);
border:1px solid var(--line); border-radius:8px; padding:9px 11px;
}
textarea{resize:vertical; min-height:44px; max-height:200px; width:100%}
input{width:100%}
input:focus, textarea:focus, select:focus{outline:none; border-color:var(--acid)}
button{cursor:pointer; background:var(--acid); color:#0a0d13; font-weight:600; border-color:transparent}
button.ghost{background:var(--panel); color:var(--ink); border:1px solid var(--line); font-weight:400}
button:hover{filter:brightness(1.08)}
button:disabled{opacity:.5; cursor:default}
label{display:block; margin-bottom:12px; color:var(--mut); font-size:12px}
label > textarea, label > input{margin-top:6px}
#setupErr{color:var(--err); font-size:12px; min-height:1em; margin-top:8px}
/* ---------- APP (threaded) ---------- */
#sidebar{
display:flex; flex-direction:column; min-height:0;
background:var(--panel); border-right:1px solid var(--line);
}
#sideHead{
padding:10px 10px 6px; display:flex; gap:6px; align-items:center;
border-bottom:1px solid var(--line);
}
#sideHead input{padding:7px 9px; font-size:13px}
#addBtn{padding:7px 10px; font-size:13px}
#contactsList{overflow:auto; flex:1; min-height:0}
.contactRow{
padding:9px 12px; cursor:pointer; border-bottom:1px solid rgba(255,255,255,.03);
display:flex; align-items:center; gap:8px;
}
.contactRow:hover{background:var(--panel2)}
.contactRow.active{background:var(--panel3)}
.contactRow .avatar{
width:30px; height:30px; border-radius:50%; flex-shrink:0;
background:linear-gradient(135deg, #2a3a4a, #1a2432);
display:flex; align-items:center; justify-content:center;
font-size:12px; color:var(--mut); font-weight:600; text-transform:uppercase;
}
.contactRow .meta{min-width:0; flex:1}
.contactRow .name{
font-size:13px; font-weight:500; color:var(--ink);
overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.contactRow .last{
font-size:11px; color:var(--mut);
overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.contactRow .unread{
background:var(--acid); color:#0a0d13; font-size:10px; font-weight:700;
padding:1px 6px; border-radius:999px; min-width:18px; text-align:center;
}
#sidebarFoot{padding:8px 10px; border-top:1px solid var(--line); font-size:11px; color:var(--dim)}
#thread{display:flex; flex-direction:column; min-height:0; background:var(--bg)}
#threadHead{
padding:12px 16px; border-bottom:1px solid var(--line);
display:flex; align-items:center; gap:12px;
}
#threadHead .name{font-weight:600; font-size:14px}
#threadHead .pk{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11px; color:var(--mut)}
#threadHead .trash{
margin-left:auto; background:none; border:none; color:var(--dim); cursor:pointer;
padding:4px 8px; font-size:12px;
}
#threadHead .trash:hover{color:var(--err)}
#threadBody{
flex:1; overflow:auto; padding:12px 16px;
display:flex; flex-direction:column; gap:6px;
}
.bubble{max-width:70%; padding:7px 11px; border-radius:14px; line-height:1.4}
.bubble .body{white-space:pre-wrap; word-break:break-word}
.bubble .time{display:block; font-size:10px; color:var(--dim); margin-top:3px}
.bubble.in{background:var(--them); align-self:flex-start; border-bottom-left-radius:4px}
.bubble.out{background:var(--me); align-self:flex-end; border-bottom-right-radius:4px}
.empty{color:var(--dim); text-align:center; padding:32px 16px; font-size:13px}
#composeRow{display:flex; gap:8px; padding:10px 14px; border-top:1px solid var(--line); background:var(--panel)}
#composeRow textarea{flex:1; min-height:38px; max-height:160px; resize:none}
#sendBtn{padding:0 18px}
#sendStatus{font-size:11px; color:var(--mut); padding:0 14px 6px; min-height:12px}
/* ---------- Add contact / new thread modal ---------- */
.modal-bg{
position:fixed; inset:0; background:rgba(0,0,0,.5);
display:none; align-items:center; justify-content:center; z-index:30;
}
.modal-bg.show{display:flex}
.modal{
width:380px; max-width:90vw; background:var(--panel); border:1px solid var(--line2);
border-radius:12px; padding:16px;
}
.modal h2{margin:0 0 10px; font-size:14px}
.modal .buttons{display:flex; gap:8px; justify-content:flex-end; margin-top:10px}
.modal label{margin-bottom:10px}
#addErr{color:var(--err); font-size:12px; min-height:1em}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< / style >
< / head >
< body class = "mode-setup" >
< header >
< span class = "mark" > ⛓️✉️< / span >
< h1 > Hermes Messages< / h1 >
< span id = "status" > connecting…< / span >
< span id = "ident" > < / span >
< / header >
< main >
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
<!-- SETUP SCREEN -->
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< section id = "setup" >
2026-08-19 00:38:52 +02:00
< p class = "lede" > Hermes derives its Nostr identity at
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< code class = "inline" > silentmode/messenger/0< / code > — a purpose disjoint from your
wallet's transaction key, so a compromised relay never yields tx-signing power.
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
Message history is stored locally on this device, encrypted at rest.< / p >
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< div class = "warn" > This is a proof build. Use a wallet you don't hold funds in.< / div >
2026-08-19 00:38:52 +02:00
< div id = "vaultBind" style = "display:none; margin-bottom:16px" >
< div class = "row" >
< button id = "btnVault" > Sign in with password vault< / button >
< span style = "color:var(--mut); font-size:12px" > recommended — no mnemonic to re-type< / span >
< / div >
< div style = "text-align:center; color:var(--dim); font-size:12px; margin:14px 0" > — or —< / div >
< / div >
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< label >
Mnemonic (12 or 24 words)
< textarea id = "mnemonic" placeholder = "word word word …" autocomplete = "off" spellcheck = "false" > < / textarea >
< / label >
< div class = "row" >
< button id = "btnInit" > Open messages< / button >
< / div >
< div id = "setupErr" > < / div >
< / section >
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
<!-- APP (threaded) -->
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< section id = "app" >
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
< aside id = "sidebar" >
< div id = "sideHead" >
< input id = "search" placeholder = "Search contacts…" autocomplete = "off" >
< button id = "addBtn" title = "Add contact / start new chat" > +< / button >
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< / div >
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
< div id = "contactsList" > < / div >
< div id = "sidebarFoot" > Chats update live as messages arrive.< / div >
< / aside >
< section id = "thread" >
< div id = "threadHead" style = "display:none" >
< div >
< div class = "name" id = "threadName" > —< / div >
< div class = "pk" id = "threadPk" > —< / div >
< / div >
< button class = "trash" id = "btnRemoveContact" title = "Remove this contact" > remove< / button >
< / div >
< div id = "threadBody" >
< div class = "empty" > Pick a contact on the left — or hit < b > +< / b > to add one.< / div >
< / div >
< div id = "composeRow" style = "display:none" >
< textarea id = "composeText" placeholder = "Message…" rows = "1" > < / textarea >
< button id = "sendBtn" > Send< / button >
< / div >
< div id = "sendStatus" > < / div >
< / section >
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< / section >
< / main >
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
<!-- Add contact modal -->
< div class = "modal-bg" id = "addModal" >
< div class = "modal" >
< h2 > Add contact< / h2 >
< label >
Name (.bch), npub, or 64-char hex pubkey
< input id = "addName" placeholder = "alice.bch" autocomplete = "off" >
< / label >
< label >
Nickname (optional)
< input id = "addNick" placeholder = "Alice" autocomplete = "off" >
< / label >
< div id = "addErr" > < / div >
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
< div class = "buttons" style = "justify-content:space-between" >
< button class = "ghost" id = "importBtn" title = "Import contacts from a .vcf file" > Import vCard…< / button >
< div style = "display:flex; gap:8px" >
< button class = "ghost" id = "addCancel" > Cancel< / button >
< button id = "addSave" > Add< / button >
< / div >
< / div >
< / div >
< / div >
<!-- Import preview modal: a reviewable list of parsed contacts. Each row is
user-togglable; nothing is added without an explicit "Import N contacts" click.
This is the "opt-in and reviewable" part of the address-book integration. -->
< div class = "modal-bg" id = "importModal" >
< div class = "modal" style = "width:520px" >
< h2 > Import contacts < span id = "importFrom" style = "font-size:11px; color:var(--mut); font-weight:400" > < / span > < / h2 >
< p class = "lede" style = "font-size:12px" > Nothing is sent anywhere. Pick who you want to add; the rest are ignored.< / p >
< div id = "importList" style = "max-height:300px; overflow:auto; margin:10px 0; background:var(--panel2); border:1px solid var(--line); border-radius:8px; padding:4px" >
< div class = "empty" style = "padding:20px" > No importable contacts found.< / div >
< / div >
< div id = "importErr" style = "color:var(--err); font-size:12px; min-height:1em" > < / div >
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
< div class = "buttons" >
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
< button class = "ghost" id = "importCancel" > Close< / button >
< button id = "importConfirm" > Import selected< / button >
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
< / div >
< / div >
< / div >
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
< script >
const $ = (id) => document.getElementById(id);
const setMode = (m) => { document.body.className = "mode-" + m; };
const setStatus = (text, cls = "") => {
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
const el = $("status"); el.textContent = text; el.className = cls;
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
};
const shortPk = (pk) => pk ? pk.slice(0, 8) + "…" + pk.slice(-4) : "";
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
// Hermes is a .bch-first product — the TLD is noise in a chat list.
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
const displayName = (name) => name & & name.endsWith(".bch") ? name.slice(0, -4) : name;
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
const initials = (n) => {
if (!n) return "?";
const s = String(n).replace(/\.bch$/,"").trim();
return s.slice(0, 2);
};
const fmtTime = (ts) => {
if (!ts) return "";
const d = new Date(ts * 1000);
const sameDay = d.toDateString() === new Date().toDateString();
if (sameDay) return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
return d.toLocaleDateString([], { month: "short", day: "numeric" }) + " "
+ d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
};
// ---- State mirror of the main-side inbox ----
// Grouped by peerPkHex so the UI can be contact-centric.
let state = {
messages: [], // raw list, in arrival order
readState: {}, // peerPkHex -> latest createdAt the user has acknowledged
contacts: [], // address book from main
myPkHex: null,
selectedPk: null, // currently open thread
};
function threadsFromMessages() {
// Group messages by the OTHER party's pkHex. Each thread carries:
// { peerPkHex, peerName, messages: [...], lastAt, unreadCount }
const byPk = new Map();
for (const m of state.messages) {
const peerPk = (m.peerPkHex || m.senderPkHex || "").toLowerCase();
if (!peerPk) continue;
let t = byPk.get(peerPk);
if (!t) {
t = { peerPkHex: peerPk, peerName: null, messages: [], lastAt: 0, unreadCount: 0 };
byPk.set(peerPk, t);
}
// Prefer a non-null name from any message in this thread.
if (!t.peerName & & m.peerName) t.peerName = m.peerName;
t.messages.push(m);
if (m.createdAt > t.lastAt) t.lastAt = m.createdAt;
}
// Union with contacts so an empty thread (just added) still shows up.
for (const c of state.contacts) {
const k = (c.pkHex || "").toLowerCase();
if (!k) continue;
if (!byPk.has(k)) byPk.set(k, { peerPkHex: k, peerName: c.nickname || c.name, messages: [], lastAt: 0, unreadCount: 0 });
else if (!byPk.get(k).peerName) byPk.get(k).peerName = c.nickname || c.name;
}
// Unread = incoming messages newer than the readState for that peer.
for (const t of byPk.values()) {
const read = state.readState[t.peerPkHex] || 0;
t.unreadCount = t.messages.filter((m) => m.direction === "in" & & m.createdAt > read).length;
}
return [...byPk.values()].sort((a, b) => b.lastAt - a.lastAt);
}
function renderContacts() {
const list = $("contactsList");
const q = $("search").value.trim().toLowerCase();
const threads = threadsFromMessages();
const filtered = q
? threads.filter((t) => (t.peerName || "").toLowerCase().includes(q)
|| t.peerPkHex.includes(q))
: threads;
if (!filtered.length) {
list.innerHTML = '< div class = "empty" > No chats yet.< br > Hit < b > +< / b > to start one.< / div > ';
return;
}
list.innerHTML = "";
for (const t of filtered) {
const row = document.createElement("div");
row.className = "contactRow" + (t.peerPkHex === state.selectedPk ? " active" : "");
const label = displayName(t.peerName) || shortPk(t.peerPkHex);
const lastMsg = t.messages[t.messages.length - 1];
const lastText = lastMsg
? (lastMsg.direction === "out" ? "you: " : "") + lastMsg.text
: "no messages yet";
row.innerHTML = `
< div class = "avatar" > < / div >
< div class = "meta" >
< div class = "name" > < / div >
< div class = "last" > < / div >
< / div >
${t.unreadCount > 0 ? `< span class = "unread" > ${t.unreadCount}< / span > ` : ""}
`;
row.querySelector(".avatar").textContent = initials(label);
row.querySelector(".name").textContent = label;
row.querySelector(".last").textContent = lastText;
row.addEventListener("click", () => selectThread(t.peerPkHex));
list.appendChild(row);
}
}
function renderThread() {
const body = $("threadBody");
const head = $("threadHead");
const compose = $("composeRow");
if (!state.selectedPk) {
head.style.display = "none";
compose.style.display = "none";
body.innerHTML = '< div class = "empty" > Pick a contact on the left — or hit < b > +< / b > to add one.< / div > ';
return;
}
const threads = threadsFromMessages();
const t = threads.find((x) => x.peerPkHex === state.selectedPk);
const label = (t & & displayName(t.peerName)) || shortPk(state.selectedPk);
head.style.display = "flex";
$("threadName").textContent = label;
$("threadPk").textContent = state.selectedPk;
compose.style.display = "flex";
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
if (!t || !t.messages.length) {
body.innerHTML = '< div class = "empty" > No messages yet. Say hi.< / div > ';
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
return;
}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
body.innerHTML = "";
for (const m of t.messages) {
const b = document.createElement("div");
b.className = "bubble " + (m.direction === "out" ? "out" : "in");
const txt = document.createElement("div");
txt.className = "body";
txt.textContent = m.text; // never innerHTML for untrusted content
const t2 = document.createElement("span");
t2.className = "time";
t2.textContent = fmtTime(m.createdAt);
b.appendChild(txt); b.appendChild(t2);
body.appendChild(b);
}
body.scrollTop = body.scrollHeight;
// Mark this thread read up to the latest message, if any incoming is newer.
const latestIn = Math.max(0, ...t.messages.filter((m) => m.direction === "in").map((m) => m.createdAt));
const curRead = state.readState[state.selectedPk] || 0;
if (latestIn > curRead) {
state.readState[state.selectedPk] = latestIn;
hermes.markThreadRead(state.selectedPk, latestIn).catch(() => {});
renderContacts(); // refresh unread badges
}
}
function selectThread(pkHex) {
state.selectedPk = pkHex.toLowerCase();
renderContacts();
renderThread();
}
async function reloadAll() {
const [ibx, cs] = await Promise.all([hermes.inbox(), hermes.contacts.list()]);
if (ibx & & ibx.ok) {
state.messages = ibx.messages || [];
state.readState = ibx.readState || {};
}
if (cs & & cs.ok) state.contacts = cs.contacts || [];
renderContacts();
renderThread();
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
// ---- Setup flow ----
2026-08-19 00:38:52 +02:00
function afterInit(res) {
if (!res.ok) { $("setupErr").textContent = "error: " + res.err; return false; }
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
state.myPkHex = res.pkHex;
2026-08-19 00:38:52 +02:00
$("ident").innerHTML = "your npub: < b > " + res.npub.slice(0, 12) + "…" + res.npub.slice(-6) + "< / b > "
+ (res.source ? " < span style = 'color:var(--dim); font-size:11px' > (" + res.source + ")< / span > " : "");
setStatus(res.relaysConnected + "/" + res.relaysTotal + " relays",
res.relaysConnected > 0 ? "ok" : "");
setMode("app");
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
reloadAll();
2026-08-19 00:38:52 +02:00
return true;
}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
$("btnInit").addEventListener("click", async () => {
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
const mn = $("mnemonic").value.trim();
if (!mn) { $("setupErr").textContent = "enter a mnemonic"; return; }
$("btnInit").disabled = true; $("setupErr").textContent = "";
const res = await hermes.init(mn);
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
$("btnInit").disabled = false;
2026-08-19 00:38:52 +02:00
afterInit(res);
});
$("btnVault").addEventListener("click", async () => {
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
$("btnVault").disabled = true; $("setupErr").textContent = "";
2026-08-19 00:38:52 +02:00
const res = await hermes.initFromVault();
$("btnVault").disabled = false;
afterInit(res);
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
});
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
// ---- App events ----
$("search").addEventListener("input", renderContacts);
$("addBtn").addEventListener("click", () => {
$("addName").value = ""; $("addNick").value = ""; $("addErr").textContent = "";
$("addModal").classList.add("show"); $("addName").focus();
});
$("addCancel").addEventListener("click", () => $("addModal").classList.remove("show"));
$("addSave").addEventListener("click", async () => {
const name = $("addName").value.trim();
const nick = $("addNick").value.trim() || null;
if (!name) { $("addErr").textContent = "enter a name or key"; return; }
$("addSave").disabled = true; $("addErr").textContent = "";
const res = await hermes.contacts.add(name, nick);
$("addSave").disabled = false;
if (!res.ok) { $("addErr").textContent = res.err; return; }
$("addModal").classList.remove("show");
state.contacts = res.contacts || [];
renderContacts();
if (res.contact & & res.contact.pkHex) selectThread(res.contact.pkHex);
});
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
// ---- vCard import flow (opt-in) ----
$("importBtn").addEventListener("click", async () => {
$("addErr").textContent = "";
const res = await hermes.contacts.importPick();
if (!res.ok) { $("addErr").textContent = "import: " + res.err; return; }
const candidates = res.candidates || [];
if (!candidates.length) { $("addErr").textContent = "nothing to import from that file"; return; }
// Switch to the import preview modal.
$("addModal").classList.remove("show");
$("importFrom").textContent = res.fromFile ? "from " + res.fromFile : "";
$("importList").innerHTML = "";
$("importErr").textContent = "";
const importable = candidates.filter((c) => c.importable);
if (!importable.length) {
$("importList").innerHTML = '< div class = "empty" style = "padding:20px" > No contacts in that file carry a .bch name or Nostr pubkey.< br > < small > You can still add them manually one at a time.< / small > < / div > ';
} else {
for (const c of importable) {
const row = document.createElement("label");
row.style.cssText = "display:flex; gap:10px; padding:8px 10px; align-items:center; cursor:pointer; border-radius:6px;";
const cb = document.createElement("input");
cb.type = "checkbox"; cb.checked = true;
cb.dataset.identifier = c.identifier;
cb.dataset.nickname = c.displayName || "";
const meta = document.createElement("div");
meta.style.cssText = "flex:1; min-width:0";
const title = document.createElement("div");
title.style.cssText = "font-weight:500; font-size:13px; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap";
title.textContent = c.displayName || c.identifier;
const sub = document.createElement("div");
sub.style.cssText = "font-size:11px; color:var(--mut); font-family:ui-monospace,monospace; overflow:hidden; text-overflow:ellipsis; white-space:nowrap";
sub.textContent = c.identifier;
meta.appendChild(title); meta.appendChild(sub);
row.appendChild(cb); row.appendChild(meta);
$("importList").appendChild(row);
}
}
$("importModal").classList.add("show");
});
$("importCancel").addEventListener("click", () => $("importModal").classList.remove("show"));
$("importConfirm").addEventListener("click", async () => {
const picked = Array.from($("importList").querySelectorAll('input[type=checkbox]:checked'));
if (!picked.length) { $("importErr").textContent = "nothing selected"; return; }
$("importConfirm").disabled = true;
let added = 0, failed = 0;
for (const cb of picked) {
const res = await hermes.contacts.add(cb.dataset.identifier, cb.dataset.nickname || null);
if (res & & res.ok) added++; else failed++;
}
$("importConfirm").disabled = false;
$("importErr").textContent = `added ${added}, failed ${failed}`;
if (added) await reloadAll();
if (added & & !failed) {
setTimeout(() => $("importModal").classList.remove("show"), 1200);
}
});
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
$("btnRemoveContact").addEventListener("click", async () => {
if (!state.selectedPk) return;
if (!confirm("Remove this contact? Message history for this thread stays on disk.")) return;
await hermes.contacts.remove(state.selectedPk);
state.selectedPk = null;
await reloadAll();
});
$("composeText").addEventListener("keydown", (e) => {
if (e.key === "Enter" & & !e.shiftKey) { e.preventDefault(); sendCurrent(); }
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
});
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
$("sendBtn").addEventListener("click", sendCurrent);
async function sendCurrent() {
const text = $("composeText").value.trim();
if (!text || !state.selectedPk) return;
$("sendBtn").disabled = true;
$("sendStatus").textContent = "sending…";
// Send by pkHex (direct) — recipient is already known for an existing thread.
const res = await hermes.send(state.selectedPk, text);
$("sendBtn").disabled = false;
if (!res.ok) {
$("sendStatus").textContent = "error: " + res.err;
return;
}
$("sendStatus").textContent = `sent to ${res.accepted}/${res.total} relay(s)`;
$("composeText").value = "";
// The main-side send handler records the outgoing message via hermesRecord,
// which pushes a "hermes-message" to us — that triggers reloadAll + rerender.
setTimeout(() => { $("sendStatus").textContent = ""; }, 3000);
}
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
// ---- Pushes from main ----
hermes.onMessage(() => reloadAll());
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
hermes.onStatus((s) => {
setStatus(s.relaysConnected + "/" + s.relaysTotal + " relays",
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
s.relaysConnected > 0 ? "ok" : "");
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
});
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
// ---- Boot ----
2026-08-19 00:38:52 +02:00
hermes.status().then(async (r) => {
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
if (r & & r.ok & & r.ready) {
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
state.myPkHex = r.pkHex;
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
$("ident").innerHTML = "your npub: < b > " + r.npub.slice(0, 12) + "…" + r.npub.slice(-6) + "< / b > ";
setStatus(r.relaysConnected + "/" + r.relaysTotal + " relays", "ok");
setMode("app");
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
reloadAll();
2026-08-19 00:38:52 +02:00
return;
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
}
Hermes MVP: threaded chat UI, contacts, persistent history, --messages flag
Builds on the previous Hermes sessions (NIP-17 crypto + chipnet hermes.bch
+ Theseus Messages window) to turn the flat inbox into something usable.
main.js (hermes block):
- Persistent inbox per identity at
<userData>/hermes/<pkHex>/inbox.jsonl (append-only JSONL, replayed on
init). Read-state at <userData>/hermes/<pkHex>/read.json so unread
badges survive restarts.
- Address book at <userData>/hermes/contacts.json: list / add / update /
remove. hermes-contacts-add resolves .bch name OR npub OR hex pk,
canonicalises, writes.
- Reverse-resolve consults contacts first, chain second: a user-curated
nickname wins over the on-chain np lookup.
- hermes-send also records the outgoing message to the inbox so thread
view shows both sides (only when at least one relay accepted).
- Inbox messages carry direction + peerPkHex + peerName so the renderer
can group by thread without re-doing the pairing logic.
- --messages CLI flag opens the panel at launch (first instance via
whenReady, second-instance via handleLaunch) so the dev harness and
power users don't need Ctrl+Shift+M.
- Dev-only auto-init: THESEUS_HERMES_DEV_MNEMONIC env, gated on
THESEUS_USER_DATA so it refuses to touch a production profile.
- HERMES_INBOX_LIMIT raised 200 -> 2000 now that history actually sticks.
messages-preload.js:
- Adds hermes.contacts.{list, add, update, remove} and
hermes.markThreadRead; status + onMessage/onStatus unchanged.
messages.html:
- Replaces the flat inbox with a two-column layout: contact list (search
+ unread badges + last-message preview) on the left, selected thread
on the right with sender bubbles, timestamps, inline Enter-to-send
compose, remove-contact affordance.
- .bch suffix stripped from displayed names (TLD is noise in chat UI;
other TLDs stay as disambiguation).
- Mark-thread-read fires on open; vault-bind path kept intact.
2026-10-20 23:21:34 +03:00
setStatus("locked");
2026-08-19 00:38:52 +02:00
const v = await hermes.canUseVault();
if (v & & v.ok & & v.available) $("vaultBind").style.display = "block";
Hermes: wire NIP-17 messaging into Theseus, provision chipnet hermes.bch
- Hermes/proof/: standalone keystone — BIP-39→Nostr, NIP-17 wrap/unwrap,
name-addressed send/receive verified end-to-end via public relay
- Argus/src/lib/hermes-derive.js: HKDF(silentmode/messenger/0) using libauth
so the registrar can compute np without pulling nostr-tools into Argus
- Argus/src/register-hermes-chipnet.mjs: idempotent REG/UPD script; publishes
np (Nostr pubkey) and nr (relay) records for a chipnet name
- TheseusNavigator/lib/hermes.js: same derivation + wrap/unwrap + record
parsing, canonical for Theseus; guarded by lib/package.json type:module
- TheseusNavigator/messages.html + messages-preload.js: Messages panel UI
(identity from mnemonic, live inbox, compose by .bch name), .bch suffix
stripped from displayed names
- TheseusNavigator/main.js: HERMES_MOD + loadHermesLib next to VAULT_MOD;
ipc handlers (hermes-status/init/close/inbox/send/open), per-relay
subscription with auto-reconnect, status pushed on WS open/close,
reverse-resolve pk -> .bch name via cached BNS index, Ctrl+Shift+M shortcut
via web-contents-created (works from any tab)
- Chipnet hermes.bch registered with np=f2c92519...67f4 nr=wss://nos.lol
(txid 7fc6de4544c13b782f163fdb892ea6749785886d05a336282fa659d722c7e92b);
end-to-end verified in Theseus
2026-08-18 20:14:43 +02:00
});
< / script >
< / body >
< / html >