summaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-14 22:20:15 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-14 22:20:15 +0200
commit422647e7898b8df67cd11f8fb060aec23dec84aa (patch)
tree7ecca0e8df1bc8a7d26cf4b471bd44906dd9cfd1 /docs/MESHBAY_DESIGN.md
parent28c88908b88c58cba4de08710f6bb268ede02d57 (diff)
downloadmeshbay-422647e7898b8df67cd11f8fb060aec23dec84aa.tar.gz
feat(cli): meshbay-cli, a command-line client for members
Signs in to the hub, lists the account's groups and their folders, and downloads and uploads files directly with each group's node, end-to-end encrypted. With no argument it opens a two-pane interactive mode (Textual). Session and device keys are kept encrypted under the system's key storage, or a 0600 key file where there is none; node keys are pinned at first contact; a new machine links as a device. Packaged on its own on Linux (deb and rpm, on meshbay-common's venv, with a man page generated from the parser) and frozen next to meshbay-node in the full Windows installer, not the light one, with a self-test the build runs. client/keys.py ports keyderive.js and is LGPL like it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md114
1 files changed, 93 insertions, 21 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 2174879..6dd1b2e 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -16,7 +16,7 @@
> them — it names the invariant that holds today, not the incident that produced
> it. §13 is the register of those labels.
>
-> Wire versions at the time of writing: **MNP 6.2** (oldest peer accepted 4.0),
+> Wire versions at the time of writing: **MNP 6.3** (oldest peer accepted 4.0),
> **MHP 0.1**, packages **0.19.0**. The normative source for the wire format is
> `MESHBAY_NODE_PROTOCOL.md`; this document states the design the protocol
> serves, not its byte layout.
@@ -302,6 +302,7 @@ comes from the hash binding:
| Bound | Value |
|---|---|
| Request TTL | 1 h, `[node] device_request_ttl_minutes` |
+| Pending requests per account per node | 10; filing one more drops the oldest |
| Devices per account per node | 5 |
| Attempts per connection | 5, audited on exhaustion |
| Filing a request | requires the account to have at least one pinned identity already |
@@ -1105,15 +1106,22 @@ legitimate rotation.
> complete a handshake with. It is unverified at that point and is never a
> substitute for the ack.
-**The pre-proof window is three messages, and that is a bound to defend.** Only
-the two bundle fetches and `join_request` are served before the proof, because
-each is something a caller needs *in order to* prove possession of the group key:
-a returning browser has to recover its identity, and a new member has no key to
-prove with. Nothing else qualifies. Device messages are **authenticated-only** —
-a device request is countersigned later by a device already pinned, so requiring
-the caller to finish its own handshake first costs nothing and keeps the surface
-at three. The bundle fetches are additionally bounded (4 per connection) and
-audited, because that window is the neighbourhood **C4** and **C5b** came from.
+**The pre-proof window is four messages, and that is a bound to defend.** Only
+the two bundle fetches, `join_request` and `device_add_request` are served before
+the proof, because each is something a caller needs *in order to* prove possession
+of the group key: a returning browser has to recover its identity, a new member
+has no key to prove with, and a new device of a known member has none either — the
+group key is wrapped for an identity the node pinned, and a machine that keeps no
+bundle there (the desktop application, the command-line client, a browser with
+browser access off) holds no such identity until another device approves it.
+Nothing else qualifies. Filing a device request is **inert**: nothing is admitted
+until a device already pinned countersigns it, the code is bound to the new keys,
+the request is bounded per connection (5) and per account (10 pending), and it is
+audited. Every other device message — looking up, approving, listing, revoking,
+saying hello — is sent by a device that can finish its own handshake, and is
+**authenticated-only**. The bundle fetches are additionally bounded (4 per
+connection) and audited, because that window is the neighbourhood **C4** and
+**C5b** came from.
**Pinning is defence in depth, not the primary control.** A substituted node
already fails the key proof. Pinning covers the case where an attacker *holds* the
@@ -2610,16 +2618,16 @@ The rules that make this safe:
## 8. Clients
-### 8.1 Two clients, deliberately
+### 8.1 The clients, deliberately different
-| | Hub-served web SPA | Desktop client |
-|---|---|---|
-| Distribution | served by the hub | installed, signed release |
-| Code integrity | **T3 accepted** — the hub can inject | detectable *if* reproducible builds ship |
-| Key storage | IndexedDB / sessionStorage, plus a bundle on each node | OS-protected local storage; keys never bundled |
-| Transport | WebRTC | WebRTC **+ QUIC** (sidecar, for hub-less `group://`) |
-| Downloads | to disk where the engine allows it | native, streamed, unlimited |
-| Positioning | **convenience tier** — zero install | recommended for sensitive use |
+| | Hub-served web SPA | Desktop client | Command-line client (§8.6) |
+|---|---|---|---|
+| Distribution | served by the hub | installed, signed release | installed: its own `.deb`/`.rpm`, inside Windows Full and Store |
+| Code integrity | **T3 accepted** — the hub can inject | detectable *if* reproducible builds ship | as the desktop client: the hub serves it no code |
+| Key storage | IndexedDB / sessionStorage, plus a bundle on each node | OS-protected local storage; keys never bundled | OS key storage, else a 0600 file; opens a node's bundle, never writes one |
+| Transport | WebRTC | WebRTC **+ QUIC** (sidecar, for hub-less `group://`) | WebRTC |
+| Downloads | to disk where the engine allows it | native, streamed, unlimited | native, resumable, unlimited |
+| Positioning | **convenience tier** — zero install | recommended for sensitive use | a member's files from a terminal, scripts and headless machines |
**The SPA is not deprecated and stays.** It is the zero-install path, and the
objective is explicit: **a native client must not prevent web use.** It must be
@@ -2995,6 +3003,67 @@ MSE string) fed to a source buffer, with the node holding one slot per viewer.
---
+### 8.6 The command-line client
+
+`meshbay-cli` (`packages/meshbay-cli`) is a member's files from a terminal:
+`login`, `logout`, `groups`, `ls`, `get`, `put`, `trust`, and with no argument
+an interactive two-pane view (Textual) over the same library. It is not an
+operator tool — a node is administered through `meshbay-node` and its loopback
+API (§6.7) — and it does not stream, play or chat: those belong to an engine
+with a player and a page. English only.
+
+**The same protocol as the other clients, end to end.** The hub signs it in,
+issues the per-node token (E10) and relays the WebRTC offer and answer; from the
+DataChannel on, everything goes to the node and is sealed under the group key
+(§5.1, §5.2), and transfers take leases like any client's (§5.5). Nothing
+data-related passes through the hub.
+
+**Identity on a node, in this order:** the identity this machine kept from an
+earlier run; the one waiting for a device approval; the bundle the node keeps
+for browsers, opened with the passphrase (§3.7) and then kept locally; failing
+all three, a new key pair filed as a device request (§3.3) — the code is
+printed, an already linked device approves it, and the client joins with its
+own keys. It never writes a bundle. That is the desktop client's position: an
+installed program, no hub-served code (T3 does not apply), so device linking
+is admitted for it as for the application.
+
+**What it keeps.** `state.json` (hub, account, pinned node keys) and
+`secrets.enc` (AES-GCM: the refresh token, the device hub key, the node
+identities) in the user's data directory (`$XDG_DATA_HOME/meshbay-cli`,
+`%LOCALAPPDATA%\meshbay-cli`). The key that opens `secrets.enc` is in the OS
+key storage; where there is none, in a 0600 file, which `login` says once. The
+passphrase is never stored: a remembered session renews with the refresh
+token, else with the device hub key, and asks for the passphrase only when both
+fail. Two runs renewing at once take a file lock, so a rotated refresh token is
+never spent twice.
+
+**Node keys are pinned at first contact.** A node presenting another key is
+refused before anything is sent (exit 6) until `meshbay-cli trust reset NODE`
+— the deliberate way out, for a rotation the operator announced.
+
+**Text from the network is data.** Every name a node or a member chose is
+printed with control and bidi characters escaped, and drawn as plain text,
+never markup, in the interactive view. Local names are made writable on the
+machine and kept inside the destination; on Windows a download carries the
+Mark of the Web.
+
+**Remote paths** are `group[@owner]/root/folder/file`: `@owner` only when two
+groups share a name, names compared without case, `%2F` and `%25` for a `/` or
+a `%` in a name, and a link copied from the application accepted as it is.
+
+**Concurrency.** At most six WebRTC negotiations in flight, one connection per
+group; a group whose node is away is reported without stopping the others
+(exit 4).
+
+**Exit codes** are a contract for scripts: 1 usage, 2 sign-in, 3 not found,
+4 partial, 5 node unreachable or waiting for device approval, 6 node key
+changed, 7 version, 8 refused before anything was sent, 130 interrupted.
+
+**Packaging.** Its own `.deb`/`.rpm` (`meshbay-cli`, with the packages only it
+uses, on top of `meshbay-common`'s venv) and a man page generated from its
+argument parser; on Windows, `meshbay-cli.exe` beside `meshbay-node.exe` in
+the Full installer and the Store package, not in Light.
+
## 9. Group applications
### 9.1 The plug-in architecture
@@ -4487,7 +4556,9 @@ phone's photo backup (§9.12), the
operator CLI and loopback control API, the desktop client through its identity
and download stages, account recovery, the Windows port through packaging, and
the Android client — the shell, native keys, downloads and uploads, casting,
-and notifications while it is closed, fetched or pushed (§11.3).
+and notifications while it is closed, fetched or pushed (§11.3) — and the
+command-line client: groups, listing, downloads and uploads with resume,
+device linking, and an interactive two-pane view (§8.6).
The packages install: a machine has been taken from the built artefacts to a
running hub and node on **Ubuntu 26.04 (`.deb`), Fedora 44 (`.rpm`) and
@@ -4518,6 +4589,7 @@ process runs it — `systemctl --user` on Linux, Task Scheduler on Windows.
| — | **HEIC/HEIF in Photos.** The indexer does not classify `.heic`/`.heif` as images and no browser but Safari draws them, so a phone that shoots HEIC has its backed-up photos stored and listed in Files but absent from Photos. Needs `pillow-heif` in every package and a JPEG rendition for the viewer (§9.12) |
| — | **Photo backup with the application closed**, and **"remove what this phone sent"** (§9.12). A closed application has no page and so no transport; it runs at the next opening |
| — | **Android: phone behaviour and release** — the back button driving the page, recovery from a network handover, keeping a download alive with the screen off, lock-screen media controls, and an update channel (§11.3) |
+| — | **Command-line client: cross-group search and chat** (§8.6). Not run on Windows yet; its transfer windows are not yet measured against a node on another machine |
| — | **Federation between two hubs.** The protocol is written and switched off in the code (§7.6); what is not built is one run between two machines |
### 15.3 Open, and why each is where it is
@@ -4592,7 +4664,7 @@ superseded document kept under `docs/`, a note somebody holds elsewhere.
| `draft-v5 §6.2`–`§6.4` — node registration, signaling, client addresses | §7.2 |
| `draft-v5 §7` — cryptography | §4.1–§4.4, §4.6 |
| `draft-v5 §7.1` — the keypair bundle and C4 | §3.7 |
-| `draft-v5 §8.1`, `§8.2`, `desktop-client-v1.md §2` — the two clients, the shell | §8.1, §8.2 |
+| `draft-v5 §8.1`, `§8.2`, `desktop-client-v1.md §2` — the clients, the shell | §8.1, §8.2 |
| `draft-v5 §9`, `draft-v6 §5` — open items | §15.3 |
| `draft-v5 §10` — testing posture | §12 |
| `draft-v6 §2.2`, `desktop-client-v1.md §4`, `§4.5`, `§4.6` — device linking | §3.3 |