diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub')
17 files changed, 80 insertions, 258 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/crypto.js b/packages/meshbay-hub/src/meshbay_hub/static/crypto.js index c239cc8..ce5ec76 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/crypto.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/crypto.js @@ -328,14 +328,6 @@ async function secretDigest(value) { .map((b) => b.toString(16).padStart(2, '0')).join(''); } -function rootAddSubject(path, name, kind, writable, removable) { - return adminSubject({ path, name, kind, writable, removable }); -} - -function groupAttachSubject(name, sharedDir, writable) { - return adminSubject({ name, shared_dir: sharedDir, writable }); -} - function inviteCreateSubject(userId, username) { return adminSubject({ user_id: userId, username }); } @@ -625,7 +617,7 @@ window.MeshBayCrypto = { importGEK, deriveChunkKey, decryptChunkBin, openGroup, sealGroup, unwrapGEK, b64encode, b64decode, - adminTranscript, adminSubject, rootAddSubject, groupAttachSubject, + adminTranscript, adminSubject, inviteCreateSubject, tmdbConfigSubject, handshakeTranscript, handshakeProof, webrtcBinding, challengeTranscript, joinTranscript, verifyNodeSignature, constantTimeEqual, deviceRequestTranscript, deviceAddTranscript, deviceHelloTranscript, diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js index bde218b..29aa7eb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js @@ -22,21 +22,23 @@ export const INVITE_EMAIL_PREF = 'invite_email'; * One component, two modes, because the Create Group wizard and the Settings * page were drifting apart while showing the same thing: * - * mode="live" — a hosted group. Every change is a signed operator op sent - * over MNP, or the loopback API when the node is on this - * machine and there is no live connection. + * mode="live" — a hosted group. What widens the node's sharing — adding + * a directory, its `writable` and `removable` switches — goes + * through the loopback API only, so only on the node's own + * machine. Removing, ejecting and plugging are signed ops + * over MNP, or the loopback API when there is no connection. * mode="local" — the wizard, before the group exists. Changes are held in * an array the caller owns; nothing is persisted until the * group is attached. * - * **Both paths matter and neither is optional.** The operator of a node is not - * necessarily sitting at it: they may be signing in from any browser, and the - * only thing that reaches their node from there is MNP. An earlier version of - * this read its roots exclusively from the loopback API, which resolves to - * "not available" in a browser — so the section rendered for nobody on the - * web, while the controls it replaced had worked there. `mnpRoots` is the - * source whenever a connection exists; the loopback list is the fallback for - * a local node that is not currently connected (a group still scanning, say). + * **The list is shown wherever the operator is.** They may be signing in from + * any browser, and the only thing that reaches their node from there is MNP. + * An earlier version of this read its roots exclusively from the loopback API, + * which resolves to "not available" in a browser — so the section rendered for + * nobody on the web. `mnpRoots` is the source whenever a connection exists; the + * loopback list is the fallback for a local node that is not currently + * connected (a group still scanning, say). From a browser the table is read + * only where it would widen anything (MNP 6.0). * * Props: * roots — the node's current roots: { name, path, writable, @@ -75,8 +77,6 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, <p class=${msg.error ? 'error-msg' : 'settings-hint'} role=${msg.error ? 'alert' : 'status'} style="margin-top:10px">${msg.text}</p>`; - const [pathDraft, setPathDraft] = useState(''); - const [addingByPath, setAddingByPath] = useState(false); // A toggle has to move under the finger, and the answer only comes back // when the node has signed, written node.toml and pushed the new table. @@ -115,12 +115,20 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, // left out otherwise, rather than printing a row of blanks. const hasPaths = displayRoots.some((r) => r.path); - // Which door a change goes through. MNP first: it is the only one that - // exists for an operator on the web, and it is signed, which the loopback - // API is not (it is authorized by being on localhost with the run token). + // Which door a change goes through. + // + // Widening what the node shares — a new directory, `writable`, `removable` — + // only through the loopback API, which exists only on the node's own + // machine. Over MNP these were signed ops, and a + // signature proves that the operator's key signed, not that the operator + // meant it: in a browser that key is driven by code the hub serves. + // + // Narrowing — remove, eject, plug — MNP first, then loopback. const overMnp = !isLocal && transport && transport.connected; const overLoopback = !isLocal && !overMnp && nodeAvail; + const onNodeMachine = !isLocal && nodeAvail; const canEdit = isLocal || overMnp || overLoopback; + const canWiden = isLocal || onNodeMachine; // Deliberately no index refresh after a root change. // @@ -158,9 +166,8 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, ...prev, [rootName]: { ...(prev[rootName] || {}), ...updates }, })); const ok = await run(async () => { - if (overMnp) await transport.updateRoot(groupId, rootName, updates, signFn); - else if (overLoopback) await platform.node.op('updateRoot', { groupId, rootName, updates }); - else throw new Error(t('node.root_no_route')); + if (onNodeMachine) await platform.node.op('updateRoot', { groupId, rootName, updates }); + else throw new Error(t('settings_node.roots_local_only_hint')); }); // Only a failure clears the patch here; a success waits for the node's // own table, so the switch never travels backwards on its way forwards. @@ -169,8 +176,7 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, const next = { ...prev }; delete next[rootName]; return next; }); } - }, [isLocal, localRoots, onLocalRootsChange, overMnp, overLoopback, - transport, groupId, signFn, run]); + }, [isLocal, localRoots, onLocalRootsChange, onNodeMachine, groupId, run]); const doEjectRoot = useCallback((rootName) => run(async () => { if (overMnp) await transport.ejectRoot(groupId, rootName, signFn); @@ -203,10 +209,9 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, }, [isLocal, localRoots, onLocalRootsChange, overMnp, overLoopback, transport, groupId, signFn, run]); - // Adding a root needs a directory that exists on the *node's* filesystem. - // With the node on this machine that is a native folder picker; from any - // other browser the operator has to type the path, because nothing in a web - // page can browse a remote disk. Both end at the same signed op. + // Adding a root: a native folder picker on the node's own machine, and + // nothing anywhere else — a path typed into a page is a path a script in the + // page could have typed. const addRootAtPath = useCallback(async (path, name) => { if (isLocal) { if ((localRoots || []).some(r => r.path === path)) return true; @@ -222,16 +227,12 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, } setIndexProgress(null); return run(async () => { - if (overMnp) { - await transport.addRoot(groupId, path, { name }, signFn); - } else if (overLoopback) { - await platform.node.op('addRoot', { groupId, path, name }); - await platform.node.op('reload'); - await platform.watchIndexProgress(groupId, setIndexProgress); - } else throw new Error(t('node.root_no_route')); + if (!onNodeMachine) throw new Error(t('settings_node.roots_local_only_hint')); + await platform.node.op('addRoot', { groupId, path, name }); + await platform.node.op('reload'); + await platform.watchIndexProgress(groupId, setIndexProgress); }); - }, [isLocal, localRoots, onLocalRootsChange, overMnp, overLoopback, - transport, groupId, signFn, run]); + }, [isLocal, localRoots, onLocalRootsChange, onNodeMachine, groupId, run]); const doPickRoot = useCallback(async () => { const chosen = await platform.rootPicker.choose(); @@ -240,48 +241,23 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, if (ok && !isLocal) say(t('node.root_added')); }, [addRootAtPath, isLocal]); - const doAddByPath = useCallback(async () => { - const path = pathDraft.trim(); - if (!path) return; - // The name is the node's business — it derives the basename and refuses a - // duplicate. Sending one guessed from a string typed here would be a - // second opinion about something already decided in one place. - const ok = await addRootAtPath(path, ''); - if (ok) { setPathDraft(''); setAddingByPath(false); if (!isLocal) say(t('node.root_added')); } - }, [pathDraft, addRootAtPath, isLocal]); - - const addControls = !canEdit ? '' : html` - ${platform.rootPicker.available ? html` - <button class="btn btn-small btn-secondary" style="margin-top:8px" - disabled=${busy} onClick=${doPickRoot}> - <${Icon} name="folder-plus" /> ${t('node.add_root')} - </button> - ` : addingByPath ? html` - <div class="sdt-add-row"> - <input class="sdt-add-input" type="text" value=${pathDraft} - placeholder=${t('node.root_path_placeholder')} - disabled=${busy} - onInput=${(e) => setPathDraft(e.target.value)} - onKeyDown=${(e) => { if (e.key === 'Enter') doAddByPath(); }} /> - <button class="btn btn-small btn-secondary" disabled=${busy || !pathDraft.trim()} - onClick=${doAddByPath}>${t('node.add_root')}</button> - <button class="btn btn-small" disabled=${busy} - onClick=${() => { setAddingByPath(false); setPathDraft(''); }}> - ${t('settings.cancel')}</button> - </div> - <p class="settings-hint">${t('node.root_path_hint')}</p> - ` : html` - <button class="btn btn-small btn-secondary" style="margin-top:8px" - disabled=${busy} onClick=${() => setAddingByPath(true)}> - <${Icon} name="folder-plus" /> ${t('node.add_root')} - </button> - `} + const addControls = !canWiden || !platform.rootPicker.available ? '' : html` + <button class="btn btn-small btn-secondary" style="margin-top:8px" + disabled=${busy} onClick=${doPickRoot}> + <${Icon} name="folder-plus" /> ${t('node.add_root')} + </button> `; + // Said once, under the table, rather than as a tooltip on each switch: the + // switches are not broken, they are somewhere else. + const localOnlyHint = canEdit && !canWiden && html` + <p class="settings-hint" style="margin-top:8px"> + ${t('settings_node.roots_local_only_hint')}</p>`; if (!displayRoots.length) { return html` <div class="shared-directories-table"> <p class="settings-hint">${t('settings_node.shared_directories_hint')}</p> + ${localOnlyHint} ${addControls} ${message} </div> @@ -326,14 +302,15 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, doing the job. */''} ${canEdit && html` <td class="sdt-col-toggle"> - <${ToggleSwitch} checked=${!!r.writable} disabled=${busy || !!r.ejected} + <${ToggleSwitch} checked=${!!r.writable} + disabled=${busy || !!r.ejected || !canWiden} label=${t('node.root_rw')} onChange=${(v) => doUpdateRoot(r.name, { writable: v })} /> </td> `} ${canEdit && !isLocal && html` <td class="sdt-col-toggle"> - <${ToggleSwitch} checked=${!!r.removable} disabled=${busy} + <${ToggleSwitch} checked=${!!r.removable} disabled=${busy || !canWiden} label=${t('node.removable')} onChange=${(v) => doUpdateRoot(r.name, { removable: v })} /> </td> @@ -360,6 +337,7 @@ function SharedDirectoriesTable({ roots, groupId, transport, signFn, `; })} </tbody> </table> + ${localOnlyHint} ${addControls} ${message} ${indexProgress && indexProgress.scanning && html` @@ -426,6 +404,9 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, // Node loopback state (Electron-only) const [nodeDetected, setNodeDetected] = useState(false); + // A node on this machine is not necessarily the one hosting this group, and + // the table's loopback door is only a door to *that* node. + const [nodeHostsGroup, setNodeHostsGroup] = useState(false); const [nodeRoots, setNodeRoots] = useState([]); const [nodeGroupName, setNodeGroupName] = useState(''); const [nodeBusy, setNodeBusy] = useState(false); @@ -464,6 +445,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, const data = await platform.node.op('groups'); const groups = data.groups || []; const ng = groups.find(g => g.id === groupId); + setNodeHostsGroup(Boolean(ng)); if (ng) { setNodeRoots(ng.roots || []); setNodeGroupName(ng.name || ''); @@ -1182,7 +1164,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, groupId=${groupId} transport=${transportRef.current} signFn=${adminSignFn} - nodeDetected=${nodeDetected} + nodeDetected=${nodeDetected && nodeHostsGroup} onRootsChange=${loadNodeInfo} onRefreshIndex=${onRefreshIndex} /> </${CollapsibleSection}> 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 c650f75..c78bb52 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -956,8 +956,6 @@ export default { 'node.root_no_signing_key': 'Kein Signaturschlüssel verfügbar — koppeln Sie dieses Gerät zuerst mit dem Node', 'node.root_no_route': 'Keine Verbindung zum Node — verbinden Sie sich damit oder verwenden Sie die App auf dem Rechner, der ihn hostet', 'node.root_remove_last': 'Eine Gruppe braucht mindestens ein Verzeichnis', - 'node.root_path_hint': 'Der Pfad, wie der Node ihn sieht, auf dem Rechner, der diese Gruppe hostet.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Pfad', 'node.directory': 'Verzeichnis', 'node.root_added': 'Verzeichnis hinzugefügt.', @@ -1079,6 +1077,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Registrieren Sie sich bei TMDB, um einen eigenen API-Schlüssel zu erzeugen.', 'settings_app.tmdb_token_link': 'Schlüssel holen', 'settings_node.roots_offline_hint': 'Nicht mit dem Node verbunden — Änderungen laufen über den lokalen Node und greifen beim nächsten Neuladen.', + 'settings_node.roots_local_only_hint': 'Ein Verzeichnis hinzufügen sowie Lesen/Schreiben oder Wechselmedium umschalten geht nur auf dem Rechner, auf dem der Node läuft — in der Desktop-Anwendung oder mit meshbay-node root.', 'settings_node.directories_title': 'App-Verzeichnisse', 'settings_node.directories_hint': 'Freigegebene Ordner und welchen davon die Videos-, Musik- und Fotos-Apps als eigene(n) Einstiegspunkt(e) nutzen.', @@ -1251,7 +1250,6 @@ export default { 'settings.browser_access_off_in_browser': 'Der Browserzugang ist für dieses Konto ausgeschaltet. Er wird in der MeshBay-Desktopanwendung eingeschaltet, die diesen Browser auch für sich selbst freigeben kann.', 'group.browser_access_off': 'Der Browserzugang ist für dieses Konto ausgeschaltet. Schalten Sie ihn in der MeshBay-Desktopanwendung (Profil) ein oder geben Sie diesen Browser dort frei.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'Den Ordner {path} mit den Mitgliedern einer auf diesem Computer gehosteten Gruppe teilen? Er wurde nicht in der Ordnerauswahl gewählt.', 'native.node_account_confirm': 'Der Node auf diesem Computer ist für {current} eingerichtet. Stattdessen für {next} einrichten? Er stellt dann die Gruppen, die er für {current} hostet, nicht mehr bereit.', 'native.browser_access_confirm': 'Die Anmeldung über einen Browser erlauben? Die Anwendung legt Ihre Identität auf jedem genutzten Node ab, so versiegelt, dass nur Ihre Passphrase zusammen mit Ihrem Hub-Konto sie öffnen kann.', 'native.declined': 'Abgebrochen — nichts wurde geändert.', 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 b4e4adf..f5879b3 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -778,6 +778,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Sign up on TMDB to generate your own API key.', 'settings_app.tmdb_token_link': 'Get a key', 'settings_node.roots_offline_hint': 'Not connected to the node — changes go through the local node instead, and take effect on its next reload.', + 'settings_node.roots_local_only_hint': 'Adding a directory, and switching read-write or removable, are done on the machine running the node — in the desktop application, or with meshbay-node root.', 'settings_node.directories_title': 'App directories', 'settings_node.directories_hint': 'Which shared folders the Videos, Music and Photos apps use as their entry point(s).', @@ -1030,8 +1031,6 @@ export default { 'node.root_no_signing_key': 'No signing key available — pair this device with the node first', 'node.root_no_route': 'No route to the node — connect to it, or use the app on the machine hosting it', 'node.root_remove_last': 'A group needs at least one directory', - 'node.root_path_hint': 'The path as the node sees it, on the machine hosting this group.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Path', 'node.root_added': 'Directory added.', 'node.root_removed': 'Directory removed. Restart recommended to update the index.', @@ -1232,7 +1231,6 @@ export default { 'settings.browser_access_off_in_browser': 'Browser access is off for this account. It is turned on in the MeshBay desktop application, which can also approve this browser for itself.', 'group.browser_access_off': 'Browser access is off for this account. Turn it on in the MeshBay desktop application (Profile), or approve this browser from it.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'Share the folder {path} with the members of a group hosted on this computer? It was not chosen in the folder picker.', 'native.node_account_confirm': 'The node on this computer is set up for {current}. Set it up for {next} instead? It will stop serving the groups it hosts for {current}.', 'native.browser_access_confirm': 'Allow signing in from a browser? The application will leave your identity on every node you use, sealed so that only your passphrase together with your hub account can open it.', 'native.declined': 'Cancelled — nothing was changed.', 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 7422b13..a35aa96 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -950,8 +950,6 @@ export default { 'node.root_no_signing_key': 'No hay clave de firma disponible: empareja primero este dispositivo con el nodo', 'node.root_no_route': 'No hay ruta al nodo: conéctate a él o usa la aplicación en la máquina que lo aloja', 'node.root_remove_last': 'Un grupo necesita al menos un directorio', - 'node.root_path_hint': 'La ruta tal como la ve el nodo, en la máquina que aloja este grupo.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Ruta', 'node.directory': 'Directorio', 'node.root_added': 'Directorio añadido.', @@ -1073,6 +1071,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Regístrate en TMDB para generar tu propia clave de API.', 'settings_app.tmdb_token_link': 'Obtener una clave', 'settings_node.roots_offline_hint': 'Sin conexión con el nodo: los cambios pasan por el nodo local y se aplican en su próxima recarga.', + 'settings_node.roots_local_only_hint': 'Añadir una carpeta y cambiar lectura-escritura o extraíble se hace en la máquina del nodo: en la aplicación de escritorio o con meshbay-node root.', 'settings_node.directories_title': 'Directorios de apps', 'settings_node.directories_hint': 'Carpetas compartidas, y cuál de ellas usan las apps de Vídeos, Música y Fotos como su(s) propio(s) punto(s) de entrada.', @@ -1245,7 +1244,6 @@ export default { 'settings.browser_access_off_in_browser': 'El acceso desde el navegador está desactivado para esta cuenta. Se activa en la aplicación de escritorio de MeshBay, que también puede aprobar este navegador por sí mismo.', 'group.browser_access_off': 'El acceso desde el navegador está desactivado para esta cuenta. Actívelo en la aplicación de escritorio de MeshBay (Perfil) o apruebe este navegador desde ella.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': '¿Compartir la carpeta {path} con los miembros de un grupo alojado en este ordenador? No se eligió en el selector de carpetas.', 'native.node_account_confirm': 'El node de este ordenador está configurado para {current}. ¿Configurarlo para {next} en su lugar? Dejará de servir los grupos que aloja para {current}.', 'native.browser_access_confirm': '¿Permitir el acceso desde un navegador? La aplicación dejará su identidad en cada node que use, sellada de modo que solo su frase de contraseña, junto con su cuenta del hub, pueda abrirla.', 'native.declined': 'Cancelado: no se ha cambiado nada.', 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 d7d9228..2e823cd 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -959,8 +959,6 @@ export default { 'node.root_no_signing_key': 'Aucune clé de signature disponible — appairez d\'abord cet appareil avec le nœud', 'node.root_no_route': 'Aucune route vers le nœud — connectez-vous à lui, ou utilisez l\'application sur la machine qui l\'héberge', 'node.root_remove_last': 'Un groupe a besoin d\'au moins un répertoire', - 'node.root_path_hint': 'Le chemin tel que le nœud le voit, sur la machine qui héberge ce groupe.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Chemin', 'node.root_added': 'Répertoire ajouté.', 'node.root_remove_confirm': 'Retirer « {name} » de ce groupe ?', @@ -1091,6 +1089,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Créez un compte TMDB pour générer votre propre clé d\'API.', 'settings_app.tmdb_token_link': 'Obtenir une clé', 'settings_node.roots_offline_hint': 'Non connecté au nœud — les changements passent par le nœud local et prennent effet à son prochain rechargement.', + 'settings_node.roots_local_only_hint': 'L\'ajout d\'un dossier et le passage en lecture-écriture ou amovible se font sur la machine du nœud — dans l\'application de bureau, ou avec meshbay-node root.', 'settings_node.directories_title': 'Répertoires des applications', 'settings_node.directories_hint': 'Quel(s) dossier(s) partagés les applications Vidéos, Musique et Photos utilisent comme leur(s) propre(s) point(s) d\'entrée.', @@ -1260,7 +1259,6 @@ export default { 'settings.browser_access_off_in_browser': 'L\'accès depuis un navigateur est désactivé pour ce compte. Il s\'active dans l\'application de bureau MeshBay, qui peut aussi approuver ce navigateur pour lui-même.', 'group.browser_access_off': 'L\'accès depuis un navigateur est désactivé pour ce compte. Activez-le dans l\'application de bureau MeshBay (Profil), ou approuvez ce navigateur depuis celle-ci.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'Partager le dossier {path} avec les membres d\'un groupe hébergé sur cet ordinateur ? Il n\'a pas été choisi dans le sélecteur de dossier.', 'native.node_account_confirm': 'Le node de cet ordinateur est configuré pour {current}. Le configurer pour {next} à la place ? Il cessera de servir les groupes qu\'il héberge pour {current}.', 'native.browser_access_confirm': 'Autoriser la connexion depuis un navigateur ? L\'application déposera votre identité sur chaque node que vous utilisez, scellée de sorte que seule votre phrase secrète, avec votre compte sur le hub, puisse l\'ouvrir.', 'native.declined': 'Annulé — rien n\'a été modifié.', 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 5052378..84879e8 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -958,8 +958,6 @@ export default { 'node.root_no_signing_key': 'Nessuna chiave di firma disponibile: associa prima questo dispositivo al nodo', 'node.root_no_route': 'Nessuna via verso il nodo: connettiti a esso oppure usa l\'applicazione sulla macchina che lo ospita', 'node.root_remove_last': 'Un gruppo ha bisogno di almeno una directory', - 'node.root_path_hint': 'Il percorso come lo vede il nodo, sulla macchina che ospita questo gruppo.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Percorso', 'node.directory': 'Directory', 'node.root_added': 'Directory aggiunta.', @@ -1087,6 +1085,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Registrati su TMDB per generare la tua chiave API.', 'settings_app.tmdb_token_link': 'Ottieni una chiave', 'settings_node.roots_offline_hint': 'Non connesso al nodo: le modifiche passano dal nodo locale e hanno effetto al successivo ricaricamento.', + 'settings_node.roots_local_only_hint': 'L\'aggiunta di una cartella e il passaggio a lettura-scrittura o rimovibile si fanno sulla macchina del nodo: nell\'applicazione desktop o con meshbay-node root.', 'settings_node.directories_title': 'Directory delle app', 'settings_node.directories_hint': 'Cartelle condivise, e quale di esse le app Video, Musica e Foto usano come proprio/i punto/i di ingresso.', @@ -1259,7 +1258,6 @@ export default { 'settings.browser_access_off_in_browser': 'L\'accesso dal browser è disattivato per questo account. Si attiva nell\'applicazione desktop di MeshBay, che può anche approvare questo browser per sé.', 'group.browser_access_off': 'L\'accesso dal browser è disattivato per questo account. Attivalo nell\'applicazione desktop di MeshBay (Profilo) o approva questo browser da lì.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'Condividere la cartella {path} con i membri di un gruppo ospitato su questo computer? Non è stata scelta nel selettore di cartelle.', 'native.node_account_confirm': 'Il node di questo computer è configurato per {current}. Configurarlo invece per {next}? Smetterà di servire i gruppi che ospita per {current}.', 'native.browser_access_confirm': 'Consentire l\'accesso da un browser? L\'applicazione lascerà la tua identità su ogni node che usi, sigillata in modo che solo la tua passphrase, insieme al tuo account sull\'hub, possa aprirla.', 'native.declined': 'Annullato: non è stato modificato nulla.', 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 47a968c..9167efe 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -944,8 +944,6 @@ export default { 'node.root_no_signing_key': '署名鍵がありません — 先にこの端末をノードとペアリングしてください', 'node.root_no_route': 'ノードへの経路がありません — 接続するか、ノードを動かしているマシンでアプリを使ってください', 'node.root_remove_last': 'グループには少なくとも 1 つのディレクトリが必要です', - 'node.root_path_hint': 'このグループをホストしているマシン上で、ノードから見たパスです。', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'パス', 'node.directory': 'ディレクトリ', 'node.root_added': 'ディレクトリを追加しました。', @@ -1071,6 +1069,7 @@ export default { 'settings_app.tmdb_token_prompt': 'TMDB に登録して、自分の API キーを発行してください。', 'settings_app.tmdb_token_link': 'キーを取得', 'settings_node.roots_offline_hint': 'ノードに接続していません — 変更はローカルノード経由で行われ、次回の再読み込みで反映されます。', + 'settings_node.roots_local_only_hint': 'ディレクトリの追加と、読み書き・リムーバブルの切り替えは、ノードが動いているマシンで行います — デスクトップアプリ、または meshbay-node root で。', 'settings_node.directories_title': 'アプリのディレクトリ', 'settings_node.directories_hint': '共有フォルダと、動画・音楽・写真の各アプリがそれぞれの起点として使用するフォルダです。', @@ -1243,7 +1242,6 @@ export default { 'settings.browser_access_off_in_browser': 'このアカウントではブラウザーからのアクセスがオフです。MeshBay デスクトップアプリでオンにできます。アプリからこのブラウザーだけを承認することもできます。', 'group.browser_access_off': 'このアカウントではブラウザーからのアクセスがオフです。MeshBay デスクトップアプリ(プロフィール)でオンにするか、アプリからこのブラウザーを承認してください。', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'このコンピューターでホストしているグループのメンバーとフォルダー {path} を共有しますか?このフォルダーはフォルダー選択画面で選ばれたものではありません。', 'native.node_account_confirm': 'このコンピューターの node は {current} 用に設定されています。代わりに {next} 用に設定しますか?{current} のためにホストしているグループは提供されなくなります。', 'native.browser_access_confirm': 'ブラウザーからのサインインを許可しますか?アプリは、利用中のすべての node にあなたの ID を残します。これは、パスフレーズと hub のアカウントの両方がそろった場合にのみ開けるよう封印されます。', 'native.declined': 'キャンセルしました。何も変更されていません。', 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 eaad700..4845c03 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -960,8 +960,6 @@ export default { 'node.root_no_signing_key': 'Geen ondertekeningssleutel beschikbaar — koppel dit apparaat eerst aan de node', 'node.root_no_route': 'Geen route naar de node — maak verbinding, of gebruik de app op de machine die hem host', 'node.root_remove_last': 'Een groep heeft minstens één map nodig', - 'node.root_path_hint': 'Het pad zoals de node het ziet, op de machine die deze groep host.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Pad', 'node.directory': 'Map', 'node.root_added': 'Map toegevoegd.', @@ -1089,6 +1087,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Meld u aan bij TMDB om uw eigen API-sleutel te maken.', 'settings_app.tmdb_token_link': 'Sleutel ophalen', 'settings_node.roots_offline_hint': 'Niet verbonden met de node — wijzigingen gaan via de lokale node en worden bij de volgende herlaadbeurt actief.', + 'settings_node.roots_local_only_hint': 'Een map toevoegen en lezen-schrijven of verwisselbaar omzetten gebeurt op de machine van de node — in de desktopapplicatie of met meshbay-node root.', 'settings_node.directories_title': 'App-mappen', 'settings_node.directories_hint': 'Gedeelde mappen, en welke daarvan de Video\'s-, Muziek- en Foto\'s-apps als eigen startpunt(en) gebruiken.', @@ -1261,7 +1260,6 @@ export default { 'settings.browser_access_off_in_browser': 'Browsertoegang staat uit voor dit account. Die wordt aangezet in de MeshBay-desktoptoepassing, die deze browser ook voor zichzelf kan goedkeuren.', 'group.browser_access_off': 'Browsertoegang staat uit voor dit account. Zet die aan in de MeshBay-desktoptoepassing (Profiel), of keur deze browser daar goed.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'De map {path} delen met de leden van een groep die op deze computer wordt gehost? Hij is niet gekozen in de mapkiezer.', 'native.node_account_confirm': 'De node op deze computer is ingesteld voor {current}. In plaats daarvan instellen voor {next}? Hij stopt dan met het aanbieden van de groepen die hij voor {current} host.', 'native.browser_access_confirm': 'Aanmelden vanuit een browser toestaan? De toepassing laat je identiteit achter op elke node die je gebruikt, zo verzegeld dat alleen je wachtzin samen met je hub-account haar kan openen.', 'native.declined': 'Geannuleerd — er is niets gewijzigd.', 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 2635fb0..e3f21d3 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -982,8 +982,6 @@ export default { 'node.root_no_signing_key': 'Brak klucza podpisu — najpierw sparuj to urządzenie z węzłem', 'node.root_no_route': 'Brak połączenia z węzłem — połącz się z nim albo użyj aplikacji na komputerze, który go hostuje', 'node.root_remove_last': 'Grupa wymaga co najmniej jednego katalogu', - 'node.root_path_hint': 'Ścieżka widziana przez węzeł, na komputerze hostującym tę grupę.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Ścieżka', 'node.directory': 'Katalog', 'node.root_added': 'Katalog dodany.', @@ -1115,6 +1113,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Zarejestruj się w TMDB, aby wygenerować własny klucz API.', 'settings_app.tmdb_token_link': 'Pobierz klucz', 'settings_node.roots_offline_hint': 'Brak połączenia z węzłem — zmiany przechodzą przez węzeł lokalny i zaczną działać po jego następnym przeładowaniu.', + 'settings_node.roots_local_only_hint': 'Dodanie katalogu oraz przełączenie odczytu-zapisu lub nośnika wymiennego odbywa się na komputerze węzła — w aplikacji desktopowej lub poleceniem meshbay-node root.', 'settings_node.directories_title': 'Katalogi aplikacji', 'settings_node.directories_hint': 'Katalogi udostępnione oraz to, który z nich aplikacje Wideo, Muzyka i Zdjęcia traktują jako własny punkt (punkty) wejścia.', @@ -1287,7 +1286,6 @@ export default { 'settings.browser_access_off_in_browser': 'Dostęp z przeglądarki jest wyłączony dla tego konta. Włącza się go w aplikacji desktopowej MeshBay, która może też zatwierdzić tę przeglądarkę dla niej samej.', 'group.browser_access_off': 'Dostęp z przeglądarki jest wyłączony dla tego konta. Włącz go w aplikacji desktopowej MeshBay (Profil) albo zatwierdź tę przeglądarkę w tej aplikacji.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'Udostępnić folder {path} członkom grupy hostowanej na tym komputerze? Nie został wybrany w oknie wyboru folderu.', 'native.node_account_confirm': 'Node na tym komputerze jest skonfigurowany dla {current}. Skonfigurować go zamiast tego dla {next}? Przestanie obsługiwać grupy, które hostuje dla {current}.', 'native.browser_access_confirm': 'Zezwolić na logowanie z przeglądarki? Aplikacja pozostawi Twoją tożsamość na każdym używanym node, zapieczętowaną tak, by otworzyć ją mogło tylko Twoje hasło wraz z kontem na hubie.', 'native.declined': 'Anulowano — nic nie zostało zmienione.', 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 207c1b7..00510c6 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 @@ -951,8 +951,6 @@ export default { 'node.root_no_signing_key': 'Nenhuma chave de assinatura disponível — pareie este dispositivo com o nó primeiro', 'node.root_no_route': 'Sem rota até o nó — conecte-se a ele ou use o aplicativo na máquina que o hospeda', 'node.root_remove_last': 'Um grupo precisa de pelo menos um diretório', - 'node.root_path_hint': 'O caminho como o nó o vê, na máquina que hospeda este grupo.', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': 'Caminho', 'node.directory': 'Diretório', 'node.root_added': 'Diretório adicionado.', @@ -1074,6 +1072,7 @@ export default { 'settings_app.tmdb_token_prompt': 'Cadastre-se no TMDB para gerar sua própria chave de API.', 'settings_app.tmdb_token_link': 'Obter uma chave', 'settings_node.roots_offline_hint': 'Sem conexão com o nó — as alterações passam pelo nó local e entram em vigor no próximo recarregamento.', + 'settings_node.roots_local_only_hint': 'Adicionar uma pasta e alternar leitura-gravação ou removível são feitos na máquina do nó — no aplicativo de desktop ou com meshbay-node root.', 'settings_node.directories_title': 'Diretórios de apps', 'settings_node.directories_hint': 'Pastas compartilhadas, e qual delas os apps Vídeos, Música e Fotos tratam como seu(s) próprio(s) ponto(s) de entrada.', @@ -1246,7 +1245,6 @@ export default { 'settings.browser_access_off_in_browser': 'O acesso pelo navegador está desativado para esta conta. Ele é ativado no aplicativo de desktop do MeshBay, que também pode aprovar este navegador para si.', 'group.browser_access_off': 'O acesso pelo navegador está desativado para esta conta. Ative-o no aplicativo de desktop do MeshBay (Perfil) ou aprove este navegador por ele.', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': 'Compartilhar a pasta {path} com os membros de um grupo hospedado neste computador? Ela não foi escolhida no seletor de pastas.', 'native.node_account_confirm': 'O node deste computador está configurado para {current}. Configurá-lo para {next} em vez disso? Ele deixará de servir os grupos que hospeda para {current}.', 'native.browser_access_confirm': 'Permitir o acesso por um navegador? O aplicativo deixará sua identidade em cada node que você usa, selada de forma que apenas sua frase secreta, junto com sua conta no hub, possa abri-la.', 'native.declined': 'Cancelado — nada foi alterado.', 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 3ea8699..51a6572 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 @@ -932,8 +932,6 @@ export default { 'node.root_no_signing_key': '没有可用的签名密钥 — 请先将本设备与节点配对', 'node.root_no_route': '无法连接到节点 — 请先连接,或在运行该节点的机器上使用应用', 'node.root_remove_last': '每个群组至少需要一个目录', - 'node.root_path_hint': '托管该群组的机器上,节点所看到的路径。', - 'node.root_path_placeholder': '/home/user/Media', 'node.root_path': '路径', 'node.directory': '目录', 'node.root_added': '目录已添加。', @@ -1059,6 +1057,7 @@ export default { 'settings_app.tmdb_token_prompt': '在 TMDB 注册以生成你自己的 API 密钥。', 'settings_app.tmdb_token_link': '获取密钥', 'settings_node.roots_offline_hint': '未连接到节点 — 变更将通过本地节点进行,并在其下次重新加载时生效。', + 'settings_node.roots_local_only_hint': '添加目录以及切换读写或可移除,只能在运行节点的机器上进行 — 在桌面应用中,或使用 meshbay-node root。', 'settings_node.directories_title': '应用目录', 'settings_node.directories_hint': '共享文件夹,以及“视频”“音乐”和“照片”应用各自使用哪个(些)作为入口。', @@ -1232,7 +1231,6 @@ export default { 'settings.browser_access_off_in_browser': '此账户已关闭浏览器访问。可在 MeshBay 桌面应用中开启,该应用也可以单独批准此浏览器。', 'group.browser_access_off': '此账户已关闭浏览器访问。请在 MeshBay 桌面应用(个人资料)中开启,或从该应用批准此浏览器。', // Worded by the desktop main process for its own dialogs (main.js). - 'native.folder_confirm': '与这台电脑上托管的群组成员共享文件夹 {path}?该文件夹不是在文件夹选择器中选择的。', 'native.node_account_confirm': '这台电脑上的 node 已为 {current} 设置。改为为 {next} 设置吗?它将不再为 {current} 提供其托管的群组。', 'native.browser_access_confirm': '允许从浏览器登录吗?应用会在您使用的每个 node 上留下您的身份,并加以封存,只有您的密码短语配合您的 hub 账户才能打开。', 'native.declined': '已取消,未做任何更改。', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index fc91596..23013a4 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -3346,13 +3346,6 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.sdt-add-row { display: flex; gap: 6px; align-items: center; margin-top: 8px; } -.sdt-add-input { - flex: 1 1 auto; min-width: 0; padding: 5px 8px; - border: 1px solid var(--border); border-radius: 4px; - background: var(--bg-surface); color: var(--text); - font-family: inherit; font-size: 0.9em; -} .sdt-col-toggle { width: 90px; text-align: center; } .sdt-col-toggle th { text-align: center; } .sdt-col-toggle .toggle-switch { justify-content: center; } diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js b/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js index 1a5a4c0..63cb644 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js @@ -241,37 +241,6 @@ extendTransport(class { // ── Node management (D5) ─────────────────────────────────────────────── - async fetchNodeStatus() { - const msg = await this._sendAndWait({ type: 'node_status', v: '0.1' }); - if (msg.type === 'error') throw new Error(msg.detail); - return msg; - } - - async updateNodeSettings(settings) { - const msg = await this._sendAndWait({ - type: 'node_settings_set', v: '0.1', settings, - }); - if (msg.type === 'error') throw new Error(msg.detail); - return msg; - } - - async addRoot(groupId, path, { name, kind, writable, removable } = {}, signFn) { - const msg = await this._sendAndWait({ - type: 'root_add', v: '1.1', - group_id: groupId, path, - name: name || '', kind: kind || 'generic', - writable: !!writable, removable: !!removable, - }); - if (msg.type === 'error') throw new Error(msg.detail); - if (msg.type === 'admin_challenge') { - // Everything the node will act on is in the subject, `writable` included. - const subject = window.MeshBayCrypto.rootAddSubject( - path, name || '', kind || 'generic', !!writable, !!removable); - return this._authorizeAdminOp(msg, 'root_add', subject, signFn); - } - return msg; - } - async removeRoot(groupId, rootName, signFn) { const msg = await this._sendAndWait({ type: 'root_remove', v: '0.1', @@ -284,24 +253,6 @@ extendTransport(class { return msg; } - async updateRoot(groupId, rootName, { writable, removable } = {}, signFn) { - const updates = []; - if (writable !== undefined) updates.push(`rw=${writable ? 'on' : 'off'}`); - if (removable !== undefined) updates.push(`rem=${removable ? 'on' : 'off'}`); - const subject = updates.length ? `${rootName}:${updates.join(',')}` : rootName; - const msg = await this._sendAndWait({ - type: 'root_update', v: '1.1', - group_id: groupId, root_name: rootName, - ...(writable !== undefined && { writable }), - ...(removable !== undefined && { removable }), - }); - if (msg.type === 'error') throw new Error(msg.detail); - if (msg.type === 'admin_challenge') { - return this._authorizeAdminOp(msg, 'root_update', subject, signFn); - } - return msg; - } - async ejectRoot(groupId, rootName, signFn) { const msg = await this._sendAndWait({ type: 'root_eject', v: '1.1', @@ -326,81 +277,6 @@ extendTransport(class { return msg; } - async unpinMember(userId, signFn) { - const msg = await this._sendAndWait({ - type: 'member_unpin', v: '0.1', user_id: userId, - }); - if (msg.type === 'error') throw new Error(msg.detail); - if (msg.type === 'admin_challenge') { - return this._authorizeAdminOp(msg, 'member_unpin', userId, signFn); - } - return msg; - } - - async rotateGek(groupId, signFn) { - const msg = await this._sendAndWait({ - type: 'gek_rotate', v: '0.1', group_id: groupId, - }); - if (msg.type === 'error') throw new Error(msg.detail); - if (msg.type === 'admin_challenge') { - return this._authorizeAdminOp(msg, 'gek_rotate', groupId, signFn); - } - return msg; - } - - async fetchRoster(groupId) { - const msg = await this._sendAndWait({ - type: 'roster_read', v: '0.1', group_id: groupId || '', - }); - if (msg.type === 'error') throw new Error(msg.detail); - return msg; - } - - async fetchDenylist() { - const msg = await this._sendAndWait({ type: 'denylist_read', v: '0.1' }); - if (msg.type === 'error') throw new Error(msg.detail); - return msg; - } - - async clearDenylist(subject) { - const msg = await this._sendAndWait({ - type: 'denylist_clear', v: '0.1', subject: subject || '', - }); - if (msg.type === 'error') throw new Error(msg.detail); - return msg; - } - - async attachGroup(name, sharedDir, uploadDir, signFn) { - const msg = await this._sendAndWait({ - type: 'group_attach', v: '0.1', - name, shared_dir: sharedDir, upload_dir: uploadDir || '', writable: true, - }); - if (msg.type === 'error') throw new Error(msg.detail); - if (msg.type === 'admin_challenge') { - // The directory being exposed is signed, not only the group's name. - const subject = window.MeshBayCrypto.groupAttachSubject(name, sharedDir, true); - return this._authorizeAdminOp(msg, 'group_attach', subject, signFn); - } - return msg; - } - - async detachGroup(name, signFn) { - const msg = await this._sendAndWait({ - type: 'group_detach', v: '0.1', name, - }); - if (msg.type === 'error') throw new Error(msg.detail); - if (msg.type === 'admin_challenge') { - return this._authorizeAdminOp(msg, 'group_detach', name, signFn); - } - return msg; - } - - async reloadConfig() { - const msg = await this._sendAndWait({ type: 'node_reload', v: '0.1' }); - if (msg.type === 'error') throw new Error(msg.detail); - return msg; - } - /** * Ask the node for a one-time pairing code admitting `userId` to this group. * diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport-chat.js b/packages/meshbay-hub/src/meshbay_hub/static/transport-chat.js index e49eb4c..b734d44 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport-chat.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport-chat.js @@ -57,7 +57,7 @@ extendTransport(class { * * Not a switch — there is nothing to turn on. The removals that matter open * an epoch by themselves; this is the operator saying "move the key anyway", - * the same instruction as `rotateGek` and signed for the same reason. + * signed like every operator instruction. */ async rotateChatEpoch(signFn) { // This connection's own group, not a parameter. Every settings pane takes diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport-media.js b/packages/meshbay-hub/src/meshbay_hub/static/transport-media.js index cf53c08..b4d4048 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport-media.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport-media.js @@ -99,7 +99,7 @@ extendTransport(class { * queried in (e.g. "fr-FR") — one for the whole node, since both are one * operator's shared credential/cache, not a per-group concern (see * setTmdbEnabled below for the per-group on/off switch). Signed like - * setAppsEnabled/updateRoot — an unsigned change would let any + * setAppsEnabled — an unsigned change would let any * member alter outbound third-party network traffic the operator never * agreed to (docs/MESHBAY_DESIGN.md §9.7, §6.5). `token: ''` explicitly clears * a previously-set custom token; omit it (undefined/null), like diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js index f9e1370..279396a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js @@ -193,8 +193,7 @@ function _aborted() { // table, then on Chat's directory, where it meant the pane went on showing an // unsaved-looking draft after a save that had worked. const BROADCAST_ACK_TYPES = new Set([ - 'root_update_ack', 'root_eject_ack', 'root_plug_ack', - 'root_add_ack', 'root_remove_ack', + 'root_eject_ack', 'root_plug_ack', 'root_remove_ack', 'app_directories_ack', 'chat_directory_ack', 'chat_link_preview_ack', 'chat_epoch_ack', 'search_listed_ack', ]); @@ -220,10 +219,9 @@ const ADMIN_OP_TYPES = new Set([ 'tmdb_override', 'tmdb_rematch', 'tmdb_config', 'tmdb_enabled', 'musicbrainz_enabled', 'file_delete', 'dir_delete', 'apps_enabled', 'set_scan_settings', 'member_revoke', - 'root_add', 'root_remove', 'root_update', 'root_eject', 'root_plug', + 'root_remove', 'root_eject', 'root_plug', 'app_directories', 'chat_directory', 'chat_link_preview', 'chat_epoch', - 'search_listed', 'member_unpin', 'gek_rotate', 'group_attach', - 'group_detach', 'invite_create', 'invite_link_create', 'invite_cancel', + 'search_listed', 'invite_create', 'invite_link_create', 'invite_cancel', ]); // ── Diagnostic trace (opt-in, off by default) ─────────────────────────────── @@ -367,9 +365,10 @@ window.addEventListener('hashchange', () => { // The `v: '0.1'` on every other message in this file is the historical value // and is read by nothing; it is left alone deliberately. The range is // negotiated once, at the start, not restated per message. -const MNP_V = '5.0'; -// Not raised with 5.0 (see meshbay_common/__init__.py): the break is confined to -// four signed operations, which a peer on the other side of it refuses to sign. +const MNP_V = '6.0'; +// Not raised with 5.0 or 6.0 (see meshbay_common/__init__.py): each break is +// confined to a few signed operations — 5.0 changed four subjects, 6.0 removed +// root_add, root_update and group_attach — and everything else still works. // Set at 4.0, a flag day. A member now presents a short-lived // MNP-audience token in the handshake, not its hub session token — a node // older than 4.0 expected the session token, and one newer refuses it, so the @@ -2030,11 +2029,11 @@ class MeshBayTransport { this._onMusicbrainzEnabled(Boolean(msg.enabled)); } - // A root's flags changed, or one was ejected, plugged, added or removed. - // Broadcast by the node to every peer, so everyone's table updates without - // waiting for the next index_sync. - if (msg.type === 'root_update_ack' || msg.type === 'root_eject_ack' - || msg.type === 'root_plug_ack' || msg.type === 'root_add_ack' + // A root was ejected, plugged or removed. Broadcast by the node to every + // peer, so everyone's table updates without waiting for the next index_sync. + // A root added or a flag changed — on the node's own machine, never over + // MNP — arrives in the index_delta the node pushes. + if (msg.type === 'root_eject_ack' || msg.type === 'root_plug_ack' || msg.type === 'root_remove_ack') { if (this._onRootsChanged) this._onRootsChanged(msg); } |