hermes: use the vector mark everywhere, retire the raster paths

User supplied the real vector source at Hermes/Icons/file.svg. Dropped the
background rectangle, kept the sky-blue foreground + dark inner detail
cutouts, squared the viewBox around the content bbox with a small margin.
Result: ~6.6 KB SVG, pixel-perfect at every size — no more 60x downscale
blur.

- bundled-addons/hermes/assets/mark.svg: tight-cropped vector
  (viewBox centres the icon in a square so 1:1 slots don't crop).
- bundled-addons/hermes/assets/mark-64.svg: same, pre-normalised to a
  0..64 viewBox for legacy consumers that assume that range.
- bundled-addons/hermes/addon.json: version 0.2.4; manifest icon is now
  the SVG as a data:image/svg+xml;base64 URI (8.8 KB vs the previous
  16 KB PNG data URI).
- bundled-addons/hermes/panel.html: both img tags point at mark.svg at
  24 and 72 px.
- home.html / home-brand/hermes-mark.svg: Hermes.X tile uses the vector.
- site/hermes/{favicon,branding/mark}.svg: both now the user's artwork.
- site-hermes-x/assets/{favicon,mark}.svg: same.
- All /hermes site HTML img tags swapped from mark.webp to mark.svg.

Installed bundle + user-data cache hot-patched to 0.2.4 so this takes
effect on the running Theseus without a reinstall.
This commit is contained in:
Local Dev 2026-10-21 22:52:18 +03:00
parent 60821e4eae
commit f8628b0bb3
5 changed files with 601 additions and 616 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 329 B

After

Width:  |  Height:  |  Size: 6.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -1,185 +1,185 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hermes.x — brand kit</title>
<meta name="description" content="Hermes brand kit: mark, palette, name, voice.">
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="../assets/shared.css">
<style>
.mark-xl{color:var(--sky); line-height:0; display:inline-flex}
.mark-xl svg{width:160px; height:160px}
.mark-row{display:flex;gap:16px;align-items:center;flex-wrap:wrap;margin:1rem 0 2rem}
.mark-row .cell{display:flex;flex-direction:column;align-items:center;gap:10px;padding:16px;background:var(--panel);border:1px solid var(--line);border-radius:12px;min-width:130px}
.mark-row .cell small{color:var(--mut);font-size:11px;text-align:center}
.mark-row .cell svg{display:block}
.mark-16{color:var(--ink);line-height:0}
.mark-16 svg{width:16px;height:16px}
.mark-32{color:var(--ink);line-height:0}
.mark-32 svg{width:32px;height:32px}
.mark-64{color:var(--sky);line-height:0}
.mark-64 svg{width:64px;height:64px}
.mark-neg{color:#06111a;line-height:0;padding:14px 18px;border-radius:12px;background:var(--sky)}
.mark-neg svg{width:48px;height:48px}
.swatches{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin:1rem 0 2rem}
.sw{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.sw .block{height:70px}
.sw .meta{padding:10px 12px;font-size:12px;color:var(--mut)}
.sw .meta b{display:block;color:var(--ink);font-size:13px;font-weight:600;margin-bottom:2px}
.sw .meta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.tbl{width:100%;border-collapse:collapse;margin:1rem 0 2rem;font-size:14px}
.tbl th,.tbl td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:top}
.tbl th{color:var(--mut);font-weight:500;font-size:12px;text-transform:uppercase;letter-spacing:.4px}
.tbl td.y{color:var(--ok)} .tbl td.n{color:var(--err)}
.dl-row{display:flex;gap:10px;flex-wrap:wrap;margin:1rem 0}
.dl-row a{display:inline-flex;align-items:center;gap:8px;text-decoration:none;padding:10px 14px;border-radius:999px;background:var(--panel);color:var(--ink);border:1px solid var(--line);font-size:13px}
.dl-row a:hover{background:var(--panel2);border-color:var(--line2)}
</style>
</head>
<body>
<nav class="topnav">
<a class="brand" href="../">
<img src="../assets/mark.webp" alt="Hermes" width="5" height="22" style="display:block">
<span>Hermes<span class="x">.X</span></span>
</a>
<a href="../">Overview</a>
<a href="../messenger/">Messenger</a>
<a href="./" class="here">Branding</a>
<a href="https://theseus.x/" class="get">Get Theseus</a>
</nav>
<header class="hero">
<div class="mark mark-xl" aria-hidden="true">
<img src="../assets/mark.webp" alt="Hermes" width="3" height="22" style="display:block">
</div>
<h1>Brand kit</h1>
<p class="tag">Mark, palette, name, voice. One page so client builds, add-on
chrome, screenshots and third-party press stay consistent.</p>
</header>
<div class="wrap narrow">
<section class="plain">
<h2>The name</h2>
<p>In English the product is <b>Hermes</b> — after the Greek god of
messengers, travellers, and crossings. In modern Greek the same name is
<b>Ermis</b> (Ἑρμῆς). Written form is always Hermes outside Greek contexts.</p>
<p>The keyboard shortcut inside Theseus is <code class="inline">Ctrl+Shift+E</code>
— E for Ermis, off the collision path with Chrome's own Ctrl+Shift+M.</p>
</section>
<section>
<h2>The mark</h2>
<p class="lede">A tilted envelope with three speed-trails on the back edge
— a letter mid-flight. Monochrome, scales from 16 px (sidebar icon) to
160 px (hero) without legibility loss. SVG uses
<code class="inline">currentColor</code> so it inherits whatever text
colour it sits in.</p>
<div class="mark-row">
<div class="cell">
<div class="mark-16"><img src="../assets/mark.webp" alt="Hermes" width="5" height="22" style="display:block"></div>
<small>16 px — stroke variant (one trail)</small>
</div>
<div class="cell">
<div class="mark-32"><img src="../assets/mark.webp" alt="Hermes" width="3" height="22" style="display:block"></div>
<small>32 px — standard (3 tapered trails)</small>
</div>
<div class="cell">
<div class="mark-64"><img src="../assets/mark.webp" alt="Hermes" width="3" height="22" style="display:block"></div>
<small>64 px — in-panel</small>
</div>
<div class="cell">
<div class="mark-neg"><img src="../assets/mark.webp" alt="Hermes" width="3" height="22" style="display:block"></div>
<small>on sky background</small>
</div>
</div>
<div class="dl-row">
<a href="../assets/mark.webp" download>⬇️ mark.webp (main — the signin / top-bar icon)</a>
<a href="../assets/mark-wings-both.webp" download>⬇️ mark-wings-both.webp (variant — full upper + lower wings)</a>
</div>
<table class="tbl">
<thead><tr><th>Do</th><th>Don't</th></tr></thead>
<tbody>
<tr>
<td class="y">Keep the envelope tilted ~22°, trails on the back-left.</td>
<td class="n">Mirror the mark or tilt it the opposite direction.</td>
</tr>
<tr>
<td class="y">Let the mark inherit text colour (currentColor).</td>
<td class="n">Hard-code a colour inside the SVG; it limits reuse.</td>
</tr>
<tr>
<td class="y">Use the stroke variant below 24 px display size.</td>
<td class="n">Scale the standard mark below 20 px — trails merge.</td>
</tr>
<tr>
<td class="y">Pair with the wordmark "hermes.x" when context is unclear.</td>
<td class="n">Apply to a product with a different protocol or trust model.</td>
</tr>
</tbody>
</table>
</section>
<section>
<h2>Palette</h2>
<p class="lede">Hermes rides on a sky-blue primary — fast, airborne, legible
on both dark and light. The deep navy background is the natural pairing;
the dim tones are for muted type.</p>
<div class="swatches">
<div class="sw"><div class="block" style="background:#0b0e14"></div><div class="meta"><b>bg</b><code>#0b0e14</code> — primary surface</div></div>
<div class="sw"><div class="block" style="background:#141a24"></div><div class="meta"><b>panel</b><code>#141a24</code> — cards, sidebar</div></div>
<div class="sw"><div class="block" style="background:#38BDF8"></div><div class="meta"><b>sky</b><code>#38BDF8</code> — primary accent (mark)</div></div>
<div class="sw"><div class="block" style="background:#60A5FA"></div><div class="meta"><b>sky-soft</b><code>#60A5FA</code> — hover, secondary</div></div>
<div class="sw"><div class="block" style="background:#0EA5E9"></div><div class="meta"><b>sky-deep</b><code>#0EA5E9</code> — pressed / active</div></div>
<div class="sw"><div class="block" style="background:#FF6D00"></div><div class="meta"><b>orange</b><code>#FF6D00</code> — alternate accent</div></div>
<div class="sw"><div class="block" style="background:#FF8C1A"></div><div class="meta"><b>orange-light</b><code>#FF8C1A</code> — hover, secondary</div></div>
<div class="sw"><div class="block" style="background:#E65100"></div><div class="meta"><b>orange-deep</b><code>#E65100</code> — pressed / active</div></div>
<div class="sw"><div class="block" style="background:#e7eaf1"></div><div class="meta"><b>ink</b><code>#e7eaf1</code> — body text</div></div>
<div class="sw"><div class="block" style="background:#8b98a9"></div><div class="meta"><b>mut</b><code>#8b98a9</code> — muted text</div></div>
<div class="sw"><div class="block" style="background:#ffc75f"></div><div class="meta"><b>warn</b><code>#ffc75f</code> — warnings, unread accents</div></div>
</div>
</section>
<section>
<h2>Voice</h2>
<ul class="steps">
<li><b>Blunt about limits.</b>
<span>"Relays still see recipient and timing" is the tone, not "fully
anonymous messaging". Say what the design does and does not solve.</span></li>
<li><b>No evangelism about Nostr.</b>
<span>Nostr is the transport, not the product. Mention it where it
matters (relays, pubkeys, NIP-17) and don't elsewhere.</span></li>
<li><b>"Message by name" is the one-line pitch.</b>
<span>Everything else is supporting detail.</span></li>
<li><b>Lowercase for product nouns in body copy.</b>
<span>hermes.x, messenger, panel. Capital Hermes for titles and the first
mention per page.</span></li>
</ul>
</section>
<section>
<h2>Attribution + reuse</h2>
<p>Mark + palette are MPL-2.0 along with the rest of the Hermes client source.
Use for reviews, press, interoperability docs and the client's own chrome.
Don't apply to products that aren't actually Hermes (forks with different
protocols or trust models included).</p>
</section>
</div>
<footer>
<div>hermes.x — a <a href="https://silentmode.st/">Silent Mode</a> project.</div>
<div class="fine">Named for the messenger of the gods. Ermis in modern Greek. Spelled Hermes in English.</div>
</footer>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hermes.x — brand kit</title>
<meta name="description" content="Hermes brand kit: mark, palette, name, voice.">
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="../assets/shared.css">
<style>
.mark-xl{color:var(--sky); line-height:0; display:inline-flex}
.mark-xl svg{width:160px; height:160px}
.mark-row{display:flex;gap:16px;align-items:center;flex-wrap:wrap;margin:1rem 0 2rem}
.mark-row .cell{display:flex;flex-direction:column;align-items:center;gap:10px;padding:16px;background:var(--panel);border:1px solid var(--line);border-radius:12px;min-width:130px}
.mark-row .cell small{color:var(--mut);font-size:11px;text-align:center}
.mark-row .cell svg{display:block}
.mark-16{color:var(--ink);line-height:0}
.mark-16 svg{width:16px;height:16px}
.mark-32{color:var(--ink);line-height:0}
.mark-32 svg{width:32px;height:32px}
.mark-64{color:var(--sky);line-height:0}
.mark-64 svg{width:64px;height:64px}
.mark-neg{color:#06111a;line-height:0;padding:14px 18px;border-radius:12px;background:var(--sky)}
.mark-neg svg{width:48px;height:48px}
.swatches{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin:1rem 0 2rem}
.sw{background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.sw .block{height:70px}
.sw .meta{padding:10px 12px;font-size:12px;color:var(--mut)}
.sw .meta b{display:block;color:var(--ink);font-size:13px;font-weight:600;margin-bottom:2px}
.sw .meta code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.tbl{width:100%;border-collapse:collapse;margin:1rem 0 2rem;font-size:14px}
.tbl th,.tbl td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:top}
.tbl th{color:var(--mut);font-weight:500;font-size:12px;text-transform:uppercase;letter-spacing:.4px}
.tbl td.y{color:var(--ok)} .tbl td.n{color:var(--err)}
.dl-row{display:flex;gap:10px;flex-wrap:wrap;margin:1rem 0}
.dl-row a{display:inline-flex;align-items:center;gap:8px;text-decoration:none;padding:10px 14px;border-radius:999px;background:var(--panel);color:var(--ink);border:1px solid var(--line);font-size:13px}
.dl-row a:hover{background:var(--panel2);border-color:var(--line2)}
</style>
</head>
<body>
<nav class="topnav">
<a class="brand" href="../">
<img src="../assets/mark.svg" alt="Hermes" width="5" height="22" style="display:block">
<span>Hermes<span class="x">.X</span></span>
</a>
<a href="../">Overview</a>
<a href="../messenger/">Messenger</a>
<a href="./" class="here">Branding</a>
<a href="https://theseus.x/" class="get">Get Theseus</a>
</nav>
<header class="hero">
<div class="mark mark-xl" aria-hidden="true">
<img src="../assets/mark.svg" alt="Hermes" width="3" height="22" style="display:block">
</div>
<h1>Brand kit</h1>
<p class="tag">Mark, palette, name, voice. One page so client builds, add-on
chrome, screenshots and third-party press stay consistent.</p>
</header>
<div class="wrap narrow">
<section class="plain">
<h2>The name</h2>
<p>In English the product is <b>Hermes</b> — after the Greek god of
messengers, travellers, and crossings. In modern Greek the same name is
<b>Ermis</b> (Ἑρμῆς). Written form is always Hermes outside Greek contexts.</p>
<p>The keyboard shortcut inside Theseus is <code class="inline">Ctrl+Shift+E</code>
— E for Ermis, off the collision path with Chrome's own Ctrl+Shift+M.</p>
</section>
<section>
<h2>The mark</h2>
<p class="lede">A tilted envelope with three speed-trails on the back edge
— a letter mid-flight. Monochrome, scales from 16 px (sidebar icon) to
160 px (hero) without legibility loss. SVG uses
<code class="inline">currentColor</code> so it inherits whatever text
colour it sits in.</p>
<div class="mark-row">
<div class="cell">
<div class="mark-16"><img src="../assets/mark.svg" alt="Hermes" width="5" height="22" style="display:block"></div>
<small>16 px — stroke variant (one trail)</small>
</div>
<div class="cell">
<div class="mark-32"><img src="../assets/mark.svg" alt="Hermes" width="3" height="22" style="display:block"></div>
<small>32 px — standard (3 tapered trails)</small>
</div>
<div class="cell">
<div class="mark-64"><img src="../assets/mark.svg" alt="Hermes" width="3" height="22" style="display:block"></div>
<small>64 px — in-panel</small>
</div>
<div class="cell">
<div class="mark-neg"><img src="../assets/mark.svg" alt="Hermes" width="3" height="22" style="display:block"></div>
<small>on sky background</small>
</div>
</div>
<div class="dl-row">
<a href="../assets/mark.svg" download>⬇️ mark.webp (main — the signin / top-bar icon)</a>
<a href="../assets/mark-wings-both.webp" download>⬇️ mark-wings-both.webp (variant — full upper + lower wings)</a>
</div>
<table class="tbl">
<thead><tr><th>Do</th><th>Don't</th></tr></thead>
<tbody>
<tr>
<td class="y">Keep the envelope tilted ~22°, trails on the back-left.</td>
<td class="n">Mirror the mark or tilt it the opposite direction.</td>
</tr>
<tr>
<td class="y">Let the mark inherit text colour (currentColor).</td>
<td class="n">Hard-code a colour inside the SVG; it limits reuse.</td>
</tr>
<tr>
<td class="y">Use the stroke variant below 24 px display size.</td>
<td class="n">Scale the standard mark below 20 px — trails merge.</td>
</tr>
<tr>
<td class="y">Pair with the wordmark "hermes.x" when context is unclear.</td>
<td class="n">Apply to a product with a different protocol or trust model.</td>
</tr>
</tbody>
</table>
</section>
<section>
<h2>Palette</h2>
<p class="lede">Hermes rides on a sky-blue primary — fast, airborne, legible
on both dark and light. The deep navy background is the natural pairing;
the dim tones are for muted type.</p>
<div class="swatches">
<div class="sw"><div class="block" style="background:#0b0e14"></div><div class="meta"><b>bg</b><code>#0b0e14</code> — primary surface</div></div>
<div class="sw"><div class="block" style="background:#141a24"></div><div class="meta"><b>panel</b><code>#141a24</code> — cards, sidebar</div></div>
<div class="sw"><div class="block" style="background:#38BDF8"></div><div class="meta"><b>sky</b><code>#38BDF8</code> — primary accent (mark)</div></div>
<div class="sw"><div class="block" style="background:#60A5FA"></div><div class="meta"><b>sky-soft</b><code>#60A5FA</code> — hover, secondary</div></div>
<div class="sw"><div class="block" style="background:#0EA5E9"></div><div class="meta"><b>sky-deep</b><code>#0EA5E9</code> — pressed / active</div></div>
<div class="sw"><div class="block" style="background:#FF6D00"></div><div class="meta"><b>orange</b><code>#FF6D00</code> — alternate accent</div></div>
<div class="sw"><div class="block" style="background:#FF8C1A"></div><div class="meta"><b>orange-light</b><code>#FF8C1A</code> — hover, secondary</div></div>
<div class="sw"><div class="block" style="background:#E65100"></div><div class="meta"><b>orange-deep</b><code>#E65100</code> — pressed / active</div></div>
<div class="sw"><div class="block" style="background:#e7eaf1"></div><div class="meta"><b>ink</b><code>#e7eaf1</code> — body text</div></div>
<div class="sw"><div class="block" style="background:#8b98a9"></div><div class="meta"><b>mut</b><code>#8b98a9</code> — muted text</div></div>
<div class="sw"><div class="block" style="background:#ffc75f"></div><div class="meta"><b>warn</b><code>#ffc75f</code> — warnings, unread accents</div></div>
</div>
</section>
<section>
<h2>Voice</h2>
<ul class="steps">
<li><b>Blunt about limits.</b>
<span>"Relays still see recipient and timing" is the tone, not "fully
anonymous messaging". Say what the design does and does not solve.</span></li>
<li><b>No evangelism about Nostr.</b>
<span>Nostr is the transport, not the product. Mention it where it
matters (relays, pubkeys, NIP-17) and don't elsewhere.</span></li>
<li><b>"Message by name" is the one-line pitch.</b>
<span>Everything else is supporting detail.</span></li>
<li><b>Lowercase for product nouns in body copy.</b>
<span>hermes.x, messenger, panel. Capital Hermes for titles and the first
mention per page.</span></li>
</ul>
</section>
<section>
<h2>Attribution + reuse</h2>
<p>Mark + palette are MPL-2.0 along with the rest of the Hermes client source.
Use for reviews, press, interoperability docs and the client's own chrome.
Don't apply to products that aren't actually Hermes (forks with different
protocols or trust models included).</p>
</section>
</div>
<footer>
<div>hermes.x — a <a href="https://silentmode.st/">Silent Mode</a> project.</div>
<div class="fine">Named for the messenger of the gods. Ermis in modern Greek. Spelled Hermes in English.</div>
</footer>
</body>
</html>

View file

@ -1,172 +1,172 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hermes.x — message anyone by their on-chain name</title>
<meta name="description" content="Hermes: end-to-end encrypted messaging where your Bitcoin Cash name is your identity. NIP-17 sealed DMs over Nostr. No email, no phone number, no company account.">
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml">
<link rel="mask-icon" href="assets/favicon.svg" color="#38BDF8">
<meta property="og:type" content="website">
<meta property="og:site_name" content="hermes.x">
<meta property="og:title" content="hermes.x — message anyone by their on-chain name">
<meta property="og:description" content="End-to-end encrypted DMs over Nostr, addressed by your on-chain registered name. No email, no phone, no server that can delete your account.">
<meta property="og:url" content="https://hermes.x/">
<meta property="og:image" content="https://hermes.x/assets/mark.webp">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="hermes.x — message anyone by their on-chain name">
<meta name="twitter:description" content="Sealed-sender DMs over Nostr, addressed by on-chain registered name.">
<link rel="stylesheet" href="assets/shared.css">
</head>
<body>
<nav class="topnav">
<a class="brand" href="./">
<img src="assets/mark.webp" alt="Hermes" width="5" height="22" style="display:block">
<span>Hermes<span class="x">.X</span></span>
</a>
<a href="./" class="here">Overview</a>
<a href="messenger/">Messenger</a>
<a href="branding/">Branding</a>
<a href="https://theseus.x/" class="get">Get Theseus</a>
</nav>
<header class="hero">
<div class="mark" aria-hidden="true">
<img src="assets/mark.webp" alt="Hermes" width="3" height="22" style="display:block">
</div>
<h1>Message by name<span class="x">.</span></h1>
<p class="tag">End-to-end encrypted DMs over Nostr, addressed by your
on-chain registered name instead of a public-key string.
Your name owns your key. No email, no phone number, no company, no server
that can delete your account.</p>
<div class="cta">
<a class="btn primary" href="messenger/">Open Messenger</a>
<a class="btn ghost" href="https://theseus.x/">Get Theseus (desktop)</a>
</div>
</header>
<div class="wrap narrow">
<section class="plain">
<h2>What ships today</h2>
<p class="lede">Hermes is live inside the Theseus desktop browser. Web
messenger and Android follow.</p>
<div class="grid">
<div class="card">
<h3>Threaded chat <span class="src ok">live</span></h3>
<p>Contact list on the left (search + unread badges), selected
conversation on the right, inline reply. Enter to send.</p>
</div>
<div class="card">
<h3>Send by name <span class="src ok">live</span></h3>
<p>Type <code class="inline">alice.x</code>. The client resolves the
name on-chain, wraps NIP-17 to the recipient's Nostr key, publishes
to their declared relays. Sealed-sender — the relay never sees who
sent what to whom.</p>
</div>
<div class="card">
<h3>Address book <span class="src ok">live</span></h3>
<p>Local contact list — add names, nicknames, import vCards. Nothing
uploaded; the book is on your device.</p>
</div>
<div class="card">
<h3>History on your device <span class="src ok">live</span></h3>
<p>Messages persist to disk per identity. Close and reopen the panel —
your threads are still there, unread marks included.</p>
</div>
<div class="card">
<h3>Web Messenger <span class="src next">next</span></h3>
<p><a href="messenger/">hermes.x/messenger</a> — browser-side messenger
for anyone without the desktop app. Same crypto, same identity.</p>
</div>
<div class="card">
<h3>Mobile (Ariadne) <span class="src plan">planned</span></h3>
<p>Inside the Ariadne Android app: same wallet seed, same Nostr identity,
zero extra onboarding.</p>
</div>
</div>
</section>
<section>
<h2>The one idea that makes this different</h2>
<p class="lede">Every existing Nostr client has the same wart: identity is
<code class="inline">npub1q8s7f3k…</code> gibberish. The human-name fix —
NIP-05 — resolves <code class="inline">alice@example.com</code> through a
DNS domain and a web server. That is exactly the ICANN + server chain
Bitcoin Cash names exist to escape.</p>
<div class="note">
<b>The Hermes pitch.</b><br>
<code class="inline">alice.x</code> publishes her Nostr pubkey as an
on-chain record. Her name certificate <i>is</i> her verified,
seizure-resistant identity. Message anyone by their
on-chain registered name — no phone number, no email, no ICANN,
no company, no server that can delete your account.
</div>
</section>
<section>
<h2>How it works</h2>
<ol class="steps">
<li><b>Your name owns your key.</b>
<span>Register any name on the chain, publish your Nostr pubkey as an
<code class="inline">np</code> record. The key is derived from the
same wallet seed as the name, so one seed backs both.</span></li>
<li><b>Find people by name.</b>
<span>Type <code class="inline">bob.x</code>. The client resolves
it against the chain, pulls <code class="inline">np</code>
(Nostr pubkey) and <code class="inline">nr</code> (preferred relays).
No directory server involved.</span></li>
<li><b>Send the message sealed.</b>
<span>NIP-17 gift-wrapped DMs — the relay sees neither sender nor
content, only that a wrap addressed to some pubkey exists. Route over
Tor to also hide your IP.</span></li>
<li><b>The name shows, not the pubkey.</b>
<span>Incoming messages reverse-resolve pubkey → name where possible,
so your chat list looks like a normal chat list — not a wall of hex.</span></li>
</ol>
</section>
<section>
<h2>Honest boundaries</h2>
<div class="grid">
<div class="card">
<h3>Messaging, not email</h3>
<p>No Gmail interop — by design. A separate anonymous-mailbox product
covers "sign up on services without leaking a real address".</p>
</div>
<div class="card">
<h3>Metadata resistance is good, not perfect</h3>
<p>NIP-17 hides sender and content from relays. Relays still see the
recipient pubkey, timing, and your connection IP unless you route
over Tor.</p>
</div>
<div class="card">
<h3>Relays are Nostr's soft spot</h3>
<p>A message lives only while some relay keeps it. We run one relay +
write to a handful of public relays for reach and redundancy.</p>
</div>
</div>
</section>
<section class="plain" style="text-align:center">
<h2>Try it</h2>
<p class="lede" style="margin-left:auto;margin-right:auto">
Open the web messenger right here, or install Theseus for the full
experience (persistent history, sidebar entry, keyboard shortcut).</p>
<div class="cta">
<a class="btn primary" href="messenger/">Open Messenger</a>
<a class="btn ghost" href="https://theseus.x/">Get Theseus</a>
<a class="btn ghost" href="branding/">Brand kit</a>
</div>
</section>
</div>
<footer>
<div>hermes.x — a <a href="https://silentmode.st/">Silent Mode</a> project.</div>
<div class="fine">Named for the messenger of the gods. Ermis in modern Greek.</div>
</footer>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hermes.x — message anyone by their on-chain name</title>
<meta name="description" content="Hermes: end-to-end encrypted messaging where your Bitcoin Cash name is your identity. NIP-17 sealed DMs over Nostr. No email, no phone number, no company account.">
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml">
<link rel="mask-icon" href="assets/favicon.svg" color="#38BDF8">
<meta property="og:type" content="website">
<meta property="og:site_name" content="hermes.x">
<meta property="og:title" content="hermes.x — message anyone by their on-chain name">
<meta property="og:description" content="End-to-end encrypted DMs over Nostr, addressed by your on-chain registered name. No email, no phone, no server that can delete your account.">
<meta property="og:url" content="https://hermes.x/">
<meta property="og:image" content="https://hermes.x/assets/mark.svg">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="hermes.x — message anyone by their on-chain name">
<meta name="twitter:description" content="Sealed-sender DMs over Nostr, addressed by on-chain registered name.">
<link rel="stylesheet" href="assets/shared.css">
</head>
<body>
<nav class="topnav">
<a class="brand" href="./">
<img src="assets/mark.svg" alt="Hermes" width="5" height="22" style="display:block">
<span>Hermes<span class="x">.X</span></span>
</a>
<a href="./" class="here">Overview</a>
<a href="messenger/">Messenger</a>
<a href="branding/">Branding</a>
<a href="https://theseus.x/" class="get">Get Theseus</a>
</nav>
<header class="hero">
<div class="mark" aria-hidden="true">
<img src="assets/mark.svg" alt="Hermes" width="3" height="22" style="display:block">
</div>
<h1>Message by name<span class="x">.</span></h1>
<p class="tag">End-to-end encrypted DMs over Nostr, addressed by your
on-chain registered name instead of a public-key string.
Your name owns your key. No email, no phone number, no company, no server
that can delete your account.</p>
<div class="cta">
<a class="btn primary" href="messenger/">Open Messenger</a>
<a class="btn ghost" href="https://theseus.x/">Get Theseus (desktop)</a>
</div>
</header>
<div class="wrap narrow">
<section class="plain">
<h2>What ships today</h2>
<p class="lede">Hermes is live inside the Theseus desktop browser. Web
messenger and Android follow.</p>
<div class="grid">
<div class="card">
<h3>Threaded chat <span class="src ok">live</span></h3>
<p>Contact list on the left (search + unread badges), selected
conversation on the right, inline reply. Enter to send.</p>
</div>
<div class="card">
<h3>Send by name <span class="src ok">live</span></h3>
<p>Type <code class="inline">alice.x</code>. The client resolves the
name on-chain, wraps NIP-17 to the recipient's Nostr key, publishes
to their declared relays. Sealed-sender — the relay never sees who
sent what to whom.</p>
</div>
<div class="card">
<h3>Address book <span class="src ok">live</span></h3>
<p>Local contact list — add names, nicknames, import vCards. Nothing
uploaded; the book is on your device.</p>
</div>
<div class="card">
<h3>History on your device <span class="src ok">live</span></h3>
<p>Messages persist to disk per identity. Close and reopen the panel —
your threads are still there, unread marks included.</p>
</div>
<div class="card">
<h3>Web Messenger <span class="src next">next</span></h3>
<p><a href="messenger/">hermes.x/messenger</a> — browser-side messenger
for anyone without the desktop app. Same crypto, same identity.</p>
</div>
<div class="card">
<h3>Mobile (Ariadne) <span class="src plan">planned</span></h3>
<p>Inside the Ariadne Android app: same wallet seed, same Nostr identity,
zero extra onboarding.</p>
</div>
</div>
</section>
<section>
<h2>The one idea that makes this different</h2>
<p class="lede">Every existing Nostr client has the same wart: identity is
<code class="inline">npub1q8s7f3k…</code> gibberish. The human-name fix —
NIP-05 — resolves <code class="inline">alice@example.com</code> through a
DNS domain and a web server. That is exactly the ICANN + server chain
Bitcoin Cash names exist to escape.</p>
<div class="note">
<b>The Hermes pitch.</b><br>
<code class="inline">alice.x</code> publishes her Nostr pubkey as an
on-chain record. Her name certificate <i>is</i> her verified,
seizure-resistant identity. Message anyone by their
on-chain registered name — no phone number, no email, no ICANN,
no company, no server that can delete your account.
</div>
</section>
<section>
<h2>How it works</h2>
<ol class="steps">
<li><b>Your name owns your key.</b>
<span>Register any name on the chain, publish your Nostr pubkey as an
<code class="inline">np</code> record. The key is derived from the
same wallet seed as the name, so one seed backs both.</span></li>
<li><b>Find people by name.</b>
<span>Type <code class="inline">bob.x</code>. The client resolves
it against the chain, pulls <code class="inline">np</code>
(Nostr pubkey) and <code class="inline">nr</code> (preferred relays).
No directory server involved.</span></li>
<li><b>Send the message sealed.</b>
<span>NIP-17 gift-wrapped DMs — the relay sees neither sender nor
content, only that a wrap addressed to some pubkey exists. Route over
Tor to also hide your IP.</span></li>
<li><b>The name shows, not the pubkey.</b>
<span>Incoming messages reverse-resolve pubkey → name where possible,
so your chat list looks like a normal chat list — not a wall of hex.</span></li>
</ol>
</section>
<section>
<h2>Honest boundaries</h2>
<div class="grid">
<div class="card">
<h3>Messaging, not email</h3>
<p>No Gmail interop — by design. A separate anonymous-mailbox product
covers "sign up on services without leaking a real address".</p>
</div>
<div class="card">
<h3>Metadata resistance is good, not perfect</h3>
<p>NIP-17 hides sender and content from relays. Relays still see the
recipient pubkey, timing, and your connection IP unless you route
over Tor.</p>
</div>
<div class="card">
<h3>Relays are Nostr's soft spot</h3>
<p>A message lives only while some relay keeps it. We run one relay +
write to a handful of public relays for reach and redundancy.</p>
</div>
</div>
</section>
<section class="plain" style="text-align:center">
<h2>Try it</h2>
<p class="lede" style="margin-left:auto;margin-right:auto">
Open the web messenger right here, or install Theseus for the full
experience (persistent history, sidebar entry, keyboard shortcut).</p>
<div class="cta">
<a class="btn primary" href="messenger/">Open Messenger</a>
<a class="btn ghost" href="https://theseus.x/">Get Theseus</a>
<a class="btn ghost" href="branding/">Brand kit</a>
</div>
</section>
</div>
<footer>
<div>hermes.x — a <a href="https://silentmode.st/">Silent Mode</a> project.</div>
<div class="fine">Named for the messenger of the gods. Ermis in modern Greek.</div>
</footer>
</body>
</html>

View file

@ -1,242 +1,242 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hermes.x/messenger — browser messenger (preview)</title>
<meta name="description" content="The web version of Hermes: encrypted DMs over Nostr, addressed by on-chain registered name, running in your browser. Preview of the UI; functional release pending.">
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="../assets/shared.css">
<style>
/* ---- status banner ---- */
.status-banner{
background:rgba(255,199,95,.08);
border:1px solid rgba(255,199,95,.26);
border-radius:12px;
padding:14px 18px;
margin:1.5rem auto;
max-width:780px;
font-size:14px;
color:var(--ink);
display:flex;gap:14px;align-items:flex-start;
}
.status-banner .dot{
width:10px;height:10px;border-radius:50%;background:var(--warn);
margin-top:6px;flex-shrink:0;animation:pulse 2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity:.4 } }
.status-banner b{color:var(--warn)}
/* ---- mockup frame ---- */
.mockup{
background:var(--panel);border:1px solid var(--line2);border-radius:16px;
overflow:hidden;margin:1.5rem auto;max-width:860px;
box-shadow:0 20px 60px rgba(0,0,0,.4);
}
.mockup .window-bar{
display:flex;align-items:center;gap:8px;padding:10px 14px;
background:var(--panel2);border-bottom:1px solid var(--line);
}
.mockup .window-bar .dot{width:11px;height:11px;border-radius:50%;background:#444a56}
.mockup .window-bar .dot:nth-child(1){background:#ff5f57}
.mockup .window-bar .dot:nth-child(2){background:#febc2e}
.mockup .window-bar .dot:nth-child(3){background:#28c840}
.mockup .window-bar .title{color:var(--mut);font-size:12px;margin-left:10px}
.mockup .window-bar .status{margin-left:auto;font-size:11px;color:var(--ok);padding:2px 10px;border-radius:999px;background:rgba(79,209,165,.14)}
.mockup .window-bar .npub{color:var(--mut);font-size:11px;font-family:ui-monospace,monospace;margin-left:12px}
.mockup .npub b{color:var(--sky)}
.mockup .app{display:grid;grid-template-columns:220px 1fr;min-height:420px}
.mockup .side{background:var(--panel2);border-right:1px solid var(--line);display:flex;flex-direction:column}
.mockup .side-head{padding:10px;display:flex;gap:6px;border-bottom:1px solid var(--line)}
.mockup .side-head input{flex:1;background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:6px 10px;color:var(--mut);font-size:12px}
.mockup .side-head button{background:var(--sky);color:#06111a;border:none;border-radius:6px;padding:6px 10px;font-weight:600;cursor:pointer;font-size:13px}
.mockup .contact{display:flex;gap:10px;align-items:center;padding:9px 12px;border-bottom:1px solid rgba(255,255,255,.03);cursor:pointer}
.mockup .contact.active{background:var(--panel3)}
.mockup .contact .avatar{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#2a3a4a,#1a2432);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--mut);font-weight:600;text-transform:uppercase}
.mockup .contact .meta{flex:1;min-width:0}
.mockup .contact .name{font-size:12.5px;color:var(--ink);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mockup .contact .last{font-size:11px;color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mockup .contact .badge{background:var(--sky);color:#06111a;font-size:10px;font-weight:700;padding:1px 7px;border-radius:999px}
.mockup .thread{display:flex;flex-direction:column;background:var(--bg)}
.mockup .thread-head{padding:12px 16px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:12px}
.mockup .thread-head .name{font-weight:600;font-size:13px}
.mockup .thread-head .pk{font-family:ui-monospace,monospace;font-size:10.5px;color:var(--mut)}
.mockup .thread-body{flex:1;padding:14px 16px;display:flex;flex-direction:column;gap:6px}
.mockup .bubble{max-width:70%;padding:7px 11px;border-radius:14px;font-size:13px;line-height:1.4}
.mockup .bubble.in{background:#1d2634;align-self:flex-start;border-bottom-left-radius:4px}
.mockup .bubble.out{background:#0d2a3a;align-self:flex-end;border-bottom-right-radius:4px;color:#d9ecfa}
.mockup .bubble .time{display:block;font-size:10px;color:var(--dim);margin-top:3px}
.mockup .compose{display:flex;gap:8px;padding:10px 14px;border-top:1px solid var(--line);background:var(--panel)}
.mockup .compose input{flex:1;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:9px 11px;color:var(--mut);font-size:13px}
.mockup .compose button{background:var(--sky);color:#06111a;border:none;padding:0 18px;border-radius:8px;font-weight:600;cursor:pointer}
.plan-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media (max-width:700px){.plan-grid{grid-template-columns:1fr}}
.plan{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px}
.plan h3{display:flex;align-items:center;gap:8px}
.plan ul{margin:.5rem 0 0;padding:0 0 0 1.2rem;color:var(--mut);font-size:14px;line-height:1.7}
.mockup-caption{text-align:center;color:var(--dim);font-size:12px;margin-top:-.5rem;margin-bottom:1.5rem}
</style>
</head>
<body>
<nav class="topnav">
<a class="brand" href="../">
<img src="../assets/mark.webp" alt="Hermes" width="5" height="22" style="display:block">
<span>Hermes<span class="x">.X</span></span>
</a>
<a href="../">Overview</a>
<a href="./" class="here">Messenger</a>
<a href="../branding/">Branding</a>
<a href="https://theseus.x/" class="get">Get Theseus</a>
</nav>
<header class="hero">
<div class="mark" style="color:var(--sky); line-height:0; display:inline-flex" aria-hidden="true">
<img src="../assets/mark.webp" alt="Hermes" width="96" height="96" style="display:block">
</div>
<h1>Messenger</h1>
<p class="tag">Hermes in a browser tab — end-to-end encrypted DMs over Nostr,
addressed by on-chain registered name. The desktop client has
the richest flavour; the web messenger meets you where you are.</p>
</header>
<div class="status-banner">
<div class="dot"></div>
<div>
<b>Preview.</b> The desktop messenger inside <a href="https://theseus.x/">Theseus</a>
is live today — threaded chat, persistent history, address book, keyboard
shortcut <code class="inline">Ctrl+Shift+E</code>. The web-only client
(what this page becomes) is in development: BIP-39 → Nostr key derivation,
BCH-name resolution via gateway, NIP-17 wrap/unwrap, WebSocket to relays,
IndexedDB inbox. Everything client-side, nothing uploaded.
</div>
</div>
<div class="mockup" aria-label="Messenger UI preview">
<div class="window-bar">
<div class="dot"></div><div class="dot"></div><div class="dot"></div>
<div class="title">Hermes · hermes.x/messenger</div>
<span class="npub">your npub: <b>npub1ab…z89</b></span>
<span class="status">1/1 relay</span>
</div>
<div class="app">
<aside class="side">
<div class="side-head"><input value="Search contacts…" readonly><button>+</button></div>
<div class="contact">
<div class="avatar">AL</div>
<div class="meta"><div class="name">alice</div><div class="last">you: see you tomorrow</div></div>
</div>
<div class="contact active">
<div class="avatar">BO</div>
<div class="meta"><div class="name">bob</div><div class="last">great, send me the link</div></div>
<div class="badge">2</div>
</div>
<div class="contact">
<div class="avatar">TE</div>
<div class="meta"><div class="name">test</div><div class="last">chipnet is working</div></div>
</div>
</aside>
<section class="thread">
<div class="thread-head">
<div>
<div class="name">bob</div>
<div class="pk">023bf8c1…9ad2</div>
</div>
</div>
<div class="thread-body">
<div class="bubble in">hey, did you register your name yet?<span class="time">11:38</span></div>
<div class="bubble out">just now — bob.x is live<span class="time">11:40</span></div>
<div class="bubble in">nice. send me the hermes.x link<span class="time">11:42</span></div>
<div class="bubble out">https://hermes.x/messenger<span class="time">11:43</span></div>
<div class="bubble in">great, send me the link<span class="time">11:44</span></div>
</div>
<div class="compose">
<input value="Message…" readonly>
<button>Send</button>
</div>
</section>
</div>
</div>
<div class="mockup-caption">Non-interactive preview — live build pending (see roadmap below).</div>
<div class="wrap narrow">
<section>
<h2>Two clients, one identity</h2>
<div class="plan-grid">
<div class="plan">
<h3>Desktop (Theseus) <span class="src ok">live</span></h3>
<ul>
<li>Full crypto in-process (NIP-17, nip44 v2)</li>
<li>Persistent inbox + read-state on disk</li>
<li>Address book (local JSON + vCard import)</li>
<li>Sidebar panel, Ctrl+Shift+E shortcut</li>
<li>Vault-bind: no re-typing the mnemonic</li>
<li>Reverse-resolve pk → on-chain name</li>
</ul>
<div class="cta" style="justify-content:flex-start;margin-top:1.2rem">
<a class="btn primary" href="https://theseus.x/">Get Theseus</a>
</div>
</div>
<div class="plan">
<h3>Web (this page) <span class="src next">building</span></h3>
<ul>
<li>Same crypto, same identity (same wallet seed derivation)</li>
<li>BCH name resolution via public gateway</li>
<li>WebSocket to Nostr relays from the browser</li>
<li>IndexedDB inbox — survives tab close, scoped to this origin</li>
<li>No download, no install, no account</li>
<li>Mnemonic entry only — never stored in plaintext</li>
</ul>
<div class="cta" style="justify-content:flex-start;margin-top:1.2rem">
<a class="btn ghost" href="../">Why Hermes</a>
</div>
</div>
</div>
</section>
<section>
<h2>Why wait for the web client</h2>
<p class="lede">The web messenger isn't ready yet because of three things we
want to get right before shipping:</p>
<ol class="steps">
<li><b>Key handling in a tab.</b>
<span>A wallet mnemonic is the most sensitive bit of data a user has. In
the desktop app it lives in main-process memory; in a tab it has to
survive page reloads without ever landing in plaintext anywhere. We're
wiring a WebCrypto-backed non-extractable key path with the vault PIN
as the gate.</span></li>
<li><b>Resolver gateway.</b>
<span>The desktop talks to a chipnet Fulcrum directly. A browser can't
— it needs an HTTPS gateway that answers <code class="inline">np</code>
/ <code class="inline">nr</code> for a name. The gateway is live
(<code class="inline">navigate.st/api/dns/alice.x</code>) but the
client-side cache + signature-verification path still has an edge case
with lapsed names.</span></li>
<li><b>Relay persistence.</b>
<span>Desktop keeps a long-lived WebSocket to each relay. A tab doesn't,
because browsers throttle background sockets. The web client needs
offline queueing and a well-defined "catch-up" fetch on return —
otherwise you re-open the tab and the inbox feels empty.</span></li>
</ol>
<p>Until the three are solid, <b>install Theseus</b> for the real thing.
The web client will land here when the above are green.</p>
<div class="cta">
<a class="btn primary" href="https://theseus.x/">Get Theseus</a>
<a class="btn ghost" href="../branding/">Brand kit</a>
</div>
</section>
</div>
<footer>
<div>hermes.x — a <a href="https://silentmode.st/">Silent Mode</a> project.</div>
<div class="fine">Preview only. The functional web client is being built.</div>
</footer>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>hermes.x/messenger — browser messenger (preview)</title>
<meta name="description" content="The web version of Hermes: encrypted DMs over Nostr, addressed by on-chain registered name, running in your browser. Preview of the UI; functional release pending.">
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="../assets/shared.css">
<style>
/* ---- status banner ---- */
.status-banner{
background:rgba(255,199,95,.08);
border:1px solid rgba(255,199,95,.26);
border-radius:12px;
padding:14px 18px;
margin:1.5rem auto;
max-width:780px;
font-size:14px;
color:var(--ink);
display:flex;gap:14px;align-items:flex-start;
}
.status-banner .dot{
width:10px;height:10px;border-radius:50%;background:var(--warn);
margin-top:6px;flex-shrink:0;animation:pulse 2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity:.4 } }
.status-banner b{color:var(--warn)}
/* ---- mockup frame ---- */
.mockup{
background:var(--panel);border:1px solid var(--line2);border-radius:16px;
overflow:hidden;margin:1.5rem auto;max-width:860px;
box-shadow:0 20px 60px rgba(0,0,0,.4);
}
.mockup .window-bar{
display:flex;align-items:center;gap:8px;padding:10px 14px;
background:var(--panel2);border-bottom:1px solid var(--line);
}
.mockup .window-bar .dot{width:11px;height:11px;border-radius:50%;background:#444a56}
.mockup .window-bar .dot:nth-child(1){background:#ff5f57}
.mockup .window-bar .dot:nth-child(2){background:#febc2e}
.mockup .window-bar .dot:nth-child(3){background:#28c840}
.mockup .window-bar .title{color:var(--mut);font-size:12px;margin-left:10px}
.mockup .window-bar .status{margin-left:auto;font-size:11px;color:var(--ok);padding:2px 10px;border-radius:999px;background:rgba(79,209,165,.14)}
.mockup .window-bar .npub{color:var(--mut);font-size:11px;font-family:ui-monospace,monospace;margin-left:12px}
.mockup .npub b{color:var(--sky)}
.mockup .app{display:grid;grid-template-columns:220px 1fr;min-height:420px}
.mockup .side{background:var(--panel2);border-right:1px solid var(--line);display:flex;flex-direction:column}
.mockup .side-head{padding:10px;display:flex;gap:6px;border-bottom:1px solid var(--line)}
.mockup .side-head input{flex:1;background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:6px 10px;color:var(--mut);font-size:12px}
.mockup .side-head button{background:var(--sky);color:#06111a;border:none;border-radius:6px;padding:6px 10px;font-weight:600;cursor:pointer;font-size:13px}
.mockup .contact{display:flex;gap:10px;align-items:center;padding:9px 12px;border-bottom:1px solid rgba(255,255,255,.03);cursor:pointer}
.mockup .contact.active{background:var(--panel3)}
.mockup .contact .avatar{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,#2a3a4a,#1a2432);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--mut);font-weight:600;text-transform:uppercase}
.mockup .contact .meta{flex:1;min-width:0}
.mockup .contact .name{font-size:12.5px;color:var(--ink);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mockup .contact .last{font-size:11px;color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mockup .contact .badge{background:var(--sky);color:#06111a;font-size:10px;font-weight:700;padding:1px 7px;border-radius:999px}
.mockup .thread{display:flex;flex-direction:column;background:var(--bg)}
.mockup .thread-head{padding:12px 16px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:12px}
.mockup .thread-head .name{font-weight:600;font-size:13px}
.mockup .thread-head .pk{font-family:ui-monospace,monospace;font-size:10.5px;color:var(--mut)}
.mockup .thread-body{flex:1;padding:14px 16px;display:flex;flex-direction:column;gap:6px}
.mockup .bubble{max-width:70%;padding:7px 11px;border-radius:14px;font-size:13px;line-height:1.4}
.mockup .bubble.in{background:#1d2634;align-self:flex-start;border-bottom-left-radius:4px}
.mockup .bubble.out{background:#0d2a3a;align-self:flex-end;border-bottom-right-radius:4px;color:#d9ecfa}
.mockup .bubble .time{display:block;font-size:10px;color:var(--dim);margin-top:3px}
.mockup .compose{display:flex;gap:8px;padding:10px 14px;border-top:1px solid var(--line);background:var(--panel)}
.mockup .compose input{flex:1;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:9px 11px;color:var(--mut);font-size:13px}
.mockup .compose button{background:var(--sky);color:#06111a;border:none;padding:0 18px;border-radius:8px;font-weight:600;cursor:pointer}
.plan-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media (max-width:700px){.plan-grid{grid-template-columns:1fr}}
.plan{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px}
.plan h3{display:flex;align-items:center;gap:8px}
.plan ul{margin:.5rem 0 0;padding:0 0 0 1.2rem;color:var(--mut);font-size:14px;line-height:1.7}
.mockup-caption{text-align:center;color:var(--dim);font-size:12px;margin-top:-.5rem;margin-bottom:1.5rem}
</style>
</head>
<body>
<nav class="topnav">
<a class="brand" href="../">
<img src="../assets/mark.svg" alt="Hermes" width="5" height="22" style="display:block">
<span>Hermes<span class="x">.X</span></span>
</a>
<a href="../">Overview</a>
<a href="./" class="here">Messenger</a>
<a href="../branding/">Branding</a>
<a href="https://theseus.x/" class="get">Get Theseus</a>
</nav>
<header class="hero">
<div class="mark" style="color:var(--sky); line-height:0; display:inline-flex" aria-hidden="true">
<img src="../assets/mark.svg" alt="Hermes" width="96" height="96" style="display:block">
</div>
<h1>Messenger</h1>
<p class="tag">Hermes in a browser tab — end-to-end encrypted DMs over Nostr,
addressed by on-chain registered name. The desktop client has
the richest flavour; the web messenger meets you where you are.</p>
</header>
<div class="status-banner">
<div class="dot"></div>
<div>
<b>Preview.</b> The desktop messenger inside <a href="https://theseus.x/">Theseus</a>
is live today — threaded chat, persistent history, address book, keyboard
shortcut <code class="inline">Ctrl+Shift+E</code>. The web-only client
(what this page becomes) is in development: BIP-39 → Nostr key derivation,
BCH-name resolution via gateway, NIP-17 wrap/unwrap, WebSocket to relays,
IndexedDB inbox. Everything client-side, nothing uploaded.
</div>
</div>
<div class="mockup" aria-label="Messenger UI preview">
<div class="window-bar">
<div class="dot"></div><div class="dot"></div><div class="dot"></div>
<div class="title">Hermes · hermes.x/messenger</div>
<span class="npub">your npub: <b>npub1ab…z89</b></span>
<span class="status">1/1 relay</span>
</div>
<div class="app">
<aside class="side">
<div class="side-head"><input value="Search contacts…" readonly><button>+</button></div>
<div class="contact">
<div class="avatar">AL</div>
<div class="meta"><div class="name">alice</div><div class="last">you: see you tomorrow</div></div>
</div>
<div class="contact active">
<div class="avatar">BO</div>
<div class="meta"><div class="name">bob</div><div class="last">great, send me the link</div></div>
<div class="badge">2</div>
</div>
<div class="contact">
<div class="avatar">TE</div>
<div class="meta"><div class="name">test</div><div class="last">chipnet is working</div></div>
</div>
</aside>
<section class="thread">
<div class="thread-head">
<div>
<div class="name">bob</div>
<div class="pk">023bf8c1…9ad2</div>
</div>
</div>
<div class="thread-body">
<div class="bubble in">hey, did you register your name yet?<span class="time">11:38</span></div>
<div class="bubble out">just now — bob.x is live<span class="time">11:40</span></div>
<div class="bubble in">nice. send me the hermes.x link<span class="time">11:42</span></div>
<div class="bubble out">https://hermes.x/messenger<span class="time">11:43</span></div>
<div class="bubble in">great, send me the link<span class="time">11:44</span></div>
</div>
<div class="compose">
<input value="Message…" readonly>
<button>Send</button>
</div>
</section>
</div>
</div>
<div class="mockup-caption">Non-interactive preview — live build pending (see roadmap below).</div>
<div class="wrap narrow">
<section>
<h2>Two clients, one identity</h2>
<div class="plan-grid">
<div class="plan">
<h3>Desktop (Theseus) <span class="src ok">live</span></h3>
<ul>
<li>Full crypto in-process (NIP-17, nip44 v2)</li>
<li>Persistent inbox + read-state on disk</li>
<li>Address book (local JSON + vCard import)</li>
<li>Sidebar panel, Ctrl+Shift+E shortcut</li>
<li>Vault-bind: no re-typing the mnemonic</li>
<li>Reverse-resolve pk → on-chain name</li>
</ul>
<div class="cta" style="justify-content:flex-start;margin-top:1.2rem">
<a class="btn primary" href="https://theseus.x/">Get Theseus</a>
</div>
</div>
<div class="plan">
<h3>Web (this page) <span class="src next">building</span></h3>
<ul>
<li>Same crypto, same identity (same wallet seed derivation)</li>
<li>BCH name resolution via public gateway</li>
<li>WebSocket to Nostr relays from the browser</li>
<li>IndexedDB inbox — survives tab close, scoped to this origin</li>
<li>No download, no install, no account</li>
<li>Mnemonic entry only — never stored in plaintext</li>
</ul>
<div class="cta" style="justify-content:flex-start;margin-top:1.2rem">
<a class="btn ghost" href="../">Why Hermes</a>
</div>
</div>
</div>
</section>
<section>
<h2>Why wait for the web client</h2>
<p class="lede">The web messenger isn't ready yet because of three things we
want to get right before shipping:</p>
<ol class="steps">
<li><b>Key handling in a tab.</b>
<span>A wallet mnemonic is the most sensitive bit of data a user has. In
the desktop app it lives in main-process memory; in a tab it has to
survive page reloads without ever landing in plaintext anywhere. We're
wiring a WebCrypto-backed non-extractable key path with the vault PIN
as the gate.</span></li>
<li><b>Resolver gateway.</b>
<span>The desktop talks to a chipnet Fulcrum directly. A browser can't
— it needs an HTTPS gateway that answers <code class="inline">np</code>
/ <code class="inline">nr</code> for a name. The gateway is live
(<code class="inline">navigate.st/api/dns/alice.x</code>) but the
client-side cache + signature-verification path still has an edge case
with lapsed names.</span></li>
<li><b>Relay persistence.</b>
<span>Desktop keeps a long-lived WebSocket to each relay. A tab doesn't,
because browsers throttle background sockets. The web client needs
offline queueing and a well-defined "catch-up" fetch on return —
otherwise you re-open the tab and the inbox feels empty.</span></li>
</ol>
<p>Until the three are solid, <b>install Theseus</b> for the real thing.
The web client will land here when the above are green.</p>
<div class="cta">
<a class="btn primary" href="https://theseus.x/">Get Theseus</a>
<a class="btn ghost" href="../branding/">Brand kit</a>
</div>
</section>
</div>
<footer>
<div>hermes.x — a <a href="https://silentmode.st/">Silent Mode</a> project.</div>
<div class="fine">Preview only. The functional web client is being built.</div>
</footer>
</body>
</html>