diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-20 01:52:01 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-20 01:52:01 +0200 |
| commit | 66abba3d08021b1ff538d6266e4b4e590407bde2 (patch) | |
| tree | 891999648a9654c84e0590b36e0bc95f17d84c98 /packages/meshbay-hub | |
| parent | 6770ed9cd36fe68610b74826520f8c6effc121f6 (diff) | |
| download | meshbay-66abba3d08021b1ff538d6266e4b4e590407bde2.tar.gz | |
feat(hub): show the node's public key in Node → Overview
It was only the Node ID row's fallback, so it vanished the moment the node
announced itself — and was labelled Node ID when it did show. Own row, copy
button, and the profile's instructions no longer name the node dashboard
removed on 2026-09-01.
`.node-key` wraps: 44 base64 characters with no break opportunity pushed a
412px document to 417px, which unpins every sticky element on the page. The
width probe now measures the Overview table with a real key in it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub')
13 files changed, 138 insertions, 17 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index aea1196..47f0b31 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -500,7 +500,9 @@ export default { 'settings.node_key': 'Node verknüpfen', 'settings.node_key_desc': 'Wenn Sie einen MeshBay-Node betreiben, fügen Sie hier ' + 'dessen öffentlichen Ed25519-Schlüssel ein, um ihn mit Ihrem Konto zu ' - + 'verknüpfen. Sie finden diesen Schlüssel im Administrations-Dashboard Ihres Nodes.', + + 'verknüpfen. Führen Sie `meshbay-node status` auf dem Rechner des Nodes aus, um ' + + 'den Schlüssel zu lesen, oder kopieren Sie ihn im Desktop-Client unter ' + + 'Node → Overview.', 'settings.node_key_placeholder': 'Öffentlichen Ed25519-Schlüssel des Nodes einfügen (base64)', 'settings.node_key_submit': 'Node verknüpfen', 'settings.node_key_success': 'Node erfolgreich verknüpft', @@ -1087,6 +1089,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Node-Schlüssel', + 'node.overview_node_key_copy': 'Kopieren', + 'node.overview_node_key_copied': 'Kopiert', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index d9172a6..92a45dd 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -490,7 +490,7 @@ export default { 'settings.groups': 'Group notifications', 'settings.notifications': 'Notifications', 'settings.node_key': 'Link Node', - 'settings.node_key_desc': 'If you operate a MeshBay node, paste its Ed25519 public key here to link it to your account. You can find this key in your node\'s admin dashboard.', + 'settings.node_key_desc': 'If you operate a MeshBay node, paste its Ed25519 public key here to link it to your account. Run `meshbay-node status` on the machine running the node to read the key, or copy it from Node \u2192 Overview in the desktop client.', 'settings.node_key_placeholder': 'Paste node Ed25519 public key (base64)', 'settings.node_key_submit': 'Link Node', 'settings.node_key_success': 'Node linked successfully', @@ -1073,6 +1073,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Node key', + 'node.overview_node_key_copy': 'Copy', + 'node.overview_node_key_copied': 'Copied', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index fc0e639..ae2a405 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -495,8 +495,9 @@ export default { 'settings.notifications': 'Notificaciones', 'settings.node_key': 'Vincular un node', 'settings.node_key_desc': 'Si administra un node MeshBay, pegue aquí su clave ' - + 'pública Ed25519 para vincularlo a su cuenta. Encontrará esa clave en el panel ' - + 'de administración de su node.', + + 'pública Ed25519 para vincularlo a su cuenta. Ejecute `meshbay-node status` en ' + + 'la máquina que aloja el node para leer la clave, o cópiela desde ' + + 'Node → Overview en el cliente de escritorio.', 'settings.node_key_placeholder': 'Pegue la clave pública Ed25519 del node (base64)', 'settings.node_key_submit': 'Vincular el node', 'settings.node_key_success': 'Node vinculado correctamente', @@ -1082,6 +1083,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Clave del node', + 'node.overview_node_key_copy': 'Copiar', + 'node.overview_node_key_copied': 'Copiada', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 5eb8d60..795a06c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -498,8 +498,9 @@ export default { 'settings.notifications': 'Notifications', 'settings.node_key': 'Associer un node', 'settings.node_key_desc': 'Si vous exploitez un node MeshBay, collez ici sa clé ' - + 'publique Ed25519 pour l’associer à votre compte. Vous trouverez cette clé ' - + 'dans le tableau de bord d’administration de votre node.', + + 'publique Ed25519 pour l’associer à votre compte. Lancez `meshbay-node status` ' + + 'sur la machine qui héberge le node pour lire cette clé, ou copiez-la depuis ' + + 'Node → Overview dans le client de bureau.', 'settings.node_key_placeholder': 'Collez la clé publique Ed25519 du node (base64)', 'settings.node_key_submit': 'Associer le node', 'settings.node_key_success': 'Node associé avec succès', @@ -1100,6 +1101,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Clé du node', + 'node.overview_node_key_copy': 'Copier', + 'node.overview_node_key_copied': 'Copiée', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index 6d12cb3..2cb1a31 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -498,8 +498,9 @@ export default { 'settings.notifications': 'Notifiche', 'settings.node_key': 'Collega un node', 'settings.node_key_desc': 'Se gestisce un node MeshBay, incolli qui la sua chiave ' - + 'pubblica Ed25519 per collegarlo al suo account. Trova questa chiave nel ' - + 'pannello di amministrazione del suo node.', + + 'pubblica Ed25519 per collegarlo al suo account. Esegua `meshbay-node status` ' + + 'sulla macchina che ospita il node per leggere la chiave, oppure la copi da ' + + 'Node → Overview nel client desktop.', 'settings.node_key_placeholder': 'Incolli la chiave pubblica Ed25519 del node (base64)', 'settings.node_key_submit': 'Collega il node', 'settings.node_key_success': 'Node collegato correttamente', @@ -1096,6 +1097,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Chiave del node', + 'node.overview_node_key_copy': 'Copia', + 'node.overview_node_key_copied': 'Copiata', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index f4ced88..e26110f 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -493,7 +493,8 @@ export default { 'settings.node_key': 'node を連携', 'settings.node_key_desc': 'MeshBay の node を運営している場合は、その Ed25519 ' + '公開鍵をここに貼り付けて、アカウントに連携してください。' - + 'この鍵は node の管理ダッシュボードで確認できます。', + + 'node を動かしているマシンで `meshbay-node status` を実行するか、' + + 'デスクトップクライアントの Node → Overview からコピーできます。', 'settings.node_key_placeholder': 'node の Ed25519 公開鍵を貼り付け(base64)', 'settings.node_key_submit': 'node を連携', 'settings.node_key_success': 'node を連携しました', @@ -1080,6 +1081,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'node の鍵', + 'node.overview_node_key_copy': 'コピー', + 'node.overview_node_key_copied': 'コピーしました', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 31a2321..ce3cdf9 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -499,8 +499,9 @@ export default { 'settings.notifications': 'Meldingen', 'settings.node_key': 'Node koppelen', 'settings.node_key_desc': 'Als u een MeshBay-node beheert, plak hier dan de ' - + 'openbare Ed25519-sleutel ervan om hem aan uw account te koppelen. U vindt die ' - + 'sleutel in het beheerdersdashboard van uw node.', + + 'openbare Ed25519-sleutel ervan om hem aan uw account te koppelen. Voer ' + + '`meshbay-node status` uit op de machine waarop de node draait om de sleutel te ' + + 'lezen, of kopieer hem via Node → Overview in de desktopclient.', 'settings.node_key_placeholder': 'Plak de openbare Ed25519-sleutel van de node (base64)', 'settings.node_key_submit': 'Node koppelen', 'settings.node_key_success': 'Node gekoppeld', @@ -1098,6 +1099,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Node-sleutel', + 'node.overview_node_key_copy': 'Kopiëren', + 'node.overview_node_key_copied': 'Gekopieerd', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index 463a6a9..7f347da 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -511,8 +511,9 @@ export default { 'settings.notifications': 'Powiadomienia', 'settings.node_key': 'Powiąż node', 'settings.node_key_desc': 'Osoby prowadzące własny node MeshBay mogą wkleić tutaj ' - + 'jego klucz publiczny Ed25519, aby powiązać go ze swoim kontem. Klucz ten znajduje ' - + 'się w panelu administracyjnym node.', + + 'jego klucz publiczny Ed25519, aby powiązać go ze swoim kontem. Aby odczytać ' + + 'klucz, uruchom `meshbay-node status` na maszynie z node lub skopiuj go z ' + + 'Node → Overview w kliencie desktopowym.', 'settings.node_key_placeholder': 'Wklej klucz publiczny Ed25519 node (base64)', 'settings.node_key_submit': 'Powiąż node', 'settings.node_key_success': 'Node został powiązany', @@ -1124,6 +1125,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Klucz node', + 'node.overview_node_key_copy': 'Kopiuj', + 'node.overview_node_key_copied': 'Skopiowano', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index 20822ee..f0f1077 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -497,8 +497,9 @@ export default { 'settings.notifications': 'Notificações', 'settings.node_key': 'Vincular um node', 'settings.node_key_desc': 'Se você opera um node MeshBay, cole aqui a chave pública ' - + 'Ed25519 dele para vinculá-lo à sua conta. Essa chave está no painel de ' - + 'administração do seu node.', + + 'Ed25519 dele para vinculá-lo à sua conta. Execute `meshbay-node status` na ' + + 'máquina que hospeda o node para ler a chave, ou copie-a em ' + + 'Node → Overview no cliente desktop.', 'settings.node_key_placeholder': 'Cole a chave pública Ed25519 do node (base64)', 'settings.node_key_submit': 'Vincular o node', 'settings.node_key_success': 'Node vinculado com sucesso', @@ -1083,6 +1084,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'Chave do node', + 'node.overview_node_key_copy': 'Copiar', + 'node.overview_node_key_copied': 'Copiada', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index b2d7591..be337b7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -486,7 +486,9 @@ export default { 'settings.notifications': '通知', 'settings.node_key': '关联 node', 'settings.node_key_desc': '如果您运营着一个 MeshBay node,请在此粘贴它的 Ed25519 ' - + '公钥,以将其关联到您的账户。该密钥可在您 node 的管理面板中找到。', + + '公钥,以将其关联到您的账户。在运行 node 的机器上执行 ' + + '`meshbay-node status` 可读取该密钥,或在桌面客户端的 ' + + 'Node → Overview 中复制。', 'settings.node_key_placeholder': '粘贴 node 的 Ed25519 公钥(base64)', 'settings.node_key_submit': '关联 node', 'settings.node_key_success': 'node 关联成功', @@ -1068,6 +1070,9 @@ export default { 'node.overview': 'Overview', 'node.overview_version': 'Version', 'node.overview_node_id': 'Node ID', + 'node.overview_node_key': 'node 密钥', + 'node.overview_node_key_copy': '复制', + 'node.overview_node_key_copied': '已复制', 'node.overview_quic_port': 'QUIC port', 'node.overview_hub': 'Hub', 'node.node_roster': 'Node roster', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js index 2edcbab..0b05dad 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js @@ -216,6 +216,7 @@ export function NodePage({ groups, token, username }) { const [nodeRoster, setNodeRoster] = useState(null); const [unlinkBusy, setUnlinkBusy] = useState(false); const [tab, setTab] = useState('overview'); + const [keyCopied, setKeyCopied] = useState(false); const fetchStatus = useCallback(async () => { setStatus('connecting'); @@ -257,6 +258,16 @@ export function NodePage({ groups, token, username }) { } catch {} }, []); + const copyNodeKey = useCallback(async () => { + const pk = nodeInfo && nodeInfo.pk_node_ed25519; + if (!pk) return; + try { + await navigator.clipboard.writeText(pk); + setKeyCopied(true); + setTimeout(() => setKeyCopied(false), 2000); + } catch { /* clipboard blocked -- the key is on screen to copy by hand */ } + }, [nodeInfo]); + const addRoot = useCallback(async (groupId) => { const chosen = await platform.rootPicker.choose(); if (!chosen) return; @@ -725,7 +736,16 @@ export function NodePage({ groups, token, username }) { <tbody> <tr><td>${t('node.overview_version')}</td><td>${nodeInfo.version || '—'}</td></tr> <tr><td>${t('node.overview_node_id')}</td> - <td><code>${nodeInfo.endpoint_hint || nodeInfo.pk_node_ed25519 || '—'}</code></td></tr> + <td><code>${nodeInfo.endpoint_hint || '—'}</code></td></tr> + <tr><td>${t('node.overview_node_key')}</td> + <td> + <code class="node-key">${nodeInfo.pk_node_ed25519 || '—'}</code> + ${nodeInfo.pk_node_ed25519 && html` + <button class="btn btn-small btn-secondary" onClick=${copyNodeKey}> + ${keyCopied ? t('node.overview_node_key_copied') + : t('node.overview_node_key_copy')} + </button>`} + </td></tr> <tr><td>${t('node.overview_quic_port')}</td><td>${nodeInfo.quic_port || '—'}</td></tr> <tr><td>${t('node.overview_hub')}</td><td>${nodeInfo.hub_url || '—'}</td></tr> </tbody> diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index 63f631a..7c36ed7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -3349,6 +3349,12 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } overflow-x: auto; margin-top: 4px; } +/* An Ed25519 key is 44 base64 characters with no break opportunity in them. */ +.node-key { + word-break: break-all; + overflow-wrap: anywhere; + margin-right: 8px; +} /* Audit rows: keep the fixed-shape columns on one line (the IP must never be clipped); only the free-text detail column wraps. */ .node-table-audit td, diff --git a/packages/meshbay-hub/tests/test_node_page_width_measured.py b/packages/meshbay-hub/tests/test_node_page_width_measured.py index ebf07ab..bd6a231 100644 --- a/packages/meshbay-hub/tests/test_node_page_width_measured.py +++ b/packages/meshbay-hub/tests/test_node_page_width_measured.py @@ -41,6 +41,29 @@ AUDIT_ROWS = [ ] +# A real Ed25519 public key as the node reports it: 32 bytes of base64, 44 +# characters with not one break opportunity among them. Shortening it here +# would measure the fixture instead of the page -- the exact mistake that let +# a folder name and then a translated column head push this page sideways. +NODE_KEY = "kP3vQ8mZ2rT7xN1bY6wL4hJ9cF5dS0aG8eR2uI7oK4M=" + + +def _overview_table(): + rows = [ + ("Version", "0.14.0"), + ("Node ID", "203.0.113.7:0"), + ("Node key", f'<code class="node-key">{NODE_KEY}</code>' + '<button class="btn btn-small btn-secondary">Copy</button>'), + ("QUIC port", "0"), + ("Hub", "https://example.invalid"), + ] + body = "".join(f"<tr><td>{k}</td><td>{v}</td></tr>" for k, v in rows) + return f""" + <table class="node-table node-table-overview"> + <tbody>{body}</tbody> + </table>""" + + def _audit_table(): head = "".join(f"<th>{c}</th>" for c in ("Time", "Event", "User", "IP", "Group", "Detail")) @@ -65,6 +88,10 @@ FRAGMENT = textwrap.dedent(f""" <div id="node" class="page-content node-page"> <h2>Node</h2> <div class="node-group"> + <h3 class="settings-heading">Overview</h3> + {_overview_table()} + </div> + <div class="node-group"> <h3 class="settings-heading">Audit</h3> {_audit_table()} </div> @@ -87,6 +114,7 @@ FRAGMENT = textwrap.dedent(f""" WIDTHS = [320, 360, 412, 768, 1024] SELECTORS = ["#node.node-page", "#settings", "#node .node-table-scroll", "#node .node-table-audit", "#node .node-group", + "#node .node-table-overview", "#node .node-key", "#settings .settings-section"] @@ -172,3 +200,29 @@ def test_the_audit_table_still_scrolls_itself_on_a_phone(measured, width): page = _box(measured, width, "#node.node-page") assert page["offRight"] == 0, ( f"{page['offRight']} px of the Node page is off a {width} px screen") + + +@pytest.mark.parametrize("width", [320, 360, 412]) +def test_the_node_key_does_not_widen_the_page_on_a_phone(measured, width): + """The Overview table has no scroller of its own. + + `.node-table-audit` may overflow because `.node-table-scroll` contains it. + Overview is a bare `.node-table` in a `.node-group`, so a cell that refuses + to wrap sets the column's minimum width and pushes the document itself + wider than the screen -- which detaches every `position: sticky` element on + the page from a viewport the reader can no longer see, the navigation bar + included. `.node-key` carries `word-break: break-all` for that reason; + remove it and this fails. + """ + r = measured[str(width)] + assert r["docScrollW"] <= r["viewport"]["w"], ( + f"the document scrolls to {r['docScrollW']} px on a {width} px screen " + "— the node key is pushing the page wider instead of wrapping") + table = _box(measured, width, "#node .node-table-overview") + card = _box(measured, width, "#node .node-group") + assert table["width"] <= card["width"], ( + f"at {width} px the Overview table is {table['width']} px inside a " + f"{card['width']} px card — the key is driving the column") + key = _box(measured, width, "#node .node-key") + assert key["offRight"] == 0, ( + f"{key['offRight']} px of the node key is off a {width} px screen") |