aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/locales
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-18 03:24:55 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-18 03:24:55 +0200
commit768e07046368819b8a8f15c8b21e5a8bbfcdf282 (patch)
treefba4fa5f85e3963b2281004b503be05f552aff2c /packages/meshbay-hub/src/meshbay_hub/static/locales
parente9d5e979fdab9a1cc3c729d602e6f27207b9480c (diff)
downloadmeshbay-768e07046368819b8a8f15c8b21e5a8bbfcdf282.tar.gz
feat: device linking, and signing in to the hub with a device key
Stage C. Identity keys are per node, so a browser and a desktop client are two keys on one account there — and the node refused the second where it accepted the first. Without this, an account created natively could never be opened in a browser without an operator code per node, and "a native client must not prevent web use" would have been dead on arrival. Device linking (node) --------------------- `identities` is keyed by `(user_id, pk_ed25519)` instead of `user_id` alone. The old shape did `INSERT OR REPLACE`, so a second device overwrote the first silently; SQLite cannot change a primary key in place, so the table is rebuilt. Existing pins are carried over — verified against a live roster with 10 of them, nobody re-pairs. A new device files a request bound by `sha256(code ‖ its own keys)`, and a key the node **already pinned** countersigns it. The hub cannot: it has stored no user keys since 2026-08-14, which is what makes this safe to do without an operator in the loop. **The code never reaches the node.** It lists this account's pending requests with their stored hashes; the approver recomputes and keeps the match. A node offering fabricated keys would have to produce a hash over a code it has never seen. Nothing rests on a human comparing digits — that ritual was dropped in 12.1 as "correct, unusable as the default" and must not return by the back door. The design document had the approver look a request up *by* its hash, which is circular: computing it needs the keys being asked about. Corrected in both. Revocation marks rather than deletes, because a deleted row is a key the node would happily pin again — which is the laptop somebody just reported lost. Your last device cannot be revoked: coming back would need an operator's code. Hub — the only change in the whole plan --------------------------------------- `POST /v1/users/auth` signs in with a device Ed25519 key, on the same pattern as `/v1/nodes/auth`, plus `/v1/users/devices` to register, list and retire. New `user_devices` table with an Alembic migration, because `create_all()` is not one. This is **not** the key directory that was H3, and the tests say so: nothing reads it but the hub, no group key is ever wrapped for one, and it is a different key from the per-node identities. What it does cost is metadata — the hub now knows how many devices an account has and when each last signed in. Also `client.minimum` / `client.recommended` in `GET /v1/hub/version`: an installed client meets a newer hub the day the interface ships in a package, and that is cheap now and awkward to retrofit. Browser ------- The `key_changed` refusal becomes `unknown_device` and offers a linking code instead of telling someone to find their operator. The Members panel lists this account's devices here, approves one by code, and retires one. 773 tests pass. `e2e.py` gained a step that links a device end to end against the live deployment — file, list, recompute, countersign, then open the group with the new keys and no code — and it also gained `recv_type`, because a step that assumes the next message is its own answer reads an ack left by the step before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/locales')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/de.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/en.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/es.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/it.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js13
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js13
10 files changed, 130 insertions, 0 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 aaed0d1..5a946f7 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
@@ -92,6 +92,19 @@ export default {
'group.upload': 'Hochladen',
'group.mkdir': 'Neuer Ordner',
'group.mkdir_prompt': 'Name des neuen Ordners:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(nicht verfügbar — Laufwerk getrennt)',
'group.view': 'Ansehen',
'group.delete': 'Löschen',
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 02532d6..fd2d6ec 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
@@ -92,6 +92,19 @@ export default {
'group.upload': 'Upload',
'group.mkdir': 'New folder',
'group.mkdir_prompt': 'Name of the new folder:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(unavailable — the drive is disconnected)',
'group.view': 'View',
'group.delete': 'Delete',
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 49bfc1c..e564164 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
@@ -90,6 +90,19 @@ export default {
'group.upload': 'Subir',
'group.mkdir': 'Nueva carpeta',
'group.mkdir_prompt': 'Nombre de la nueva carpeta:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(no disponible — la unidad está desconectada)',
'group.view': 'Ver',
'group.delete': 'Eliminar',
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 1afc5b1..5848f9d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
@@ -91,6 +91,19 @@ export default {
'group.upload': 'Envoyer',
'group.mkdir': 'Nouveau dossier',
'group.mkdir_prompt': 'Nom du nouveau dossier :',
+ 'device.add_title': 'Ce navigateur n’est pas encore lié à ce nœud',
+ 'device.add_hint': 'Votre compte est connu ici, mais ce navigateur détient une autre clé. Approuvez-le depuis un appareil déjà lié — sans passer par l’opérateur.',
+ 'device.add_btn': 'Obtenir un code de liaison',
+ 'device.add_show': 'Saisissez ce code sur un appareil déjà lié à ce nœud, dans l’heure :',
+ 'device.mine_title': 'Vos appareils sur ce nœud',
+ 'device.mine_hint': 'Chaque navigateur ou client que vous utilisez ici détient sa propre clé. Ils sont distincts de vos appareils sur les autres nœuds.',
+ 'device.mine_empty': 'Aucun appareil lié ici pour le moment.',
+ 'device.this_one': 'celui-ci',
+ 'device.revoke': 'Retirer',
+ 'device.revoke_confirm': 'Retirer cet appareil ? Il perdra l’accès à ce nœud jusqu’à une nouvelle liaison.',
+ 'device.approve_hint': 'Vous liez un nouvel appareil ? Saisissez le code qu’il affiche.',
+ 'device.approve_btn': 'Approuver',
+ 'device.approved': 'Appareil lié.',
'group.root_unavailable': '(indisponible — le disque est déconnecté)',
'group.view': 'Afficher',
'group.delete': 'Supprimer',
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 44b4353..61913e3 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
@@ -91,6 +91,19 @@ export default {
'group.upload': 'Carica',
'group.mkdir': 'Nuova cartella',
'group.mkdir_prompt': 'Nome della nuova cartella:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(non disponibile — l’unità è scollegata)',
'group.view': 'Visualizza',
'group.delete': 'Elimina',
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 33fdee3..aa656ee 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
@@ -89,6 +89,19 @@ export default {
'group.upload': 'アップロード',
'group.mkdir': '新しいフォルダー',
'group.mkdir_prompt': '新しいフォルダーの名前:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(利用不可 — ドライブが切断されています)',
'group.view': '表示',
'group.delete': '削除',
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 aa0385e..dae903f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
@@ -92,6 +92,19 @@ export default {
'group.upload': 'Uploaden',
'group.mkdir': 'Nieuwe map',
'group.mkdir_prompt': 'Naam van de nieuwe map:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(niet beschikbaar — de schijf is losgekoppeld)',
'group.view': 'Bekijken',
'group.delete': 'Verwijderen',
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 e66dd12..65c81ca 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
@@ -95,6 +95,19 @@ export default {
'group.upload': 'Wyślij',
'group.mkdir': 'Nowy folder',
'group.mkdir_prompt': 'Nazwa nowego folderu:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(niedostępne — dysk jest odłączony)',
'group.view': 'Podgląd',
'group.delete': 'Usuń',
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 03de040..729a7fc 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
@@ -92,6 +92,19 @@ export default {
'group.upload': 'Enviar',
'group.mkdir': 'Nova pasta',
'group.mkdir_prompt': 'Nome da nova pasta:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(indisponível — a unidade está desconectada)',
'group.view': 'Visualizar',
'group.delete': 'Excluir',
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 72995dd..0ff5a49 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
@@ -88,6 +88,19 @@ export default {
'group.upload': '上传',
'group.mkdir': '新建文件夹',
'group.mkdir_prompt': '新文件夹的名称:',
+ 'device.add_title': 'This browser is not linked to this node yet',
+ 'device.add_hint': 'Your account is known here, but this browser holds a different key. Approve it from a device already linked — no operator needed.',
+ 'device.add_btn': 'Get a linking code',
+ 'device.add_show': 'Type this code into a device already linked to this node, within the hour:',
+ 'device.mine_title': 'Your devices on this node',
+ 'device.mine_hint': 'Each browser or client you use here holds its own key. They are separate from your devices on other nodes.',
+ 'device.mine_empty': 'No device is linked here yet.',
+ 'device.this_one': 'this one',
+ 'device.revoke': 'Remove',
+ 'device.revoke_confirm': 'Remove this device? It will lose access to this node until it is linked again.',
+ 'device.approve_hint': 'Linking a new device? Enter the code it is showing.',
+ 'device.approve_btn': 'Approve',
+ 'device.approved': 'Device linked.',
'group.root_unavailable': '(不可用 — 驱动器已断开连接)',
'group.view': '查看',
'group.delete': '删除',