aboutsummaryrefslogtreecommitdiffstats
path: root/docs/meshbay-draft-v6.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-23 15:15:35 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-23 15:15:35 +0200
commit9f02ee2c09652abf1308bdfa4a3eec4e9ca9ac83 (patch)
treeb13198a79a0965f254c828adba3eb41dd5e9a5b4 /docs/meshbay-draft-v6.md
parent8dc11dc05a35a5d64ba4d2c892ccc01c7bfae3da (diff)
downloadmeshbay-9f02ee2c09652abf1308bdfa4a3eec4e9ca9ac83.tar.gz
feat(hub): split the group UI into a pluggable "applications" architecture
GroupPage's 6620-line app.js carried Chat and Files wedged in directly, with no way to add another group-level app without touching the shell itself. It is now app.js (routing, non-group pages) plus nine focused files — apps.js (the registry), chat-app.js, files-app.js, video-player.js, group-page.js (the shell), group-settings.js, hub-client.js, icon.js and file-utils.js — with docs/apps.md as the checklist for adding one (Videos/Music/Photos are sketched there, not built). Node side gained the matching enablement mechanism, mirroring member_upload exactly: a roster setting, a signed apps_enabled op enforced by _has_admin_authority, exposed in the handshake ack. Operators toggle applications per group from Settings, which also gained a small reorder: Invite, Pairing, Applications, Shared directories, Uploads, danger zone, Your devices, Members. Two bugs surfaced during the split, both missing an import across the new file boundary and invisible to node --check or a module-load probe since they only throw when the code path actually runs: - group-page.js called onRefreshAuth on a stale-token handshake rejection, but app.js never imported refreshAccessToken from hub-client.js — so a brand new member (including a group's own creator) hit "Not a member of this group" and the retry silently failed, throwing before it could refresh the token. - chat-app.js called getLocale() for message timestamps without importing it from i18n.js. Opening Chat on a group with real messages threw mid- render; uncaught, that appears to wedge Preact's render scheduler, so every button on the page stopped responding until reload. Caught the second class of bug with a proper no-undef audit across all split files (a temporarily installed ESLint 9, since the system one is too old to parse this codebase's syntax) rather than trusting grep. 827 tests pass; 6 new ones cover the apps_enabled policy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016SF6RKNBKg9qejmoMJ9ybA
Diffstat (limited to 'docs/meshbay-draft-v6.md')
-rw-r--r--docs/meshbay-draft-v6.md42
1 files changed, 42 insertions, 0 deletions
diff --git a/docs/meshbay-draft-v6.md b/docs/meshbay-draft-v6.md
index 2ea73c4..72cfdbf 100644
--- a/docs/meshbay-draft-v6.md
+++ b/docs/meshbay-draft-v6.md
@@ -26,6 +26,7 @@
| `docs/invite-pairing-v1.md` | invitations, pairing codes, the node roster — **as built** |
| `docs/per-node-identity-v1.md` | identity keys are per node; the hub stores none |
| `docs/desktop-client-v1.md` | the desktop client in full — shell, device linking, roots, packaging, execution order |
+| `docs/apps.md` | the group UI's plug-in architecture — as built, and how to add an application |
| `devel-phases-next.md` | the roadmap |
---
@@ -43,6 +44,7 @@
| 7 | Accounts | Native registration is **hybrid**: passphrase-derived `auth_key` (the recovery path) plus a device Ed25519 key for day-to-day authentication | E3 / decision 4 |
| 8 | Authorship | Chat senders are **cryptographically authenticated to each other**; an upload has a **provable owner** who may delete it, as the operator may. v5's node-asserted attribution is replaced | operator decision, §2.4b |
| 9 | Node authority | The operator may **close uploading to everyone but themselves**, per group. Signed MNP op, stored on the node, enforced by the node — the hidden button is a courtesy, the refusal is the control | §2.1b |
+| 10 | Client | A group's UI is a **set of pluggable applications** (Chat, Files today), not one monolithic page. Which are shown is a per-group, operator-signed setting on the same pattern as change 9 | §2.7 |
---
@@ -222,6 +224,43 @@ No parameter changes. `keyderive.py` now has a third consumer: the desktop clien
it, and the standing warning is unchanged — **never change those parameters in one
place**; a mismatch does not look like an error, it looks like an account nobody can open.
+### 2.7 The group UI becomes a set of applications
+
+New (2026-08-23). A group had two fixed tabs, Chat and Files, both defined inside one
+monolithic `static/app.js`. Two things motivated splitting it before adding to it: the file
+had become the thing every unrelated change touched, and the roadmap wants three more
+group-level surfaces — a Netflix-style video browser, a Spotify-style music player, a photo
+album viewer. None of the three need a protocol change: the node's indexer already
+classifies files as `video`/`audio`/`image`, and they would read the same `index_sync` /
+`file_req` / `stream_req` messages Files and the video player already use. What they need is
+somewhere to live that is not one file, and a way for an operator to turn one off.
+
+**The shape.** `group-page.js` is now the shell: the WebRTC connection, the file index, the
+tab bar, and the video/preview modals, none of which are Files- or Chat-specific. `apps.js`
+holds the registry — `[{ key, icon, labelKey, Component }]` — and every registered
+component receives the same props object from the shell, spread rather than hand-listed, so
+adding an app changes no code in the shell itself. Chat and Files each moved to their own
+file (`chat-app.js`, `files-app.js`) to prove the mechanism; nothing else exists yet.
+
+**Enablement is the same pattern as change 9, on purpose.** `apps_enabled` is a per-group
+setting: lives on the node (`roster.db`, not the hub, not `node.toml`, for the identical
+reason `member_upload` does — a hub or a config file that decided this would have authority
+over the node), changed by a signed operator instruction (`OP_APPS_ENABLED`), enforced by
+the node refusing to store an unrecognised or empty set rather than by the client's
+honesty. **Settings itself is not an app** and cannot be disabled — the one way back if
+everything else were turned off.
+
+**What this does not change.** No new server state on the hub (change 5 stands unmoved: the
+enabled-apps set is group-related state, and it lives on the node like everything else in
+that category). No new adversary or trust boundary — this is a display policy, not a key or
+a permission over content; a member whose client shows a hidden tab's data anyway would
+still be a member the node already serves that data to.
+
+Full detail — the props contract, the file layout, and a checklist for adding a new
+application — is `docs/apps.md`, on the same basis `docs/desktop-client-v1.md` holds the
+desktop client's detail: this document states what changed and what holds, not how to build
+on it.
+
---
## 3. Filesystem portability as a security property
@@ -302,3 +341,6 @@ design, which is long and belongs in one place:
**`docs/desktop-client-v1.md`** — shell requirements, device-linking protocol and schema,
account creation, node management over signed MNP ops, several roots per group, filesystem
portability, packaging and first run, the web tier, and the execution order for all of it.
+
+**`docs/apps.md`** — the group UI's plug-in architecture (§2.7): the props every
+application receives, the enablement mechanism end to end, and a checklist for adding one.