aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CLAUDE.md8
-rw-r--r--docs/MESHBAY_DESIGN.md15
-rw-r--r--docs/QUICKSTART.md380
-rw-r--r--docs/USERGUIDE.md861
4 files changed, 1260 insertions, 4 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
index 1f837eb..540bd8f 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -13,6 +13,14 @@ optional hub feature and are off on the reference deployment.
`docs/` is either an operational guide, or a superseded document kept for its
cross-references and carrying a banner that says so.
+**`docs/QUICKSTART.md` and `docs/USERGUIDE.md` are the user documentation** —
+written for somebody who is not a developer, Linux and the browser only. They
+name CLI verbs, default values, setting names and interface labels, so a change
+to any of those is a change to them: the previous user guide was retired
+because it had drifted past the point where a reader could tell which half was
+still true. What keeps them honest is that they state what is *not* built as
+readily as what is (§15.2, §15.3); a feature that lands deletes its line there.
+
## Repository structure
```
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index dcfc77b..551c0ed 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -30,6 +30,8 @@
| Read | For |
|---|---|
| **this document** | the architecture, the trust model, and the reason each decision is what it is |
+| `QUICKSTART.md` | one machine to a working group, for somebody who has installed nothing |
+| `USERGUIDE.md` | using a group and running a node, for the person who does either |
| `MESHBAY_NODE_PROTOCOL.md` | the MNP wire format, message by message |
| `transfers-v1.md` | the transfer system's failure-mode analysis, kept because a synthesis cannot carry "every way a slot can be lost" |
| `playlists.md` | the playlist design and its interface in full, with what building it corrected (§9.10) |
@@ -38,10 +40,15 @@
| `PACKAGING-GUIDE.md`, `HTTPS.md`, `MAIL-SERVER.md`, `windows-build.md` | installation and server operations |
| `CLAUDE.md` | project conventions and the engineering lessons that govern how changes are made |
-**There is no user guide.** The one that existed described the system before
-per-node identity, named roots and the sealed wire, and was retired rather than
-repaired — a document a reader cannot tell the sound parts of is worse than none.
-Writing a new one starts here.
+**The user documentation is `QUICKSTART.md` and `USERGUIDE.md`** (written
+2026-09-18). The guide that preceded them described the system before per-node
+identity, named roots and the sealed wire, and was retired rather than repaired
+— a document a reader cannot tell the sound parts of is worse than none. The
+replacements are written from this document and from the code, address a reader
+who is not a developer, and cover Linux and the browser; Windows stays in
+`PACKAGING-GUIDE.md`. **They state what is not built as readily as what is**
+(§15.2, §15.3), which is what keeps them from drifting into the same condition
+as the one they replace.
### 0.2 Reference labels
diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md
new file mode 100644
index 0000000..9a418f2
--- /dev/null
+++ b/docs/QUICKSTART.md
@@ -0,0 +1,380 @@
+# MeshBay — Quickstart
+
+This guide takes one Linux machine, turns it into a MeshBay node, creates a
+group over a directory on it, and invites a second person. It assumes you can
+use a terminal. It assumes nothing else.
+
+For what each step *means* — and for everything this guide leaves out — read
+[`USERGUIDE.md`](USERGUIDE.md).
+
+> **Scope.** Linux only: Ubuntu/Debian and Fedora. Windows is covered by
+> [`PACKAGING-GUIDE.md`](PACKAGING-GUIDE.md) and is not repeated here. This
+> guide uses **meshbay.org** as the hub; running your own is
+> [`PACKAGING-GUIDE.md`](PACKAGING-GUIDE.md) plus [`HTTPS.md`](HTTPS.md) and
+> [`MAIL-SERVER.md`](MAIL-SERVER.md).
+
+---
+
+## What you are building
+
+```
+ your machine meshbay.org your friend
+ ──────────── ─────────── ───────────
+ meshbay-node ◄─── signalling ────► hub ◄─── sign-in ───► browser
+ holds the files accounts, or desktop
+ holds the group key group list, client
+ decides who is served introductions
+ │ │
+ └──────────── encrypted, peer to peer, direct ──────────────────┘
+ (your files never touch the hub)
+```
+
+Three things, and it is worth knowing which is which before you start:
+
+| | What it is |
+|---|---|
+| **The hub** | An address book and a switchboard. It holds your account and your group's name, and introduces two machines to each other. It never sees a file, a message or a key. |
+| **The node** | A daemon on *your* machine. It holds the directories you share and the key that encrypts them. It decides who gets served. This is the part you install. |
+| **The client** | The web app the hub serves, or the desktop application. This is what you and your members look at. |
+
+---
+
+## Before you start
+
+- [ ] **A Linux machine that stays on.** Your files are served from it, so a
+ laptop that sleeps is a group that is offline half the time. A spare
+ mini-PC, a home server or an always-on desktop is the right shape.
+- [ ] **A directory to share**, with something in it.
+- [ ] **An email address** you can read — account sign-up sends a code to it.
+- [ ] **About half an hour**, plus however long a first index takes — which
+ depends on how much you are sharing, and does not need watching.
+- [ ] **Terminal access** to that machine. Everything below works over SSH; no
+ graphical session is needed on the node.
+
+You do **not** need a static IP, a domain name, port forwarding, or a router
+you control. Two residential ISPs have been measured working without any of it.
+
+---
+
+## Step 1 — Download the packages
+
+From **https://meshbay.org/downloads**, take the ones for your
+distribution:
+
+| | Ubuntu / Debian | Fedora |
+|---|---|---|
+| **`meshbay-common`** — required, install it first | `.deb` | `.rpm` |
+| **`meshbay-node`** — the daemon that hosts your files | `.deb` | `.rpm` |
+| `meshbay-client` — the desktop application, optional | `.deb` | `.rpm` |
+
+You need the first two. The desktop application is worth having if this machine
+has a screen, but nothing in this guide requires it: a terminal and a browser
+are enough.
+
+Put them in one directory — the next step installs them together.
+
+---
+
+## Step 2 — Install
+
+Install **with your package manager, not with `dpkg -i` / `rpm -ivh`**. Both of
+those ignore weak dependencies, and the video drivers that let this machine
+re-encode a film without melting are weak dependencies.
+
+```bash
+cd ~/Downloads # wherever you put them
+
+# Ubuntu / Debian
+sudo apt install ./meshbay-common_*.deb ./meshbay-node_*.deb
+
+# Fedora
+sudo dnf install ./meshbay-common-*.rpm ./meshbay-node-*.rpm
+```
+
+Add `./meshbay-client_*` to that line to install the desktop application too.
+
+`meshbay-common` carries everything the others need, so there is nothing else
+to install and no Python environment to set up. **ffmpeg**, required for video
+streaming, comes in as a recommended dependency of the command above; without
+it the node refuses to start and says so.
+
+Check it landed:
+
+```bash
+meshbay-node status
+```
+
+It will report that there is no keystore. That is the next step.
+
+---
+
+## Step 3 — Create your account
+
+Open **https://meshbay.org** in a browser and register.
+
+Three things on that page are worth slowing down for:
+
+1. **Your passphrase is not a password.** It never reaches the hub. It
+ encrypts the copy of your identity keys that lives on every node you join,
+ and nobody — not the hub, not an operator, not us — can reset it. The floor
+ is 12 characters; **four unrelated words** is the shape that actually works.
+2. **Your recovery key is shown exactly once.** It is a 32-byte secret that can
+ restore your access to your groups if you forget the passphrase. Put it in a
+ password manager before clicking past it. Lose both it and the passphrase and
+ your files are still there — but every group has to be rejoined by hand, with
+ a new identity.
+3. **Verify your email.** A code arrives; the account is inactive until you
+ enter it.
+
+---
+
+## Step 4 — Set up the node
+
+Back on the node machine:
+
+```bash
+meshbay-node init
+```
+
+It asks for the hub URL (press Enter for `https://meshbay.org`) and your hub
+username. Then it writes `~/.config/meshbay/node.toml`, creates the keystore,
+and prints something like:
+
+```
+Node key: 7mK2p...base64...=
+```
+
+**Copy that key.** In your browser, on meshbay.org: **Settings → Link Node**,
+paste it, submit.
+
+This is what lets the hub introduce your members to this machine. It is a
+public key; it identifies the node and unlocks nothing.
+
+---
+
+## Step 5 — Start the node
+
+```bash
+systemctl --user enable --now meshbay-node
+loginctl enable-linger $USER # keep serving when you are not logged in
+```
+
+`enable-linger` is not optional on a machine you reach over SSH. Without it,
+systemd stops your user services when your session ends — the group goes dark
+the moment you disconnect.
+
+Check:
+
+```bash
+meshbay-node status
+```
+
+You want to see `daemon running`. If not:
+
+```bash
+journalctl --user -u meshbay-node -f
+```
+
+### If your files live outside your home directory
+
+The node unit is hardened: it can only write inside `~/.config/meshbay` and
+`~/.local/share/meshbay`. An external drive or a second partition needs a
+drop-in, and it needs **both** lines:
+
+```bash
+mkdir -p ~/.config/systemd/user/meshbay-node.service.d
+cat > ~/.config/systemd/user/meshbay-node.service.d/paths.conf <<'EOF'
+[Service]
+ReadWritePaths=/mnt/library
+RequiresMountsFor=/mnt/library
+EOF
+systemctl --user daemon-reload
+systemctl --user restart meshbay-node
+```
+
+`RequiresMountsFor` matters as much as `ReadWritePaths`. The service runs in its
+own mount namespace, so a volume mounted *after* it started is invisible to it —
+and the symptom is a directory that reads as empty while everything else looks
+correct.
+
+---
+
+## Step 6 — Create the group
+
+In the browser, on meshbay.org: **Create group** in the sidebar.
+
+- **Name** — anything. It is shown to members as `name@yourusername`.
+- **Join policy** — leave it on **Invite only**. The alternative, *Open*, lists
+ the group publicly and lets anyone in, including the hub. There are groups
+ that want that; a first one is not it.
+- **Description** — optional, shown to members on their home page.
+
+The group now exists on the hub and has no files, no key and no host. The next
+step is all three.
+
+---
+
+## Step 7 — Hand the group to your node
+
+Back on the node machine. Use the same name you just typed:
+
+```bash
+meshbay-node group add "Family Photos" --dir ~/Pictures/shared
+meshbay-node reload
+meshbay-node gek init --group "Family Photos"
+```
+
+What those three did:
+
+| | |
+|---|---|
+| `group add` | told the node to host that group, and made the directory its first shared folder. It is **read-write** by default, so members can upload into it. Add `--no-writable` if you want a published, read-only library. |
+| `reload` | made the running daemon re-read its config without dropping anyone. |
+| `gek init` | generated the group's encryption key. **Nothing works before this** — the key never leaves your node, and every member receives it wrapped for their own key, on every connection. |
+
+The node starts indexing immediately. A large library takes a while; you can
+watch it with `meshbay-node status` or in the group page, which shows the
+progress.
+
+Want more than one directory in the group?
+
+```bash
+meshbay-node root add /mnt/library/films --name Films
+meshbay-node root add /mnt/usb/archive --removable # external drive
+meshbay-node root list
+```
+
+Roots added later are **read-only by default** — the opposite of the first one,
+deliberately. Pass `--writable` if members should be able to upload there.
+
+---
+
+## Step 8 — Pair your browser
+
+Your node does not take orders from the hub. Before your browser can invite
+anyone or delete a file, the node has to recognise it:
+
+```bash
+meshbay-node operator pair
+```
+
+It prints a code like `R3H8-TB6V`, valid 24 hours. In the browser, open your
+group → **Members** tab → enter the code.
+
+The code never passes through the hub. That is the whole point of it: a hub
+that could name your operator key could make itself your node's administrator.
+
+---
+
+## Step 9 — Let people reach you
+
+WebRTC picks a fresh UDP port per connection, so there is no single port to
+forward. What the node needs is to **accept unsolicited inbound UDP on your
+LAN** — browsers announce themselves with an address the node cannot resolve,
+so the browser must be able to call the node, not the reverse.
+
+```bash
+# Ubuntu — your LAN, never "anywhere"
+sudo ufw allow from 192.168.0.0/16 app "MeshBay Node"
+
+# Fedora — apply to the zone holding your LAN interface
+sudo firewall-cmd --permanent --zone=FedoraWorkstation --add-service=meshbay-node
+sudo firewall-cmd --reload
+```
+
+**Scope it to the LAN.** This profile opens UDP 1024-65535; it does not belong
+in an internet-facing zone. Members outside your network are reached by hole
+punching, which needs no open port at all.
+
+The node's own admin surface is not affected: it listens on loopback only,
+behind a token, and is never network-exposed.
+
+---
+
+## Step 10 — Invite someone
+
+Your friend needs a meshbay.org account first — send them to Step 3. Then, with
+their username:
+
+```bash
+meshbay-node member invite alice_dupont
+```
+
+You get:
+
+```
+INVITATION CODE K7P2-9WQX
+valid until 2026-09-25T17:40:00Z
+```
+
+Send that code however you normally talk to them — message, mail, out loud. It
+works once, for that account only, for that group only, and the hub never sees
+it. You do not need to be online when they use it.
+
+You can do the same thing from the **Members** tab of the group, from the
+browser you paired in Step 8.
+
+---
+
+## Step 11 — What your friend does
+
+1. Signs in at **https://meshbay.org**.
+2. The group is already in their sidebar — creating the invitation registered
+ their membership.
+3. They open it and are asked: *"This node needs to recognise you."* They paste
+ the code.
+4. That is it. Your node pins their key, wraps the group key for it, and their
+ files appear.
+
+They will not be asked again on that browser. A *different* browser on the same
+node recovers the same identity with their passphrase alone. A different
+*node* — someone else's group — asks for a code again, as first contact always
+does.
+
+---
+
+## Did it work?
+
+```bash
+meshbay-node status
+```
+
+A healthy node reads roughly like this:
+
+```
+hub https://meshbay.org (user yourname)
+node key 7mK2p...=
+daemon running — ok
+node_id 82.65.x.x:0
+groups 1 files 4213 peers 1
+config /home/you/.config/meshbay/node.toml
+ group Family Photos [private] 8f3c...
+ shared → /home/you/Pictures/shared (rw)
+operator yourname key AAAC3NzaC1lZDI1… paired 2026-09-18T14:02:11Z
+```
+
+If a line is missing, `status` names the command that fixes it. The four that
+catch most people:
+
+| Symptom | Cause |
+|---|---|
+| `daemon not running` | Step 5, or a startup error in `journalctl --user -u meshbay-node` |
+| `operator NONE PAIRED` | Step 8 — invites and file deletion are refused until this is done |
+| an `action needed:` block naming a group key | Step 7's `gek init` was skipped — `meshbay-node group list` shows it as `NO KEY` |
+| Group shows files to you, nothing to a member | The member never redeemed a code, or the firewall in Step 9 |
+
+---
+
+## Where to go next
+
+- **Point Videos, Music and Photos at their folders.** Group → Settings — as
+ many folders as each library needs. Until you do, those tabs stay empty and
+ say so.
+- **Turn off the applications you do not want.** Group → Members → Applications.
+ Files and Settings always stay.
+- **Add your other devices.** Your phone's browser, the desktop application:
+ each one asks to be recognised, and you approve it from a device already
+ linked. Nobody else is involved.
+- **Read [`USERGUIDE.md`](USERGUIDE.md)** — what each application does, how
+ members are managed, what happens when someone leaves, what is private and
+ what is not, and what to do when something breaks.
diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md
new file mode 100644
index 0000000..9f7cea7
--- /dev/null
+++ b/docs/USERGUIDE.md
@@ -0,0 +1,861 @@
+# MeshBay — User Guide
+
+This guide is for people who **use** MeshBay and for people who **run** a node.
+
+If you have not installed anything yet, start with
+[`QUICKSTART.md`](QUICKSTART.md) and come back here.
+
+**Contents**
+
+1. [What MeshBay is](#1-what-meshbay-is)
+2. [Your account](#2-your-account)
+3. [Your devices](#3-your-devices)
+4. [Joining a group](#4-joining-a-group)
+5. [Using a group](#5-using-a-group)
+6. [Running a node](#6-running-a-node)
+7. [Managing people](#7-managing-people)
+8. [What is private, and what is not](#8-what-is-private-and-what-is-not)
+9. [When something breaks](#9-when-something-breaks)
+10. [Reference](#10-reference)
+11. [Running your own hub](#11-running-your-own-hub)
+12. [What this software does not do yet](#12-what-this-software-does-not-do-yet)
+
+---
+
+## 1. What MeshBay is
+
+MeshBay gives a small group of people — a family, a household, a few friends —
+private access to files that live on **somebody's own machine**, with
+applications over them: a chat, a file explorer, a video library, a music
+player, a photo album.
+
+It is not a public file-sharing network and it is not a cloud. Nothing is
+uploaded to a company. The files stay where they already are.
+
+### The three parts
+
+**The hub** (meshbay.org, or one you run) holds accounts and the list of
+groups, and introduces two machines to each other. Under a kilobyte of
+connection setup passes through it. For a private group — the default, and what
+this guide assumes — it never sees a file, a message, a file name or a key. §8
+says exactly where that stops.
+
+**The node** is a daemon on the machine that holds the files. It holds the
+group's encryption key, and it is the only thing that decides who gets served.
+Not the hub — the node. If the hub invented an account and put it in your
+group, the node would still refuse it.
+
+**The client** is either the web application the hub serves, or the desktop
+application you install. Both are the same interface.
+
+### The one idea that explains the rest
+
+> **Everything about a group lives on the machine that hosts it.**
+
+The files, the list of them, who has been let in, invitations, chat history,
+which folders allow uploads, how each application is set up — all of it on that
+machine. The hub keeps accounts, group names and who belongs to what, and
+nothing more.
+
+That is why the person running the group sends you the invitation code rather
+than the website. It is why a hub having a bad day loses nothing. And it is why
+there is no "I forgot my group" button to press.
+
+### What a group is
+
+A group is **people + directories + applications**.
+
+```
+ A group's contents, as members see it
+ ─────────────────────────────────────
+ /
+ ├── Films/ → /mnt/library/films on the operator's machine
+ ├── Music/ → /mnt/audio an external drive
+ └── Documents/ → /home/them/share read-write: members may upload
+```
+
+Each of those is a **root** — a named directory. Members see the names; they
+never see, and never name, a path on the operator's disk.
+
+---
+
+## 2. Your account
+
+Your account lives on a hub. It is a username, an email address, and a
+passphrase — and the passphrase is the part worth understanding.
+
+### Your passphrase is not a password
+
+It never reaches the hub. Your browser turns it into two separate values: one
+that proves who you are to the hub, and one that encrypts **your identity keys**
+where they are stored — on each node you have joined.
+
+Two consequences:
+
+- **The hub can reset your sign-in. Nobody can reset your keys.** A hub
+ operator, a node operator and the authors of this software are all equally
+ unable to open your keys. There is no back door, no support ticket, no
+ recovery from a database.
+- **A good passphrase is worth more here than anywhere else.** Twelve
+ characters is the minimum the sign-up page accepts; four unrelated words is
+ the shape that really protects you. The locked copy of your keys on each
+ machine you join is only as strong as what locks it.
+
+### Your recovery key
+
+Shown **once**, at sign-up. It is a long random secret rendered as words. Put it
+in a password manager.
+
+What it is for: a second, recovery-encrypted copy of your identity is stored on
+each node beside the passphrase-encrypted one. With the recovery key, a
+passphrase reset also restores your access to your groups. Without it, a reset
+restores your sign-in only, and every group has to be rejoined by hand with a
+fresh code from its operator.
+
+If you missed it at sign-up, or you signed in from a new browser: **Settings →
+Recovery key → Enter recovery key** adds the backup copies to every group whose
+node is reachable.
+
+### Forgetting your passphrase
+
+**Sign-in → "Forgot your passphrase?"** You need your username **and** the
+email address on the account — the page says the same thing either way, so if
+no code arrives, one of the two did not match.
+
+A code arrives by email. Enter it with a new passphrase, and your recovery key
+if you have one.
+
+A reset also signs out every device that had been signing in by itself, so each
+one asks for your passphrase once more. That is on purpose: if you are
+resetting because something may have gone astray, a laptop that still lets
+itself in is the thing you want stopped.
+
+### Changing a passphrase you still know
+
+**Settings → Passphrase → Change passphrase.**
+
+Your keys are re-encrypted on every group whose node is online **before**
+anything changes on the hub — so if it fails half way, nothing has changed.
+Groups whose nodes were offline are listed by name afterwards; for each one,
+ask its operator to unpin you and send a fresh code.
+
+### Deleting your account
+
+**Settings → Delete account.** It removes your account, memberships and
+notifications from the hub, and frees your username.
+
+What it cannot reach is anything sitting on other people's machines: files you
+uploaded stay where their operator keeps them, and each node remembers you
+until its operator says otherwise. Connection logs are kept for a year, as the
+law requires.
+
+---
+
+## 3. Your devices
+
+You can use one account from several devices — a laptop browser, a phone, the
+desktop application. Each one gets **its own key on each machine you join** —
+they are not copies of one key, and the key you hold on one person's machine
+means nothing on another's.
+
+### Adding a device
+
+The first time a new browser opens a group, it says *"This browser is not
+linked to this node yet"* and offers a code. You then approve it **from a
+device that is already linked** — Members tab → *"Linking a new device? Enter
+the code it is showing."*
+
+No operator is involved and the code never passes through the hub — one of
+your own devices vouches for the new one, which is what stops anybody else
+adding a device to your account.
+
+A few limits: **five devices per account on each machine**, a code lasts an
+hour, and five wrong codes in a row stop the attempt.
+
+### Removing a device
+
+**Settings** on the node's group, or the Members tab: *Remove*. It loses access
+to that node until it is linked again. Removing a device also moves the chat
+key on, so it cannot read anything written from then on.
+
+### One passphrase, several browsers
+
+A second browser on a machine you have already joined picks up the *same*
+identity: it asks for your passphrase, unlocks the copy of your keys kept
+there, and you are in. No second code, nobody to ask — which is another reason
+the passphrase is worth choosing well.
+
+### The desktop application
+
+Worth installing if you use MeshBay more than occasionally:
+
+| | Browser | Desktop application |
+|---|---|---|
+| Install | none | a package |
+| Interface comes from | the hub, on every visit | inside the package, from disk |
+| Keys | in the browser, plus a copy on each node | in the OS keyring, never bundled anywhere |
+| Downloads | to disk where the browser allows it | native, streamed, no size limit |
+| Casting to a TV | — | yes |
+
+The difference that matters: a browser fetches its code from the hub every time
+you open it, while the application carries its own and never asks the hub for
+any. So the application is the one to prefer for anything you care about. The
+browser stays, and is a perfectly reasonable way to use MeshBay — being able to
+open a group on someone else's laptop with nothing installed is worth having.
+
+---
+
+## 4. Joining a group
+
+Someone who runs a node invites you. You need an account on the same hub first.
+
+1. **They send you a code** — eight characters like `K7P2-9WQX`, by message,
+ mail, or read out loud. It is good for 7 days by default, works once, and
+ only for your account in that one group.
+2. **You sign in**, and the group is already in your sidebar.
+3. **You open it.** It says *"This node needs to recognise you"*. Paste the
+ code.
+4. Done — the machine hosting the group recognises you from now on, and the
+ files appear.
+
+You will not be asked again on that browser, or on that machine.
+
+**The code never passes through the hub**, and that is the whole reason it
+exists: it proves the invitation came from the person running the group, and
+not from the service in the middle.
+
+### Leaving
+
+Group menu → **Leave group**. You lose access to its files and chat. Anything
+you uploaded stays on the machine hosting the group, which also remembers you
+until its operator says otherwise.
+
+---
+
+## 5. Using a group
+
+A group is a set of tabs. Which ones you see is the operator's choice; **Files**
+and **Settings** are always there.
+
+### Files
+
+The explorer. Roots are the top-level folders; below that it behaves like any
+file browser — sort, select, download, preview.
+
+- **Upload** appears only where the current folder is in a **read-write** root.
+ You can also drag files and whole folders onto the file list.
+- **A drop is decided before anything is sent.** A name already in the folder,
+ or a name the node cannot store, cancels the whole drop and tells you which
+ name caused it — so you never end up with half a copy.
+- **Download a folder as a zip**, built in your browser from the same encrypted
+ chunks as any other download. The node never compresses anything.
+- **Delete** — you can remove a file you uploaded yourself; anything else is
+ the operator's to remove. A folder has to be empty first, so nothing here can
+ sweep away files you cannot see.
+
+### Chat
+
+Per-group messages, always encrypted, with threads, attachments and link
+previews.
+
+- **Attachments** need a read-write folder in the group. Where there is none,
+ the paperclip is off and says so.
+- **Link previews** are fetched **by the node** — the machine hosting the group
+ makes a request to whatever site somebody linked. The operator can turn it
+ off.
+- **History is kept on the node**, and stays readable to members. A member
+ removed from the group cannot read anything written after their removal.
+- Sometimes a message reads *"Written before this device could read this
+ conversation"* — that is a device added later, not an error. A message marked
+ *"the signature does not match the sender"* is different and worth asking
+ about.
+
+### Videos
+
+A poster browser over the folders the operator chose for it. Two modes: a
+poster grid with artwork and summaries, and a plain folder-driven list that
+needs no third-party service at all.
+
+- **Nothing appears until the operator chooses at least one folder** — the tab
+ tells you so, rather than just looking empty.
+- Films and shows are **one card each**, not one per file; a show expands into
+ seasons and episodes.
+- The operator can **correct a wrong match**, and the correction applies to the
+ whole show rather than one episode.
+- Streaming has **seeking, audio-track selection and subtitles**. Picture-based
+ subtitle tracks (the ones stored as images rather than text) are not offered:
+ they would need text recognition to display, so they are left out rather than
+ listed and blank.
+- A library is read **a page at a time**; the page size is your own preference
+ in Settings, not the group's.
+
+### Music
+
+An album browser and a player, over the folders the operator chose for it.
+
+- Tags in your files come first, then folder and filename, then a lookup for
+ cover art or canonical spelling.
+- **The player survives leaving the tab** — go to Files, go to another group,
+ the music keeps playing.
+- **Playlists are yours, not the group's**, and follow you across groups and
+ devices. Favourites, queues saved as playlists, drag to reorder.
+
+### Photos
+
+An album browser over the folders the operator chose for it, where **an album
+is a folder**. Thumbnails come from the node, already rotated correctly.
+
+**Location data is never shown.** Photos shows when a picture was taken and
+what took it, and no coordinates anywhere. Worth knowing, though: the
+coordinates are still inside the photo itself, as your phone wrote them, so
+anyone who downloads the original file has them. Strip them before sharing if
+that matters to you.
+
+### Search
+
+The magnifying glass in the sidebar searches **across every group you are in**
+at once, with a Files / Videos / Music / Photos view each.
+
+A file that two groups both have is **one result**, not two — identity is the
+content, not the path. Each result says which group it came from, or how many.
+
+An operator can keep a group out of Search. It is a tidiness setting and
+protects nothing: members still see everything by opening the group.
+
+### Transfers
+
+The transfers panel shows what is moving. Downloads queue, run, pause and
+resume.
+
+- The node runs a limited number at once, and each member has their own share
+ so nobody can take the machine. Queued transfers say what they are waiting
+ for.
+- **Pausing needs a download folder** chosen in Settings → Downloads. Without
+ one, your browser decides where files go and a transfer cannot be resumed.
+- **Browsing is never queued.** Posters, thumbnails, opening a photo or a
+ document to look at it — none of it takes a transfer slot. A busy group
+ browses exactly like an idle one.
+
+### Casting to a TV
+
+**Desktop application only.** A film playing in the application can be sent to a
+cast-capable TV or dongle on the same network: *Cast to device* in the player,
+pick one from the list.
+
+The application decrypts the film and relays it to the TV itself, over your LAN.
+The TV is not a group member and holds no key — which is also why the relay's
+ports have to be reachable on the local network (§10).
+
+Subtitles travel with it. Other TV protocols — the UPnP/DLNA family — are
+designed and not built.
+
+### Notifications and settings worth knowing
+
+**Settings → Downloads** — save automatically to a folder, or ask every time.
+**Settings → Defaults** — which tab a group opens on, how many items per page.
+**Settings → Appearance** — theme and language (ten languages ship).
+**Group menu → Mute** — stop notifications for one group.
+
+---
+
+## 6. Running a node
+
+This section is for the person whose machine holds the files.
+
+### What you decide
+
+Your node, not the hub, decides who gets served and what gets served — from a
+list you build yourself with invitation codes. Two things follow from that:
+
+- The hub can tell somebody your group exists and point them at your machine,
+ but it cannot get them let in. Only your invitation does that.
+- **Your own browser counts as a stranger until you pair it**
+ (`meshbay-node operator pair`). Until then it cannot invite anyone or delete
+ anything — which is a little surprising the first time, and is what keeps the
+ decision yours.
+
+### Two ways in
+
+| | |
+|---|---|
+| **The CLI**, over SSH | no browser needed, and it works while the daemon is stopped |
+| **A paired browser** | the group's Settings and Members tabs, and the Node page in the desktop application |
+
+On a headless server the CLI is the only path, and it covers everything you
+need to run a group. One gap is known: removing **one** device of one member is
+only doable from the interface (§7). Start with:
+
+```bash
+meshbay-node status
+```
+
+It reports what is configured, what is running, and — when something is
+missing — the command that fixes it.
+
+### Directories (roots)
+
+A group's content is a set of **named directories**. The name members see is
+the directory's own basename, fixed when you add it.
+
+```bash
+meshbay-node root list
+meshbay-node root add /mnt/library/films --name Films
+meshbay-node root add /mnt/usb/archive --removable
+meshbay-node root set Films --writable
+meshbay-node root remove Films
+```
+
+Rules that will bite you if you do not know them:
+
+- **Two roots cannot share a basename**, even on different drives. `/mnt/a/Films`
+ and `/mnt/b/Films` is refused; name one of them explicitly.
+- **No root inside another.** The same bytes would be indexed twice under two
+ identities.
+- **Renaming a root rewrites every path under it**, so it is an explicit act,
+ not a cosmetic one.
+- The first directory of a new group is **read-write**; every root added later
+ is **read-only** unless you say otherwise.
+
+### Read-only and read-write
+
+A read-only directory is read-only **for everybody, including you**. That is
+intentional: a library you have published as read-only should stay as you
+arranged it, and an accidental drag-and-drop from your own browser is as
+unwelcome as anyone else's.
+
+Whether uploads are allowed is decided per directory and nowhere else. A group
+can have several directories open for uploads, or none at all — a group nobody
+can add to is a perfectly normal thing to want.
+
+### External drives
+
+Mark a root `--removable` and you get **Eject** and **Plug in** beside it.
+
+Eject before you unplug. It stops watching that directory and freezes its files
+in the index — they stay listed and are reported as unavailable, rather than
+looking to every member as though you deleted your library. Plug in checks the
+path is really back, then rescans.
+
+If a removable root's path disappears without an eject, the node ejects it for
+you. Nothing is deleted: index entries, thumbnails, metadata and chat history
+referring to those files all survive.
+
+Remember the systemd drop-in for anything outside your home directory — both
+`ReadWritePaths` and `RequiresMountsFor`. It is in
+[`QUICKSTART.md` Step 5](QUICKSTART.md#step-5--start-the-node).
+
+### Indexing
+
+The node watches its directories and re-checks them periodically — the periodic
+pass is not a backstop, it is required, because filesystem events are dropped
+under load and are unreliable on network and FUSE mounts.
+
+Files over 40 MB are identified by reading their beginning, end and middle
+rather than the whole file. A 4 TB library does not need 4 TB of reading to be
+catalogued.
+
+Group → Settings has the re-check interval and how long the node waits after a
+file changes before indexing it. Members see indexing progress while it runs.
+
+One thing that surprises people: **two identical files in one group are one
+entry**, because a file is identified by its content. A scan that reports ten
+files and indexes nine has not lost anything.
+
+### Applications and their folders
+
+Group → **Members → Applications** turns applications on and off for everyone.
+Files and Settings always stay.
+
+Group → **Settings** points each application at folders:
+
+- **Videos**, **Music** and **Photos** — as many folders as you like for each.
+ A library is rarely in one place, so pick every folder that belongs to it and
+ leave out the rest. Each tab stays empty until at least one is chosen.
+- **Chat** — one folder, for attachments. It has to be one members can write
+ to.
+
+This is not a personal view: it decides what **everyone** in the group sees in
+that tab. The change is signed by your paired browser and reaches connected
+members straight away.
+
+### Metadata from third parties
+
+**Videos** uses a film and TV database for posters, summaries and cast. It ships
+with a default credential and you can set your own; the language is a
+group-wide setting, because there is one shared cache rather than a request per
+viewer. Turn it off and Videos falls back to thumbnails and cleaned filenames,
+with no outside request at all.
+
+**Music** uses a music database for cover art and canonical spelling when a
+track carries none. It needs no credential.
+
+Both lookups are made **by the node**, once per title, for everybody. Your
+members' devices never talk to a third party.
+
+### Node settings
+
+Group Settings covers a group; **Node settings** covers the machine. From the
+desktop application's Node page, or by editing `~/.config/meshbay/node.toml`
+and running `meshbay-node reload`:
+
+| Setting | Default | What it decides |
+|---|---|---|
+| `invite_ttl_hours` | 168 (7 days) | how long an invitation stays usable |
+| `pair_ttl_hours` | 24 | how long an operator pairing code lasts |
+| `device_request_ttl_minutes` | 60 | how long a device request waits for approval |
+| `max_concurrent_streams` | 8 | how many people can watch video at once |
+| `max_concurrent_downloads` | 8 | node-wide download slots |
+| `max_concurrent_uploads` | 8 | node-wide upload slots |
+| `max_upload_gb` | 8 | the largest single file a member may send you |
+| `transcode_incompatible_video` | true | re-encode video no browser can play |
+| `hardware_video_encode` | true | use the GPU for that, if one actually works |
+
+`max_concurrent_streams` is the one to think about: a stream costs a video
+process for as long as the film lasts, so this counts **viewers**, not
+requests.
+
+`max_upload_gb` is the only one of these about your disk rather than this
+machine's work. Fractions are allowed — `0.5` is 512 MB — and the new ceiling
+applies to an upload already in progress, so raising it unblocks a file that
+was about to be refused. There is no total quota behind it: a member who can
+write to a folder can still fill the disk one capped file at a time.
+
+Turning transcoding off does two different things depending on the video: one
+your viewers' browsers can decode by themselves plays as it is, and one they
+cannot is refused with a message pointing at this setting.
+
+Changes are saved to the database for immediate effect **and** written back
+into `node.toml` so they survive a reinstall. Your hand-written comments in
+that file are left alone.
+
+---
+
+## 7. Managing people
+
+### Inviting
+
+```bash
+meshbay-node member invite alice_dupont
+```
+
+Or the group's **Members** tab, from a paired browser.
+
+They need an account on the same hub first. The invitation registers their
+membership on the hub and produces a code that never goes near it. Send the
+code out of band; they enter it the first time they open the group. You do not
+need to be online then.
+
+### Seeing who is in
+
+```bash
+meshbay-node member list
+```
+
+Names, roles, when each identity was pinned and how, plus any pending
+invitations.
+
+### Removing someone
+
+```bash
+meshbay-node member revoke alice_dupont
+meshbay-node gek rotate --group "Family Photos"
+```
+
+**Both lines, and the second one matters.** Revoking means the node stops
+handing them the group key on their next connection. It does not take back the
+key they already have — no protocol can. Rotating replaces it: every member
+still in the group gets the new one automatically, and the person you removed
+keeps the old one, which opens nothing written from now on.
+
+Anything they already downloaded stays theirs. Once a file has been copied, no
+software can reach back and take it away.
+
+If you mistype the username, `member revoke` says so rather than quietly doing
+nothing.
+
+### Unpinning, and when to use it
+
+```bash
+meshbay-node member unpin alice_dupont
+```
+
+This forgets **every device** of theirs on this node, so they can pair again
+with a new key. It is the fix for two specific situations:
+
+- Someone lost their passphrase and had no recovery key. Unpin, then send a new
+ invitation code.
+- Someone changed their passphrase while your node was offline, and their group
+ is now unreadable to them.
+
+### Devices
+
+A member's devices are managed **from the interface, not from the CLI** — their
+own *Your devices on this node* list, or your group's Members tab from a paired
+browser. A removed device is remembered as removed, so the same key cannot
+quietly reappear later.
+
+`meshbay-node member unpin <user>` is the CLI's blunt version: it removes
+**every** device that person has on this node, and they pair again from scratch.
+There is no per-device CLI verb today — noted in
+[`MESHBAY_DESIGN.md` §15.3](MESHBAY_DESIGN.md#153-open-and-why-each-is-where-it-is).
+
+### Rotating the group key
+
+```bash
+meshbay-node gek rotate --group "Family Photos"
+```
+
+Connected members get the new key with no action on their part. Anyone revoked
+keeps the old one and loses everything from that point on. Nothing already
+downloaded is affected.
+
+### The audit log
+
+Every admission, refusal, invitation and deletion is recorded on the node. The
+desktop application's Node page shows it and exports it as CSV.
+
+---
+
+## 8. What is private, and what is not
+
+**The short version: your files stay on the machine that hosts them.** They
+travel encrypted and directly to the people that machine has been told to
+serve. The hub that introduced everybody never holds them, and cannot read
+them.
+
+For most people that is the whole answer. The rest of this section is for
+deciding what belongs in a group and what is better kept elsewhere.
+
+### What you can rely on
+
+- **Nothing goes through the hub** — not your files, not their names, not even
+ the list of them. Your devices talk to the node directly.
+- **The hub cannot open your group.** It holds none of the keys, and for a
+ private group it does not know what is in it.
+- **The hub cannot let anybody in.** Only the node decides who it serves, from
+ the list its operator built with invitation codes. An account the hub added by
+ itself gets nowhere.
+- **Only your own devices can add another of your devices.** The hub cannot,
+ and neither can an operator.
+- **You get separate keys in each group you join**, so two people hosting you
+ cannot work out that you are the same person.
+- **Chat history is encrypted where it is stored.**
+- **Your client remembers each node** and refuses one whose identity has
+ changed — which is what stops a machine being quietly swapped for another.
+- **People you have already seen in a group stay checked** every time they post
+ after that.
+
+### Worth knowing before you share something
+
+- **Whoever hosts a group can read what is in it.** The files sit on their
+ machine in the ordinary way, because that is what hosting is. It is the same
+ trust you extend to a friend keeping a spare key to your flat — reasonable,
+ and worth being conscious of.
+- **Everyone in a group sees everything in it.** There is nothing below group
+ level: no per-person permissions, no private corner inside a shared library.
+ If two sets of people should not see the same things, make two groups.
+- **The desktop application protects you a little better than a browser.** A
+ browser downloads its code from the hub every time you open it; the
+ application carries its own and never asks the hub for any. For anything you
+ would rather not stake on the hub behaving, prefer the application.
+- **Your passphrase is what guards your keys.** A copy of them, locked with it,
+ sits on each machine you have joined. A long one puts that out of reach; a
+ guessable one does not. This is the single thing most worth getting right.
+- **An open group is open.** Anybody can walk into one, which is what open
+ means. Invite-only is the default, and is what you want for anything personal.
+- **The hub sees who is in which group, and when.** Never what was said or
+ shared — but the pattern of it is visible to whoever runs the hub.
+- **Deleting your account does not delete what you shared.** Files you uploaded
+ stay on the machines hosting them, and each node remembers you until its
+ operator says otherwise. Ask them if it matters.
+
+## 9. When something breaks
+
+### Start here
+
+```bash
+meshbay-node status
+journalctl --user -u meshbay-node -f
+```
+
+`status` works whether or not the daemon is running, and names the command for
+anything missing.
+
+### The common ones
+
+**"No nodes are currently online for this group."**
+The node is not running, is not reachable, or has not linked its key to the
+hub. On the node: `meshbay-node status`, then Step 4 and 5 of the quickstart.
+
+**A member sees the group but no files.**
+They never redeemed an invitation code, or the group has no key
+(`meshbay-node gek init`), or nothing has been indexed yet.
+
+**Members on other networks connect, members on your own LAN do not.**
+The firewall. A browser on your LAN has to be able to call the node, and a node
+refusing unsolicited inbound UDP cannot be called. See
+[`QUICKSTART.md` Step 9](QUICKSTART.md#step-9--let-people-reach-you).
+
+**A shared directory reads as empty and everything else looks right.**
+A mount the service cannot see. The systemd drop-in needs
+`RequiresMountsFor` as well as `ReadWritePaths` — the service has its own mount
+namespace, so a volume mounted after it started is invisible to it.
+
+**"Invitations are signed with the key this node pinned for your browser."**
+The browser is not paired. `meshbay-node operator pair`, then enter the code in
+the Members tab.
+
+**A member changed their passphrase while the node was down.**
+`meshbay-node member unpin <user>`, then a fresh invitation code.
+
+**Video says the codec is not supported.**
+The source codec has no decoder in that browser and transcoding is off, or
+ffmpeg is missing. Check `transcode_incompatible_video` and that ffmpeg is
+installed.
+
+**"Server busy" when starting a film.**
+Every streaming slot is taken. `max_concurrent_streams` counts viewers, and
+each one holds a slot for the whole film.
+
+**A download stops near the end in the browser.**
+Choose a download folder in Settings → Downloads, which switches the browser to
+writing straight to disk, and retry. The desktop application does not have this
+class of problem.
+
+**Nothing appears in Videos, Music or Photos.**
+No folders have been chosen for that application yet. Group → Settings. The tab
+says so rather than just looking empty, but it is easy to miss.
+
+### After changing config by hand
+
+```bash
+meshbay-node reload # re-read node.toml without dropping anyone
+meshbay-node restart-daemon # full restart — drops live streams
+```
+
+Prefer `reload`. A restart kills every connection, which for someone watching a
+film looks like a failure they caused.
+
+---
+
+## 10. Reference
+
+### Node commands
+
+```
+meshbay-node status what is configured and running
+meshbay-node init first-time setup
+meshbay-node reset erase all node state (destructive)
+
+meshbay-node group list|add|remove groups this node hosts
+meshbay-node root list|add|remove|set directories in a group
+meshbay-node root eject|plug removable drives
+meshbay-node gek init|rotate the group's encryption key
+
+meshbay-node operator pair authorise a browser
+meshbay-node member list|invite|revoke|unpin people
+
+meshbay-node file list|rm files, from the machine itself
+meshbay-node chat status|rotate|encrypt-history|prune
+meshbay-node transfers show|set|max-size|per-member
+ transfer limits, and the
+ largest single upload
+meshbay-node denylist show|clear
+meshbay-node stun list|add|remove|reset NAT traversal servers
+meshbay-node video rematch re-resolve video metadata
+
+meshbay-node reload re-read node.toml, hot
+meshbay-node restart-daemon full restart
+```
+
+`--group <name>` selects a group where you host more than one. `--yes` skips
+the confirmation on destructive commands. `man meshbay-node` has the full page.
+
+### Where things live
+
+| | |
+|---|---|
+| `~/.config/meshbay/node.toml` | configuration — hand-edited, commented, preserved |
+| `~/.config/meshbay/node.env` | environment: keystore unlock, third-party tokens |
+| `~/.config/meshbay/keystore.enc` | the node's own keys. **Back this up.** |
+| `~/.config/meshbay/unlock.key` | what opens the keystore. Mode 0600. |
+| `~/.local/share/meshbay/` | roster, indexes, chat, caches, thumbnails |
+| `/opt/meshbay-common/venv/` | the shared Python environment |
+
+Losing the keystore means a new node identity: every group has to be re-linked
+and every member re-admitted. It is small — back it up somewhere safe.
+
+### Ports
+
+| | |
+|---|---|
+| **inbound UDP, ephemeral** | peer connections. Scope the rule to your LAN. |
+| **127.0.0.1:18000** | the node's own control API. Loopback only, behind a per-run token, never exposed. |
+| **TCP 19550-19553, UDP 5353** | casting to a TV, desktop application only, LAN only |
+
+### Defaults worth remembering
+
+| | |
+|---|---|
+| Invitation code | 7 days, single use, one account, one group |
+| Operator pairing code | 24 hours |
+| Device linking code | 1 hour |
+| Devices per account per node | 5 |
+| Upload size limit | 8 GB per file, settable on the node |
+| Transfers at once | 8 node-wide, 2 per member per group |
+| Video streams at once | 8 |
+
+---
+
+## 11. Running your own hub
+
+You do not need to. meshbay.org exists, is free, and sees nothing of your
+content. Run your own if you want nobody else holding your account list, or you
+want a hub that is not reachable from the internet at all.
+
+A hub is a Linux server with PostgreSQL, a reverse proxy for HTTPS, and
+somewhere to send mail from. The steps are in:
+
+- [`PACKAGING-GUIDE.md`](PACKAGING-GUIDE.md) — install, database, configuration, first start
+- [`HTTPS.md`](HTTPS.md) — the reverse proxy and certificates
+- [`MAIL-SERVER.md`](MAIL-SERVER.md) — outgoing mail, which sign-up and recovery need
+
+Two things to know before you start. **Your hub serves the web application**,
+so anyone using a browser against it is trusting you with their keys — the
+same relationship you have with meshbay.org, pointed at you. And **a hub cannot
+be moved**: accounts, group registrations and node links are all on it.
+
+---
+
+## 12. What this software does not do yet
+
+Better to know now than to go looking for it:
+
+- **Packages are not signed**, and there is no signed repository yet, so there
+ is nothing to check a download against and no updates through your
+ distribution. Until that ships, take them from the download page and from
+ nowhere else.
+- **There is no Android client.** A phone browser works.
+- **A node cannot be hosted on Android**, and is not planned to be.
+- **Hubs do not talk to each other yet.** Everyone in a group needs an account
+ on the same hub.
+- **Casting reaches Chromecast devices** from the desktop application. Support
+ for other TV protocols is designed but not built.
+- **Some subtitle tracks cannot be shown** — the ones stored as images rather
+ than text, roughly one embedded track in five. Displaying them would need
+ text recognition.
+- **There is no overall storage quota.** Files are capped at 8 GB each — lower
+ or raise it on the node, above — but somebody can still fill a disk one file
+ at a time. Worth a glance now and then if you have opened a folder to people
+ you do not know well.
+- **Searching the film database by hand has no per-member limit**, and it draws
+ on the node's own quota. Only likely to matter on a large group.
+- **The record of who uploaded what belongs to the node.** It is kept and it is
+ accurate, but other members have no way to check it for themselves.
+- **Chat history stays readable to everyone in the group**, including people
+ who join later. That is what makes it a shared history rather than
+ disappearing messages — but if a conversation should leave no trace, it does
+ not belong here.
+- **Your client remembers each node's identity, but not the hub's.** It matters
+ less than it sounds: a stand-in hub still cannot read anything, and cannot
+ touch the desktop application at all.