1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
# MeshBay — Packaging
Four packages, installed under `/opt/<PACKAGE_NAME>/`:
| Package | RPM spec | DEB control | Description |
|---|---|---|---|
| `meshbay-common` | `rpm/meshbay-common.spec` | `deb/meshbay-common/` | Shared Python venv + all pip deps |
| `meshbay-hub` | `rpm/meshbay-hub.spec` | `deb/meshbay-hub/` | Hub server (FastAPI + PostgreSQL) |
| `meshbay-node` | `rpm/meshbay-node.spec` | `deb/meshbay-node/` | Node daemon + local UI |
| `meshbay-client` | `rpm/meshbay-client.spec` | `deb/meshbay-client/` | Electron desktop app |
## Building packages
```bash
# On Ubuntu 26.04:
bash packaging/build/build-packages.sh deb
# On Fedora 44 (local):
bash packaging/build/build-packages.sh rpm
# On Raspberry Pi (arm64, deferred):
bash packaging/build/build-packages.sh deb --arm64
```
Output: `meshbay-{common,hub,node,client}_<version>_{arch}.{deb,rpm}` in
`/tmp/meshbay-build/out/` (override with `MESHBAY_BUILD_DIR`). The version comes
from each package's `pyproject.toml`.
## Install order
Always install `meshbay-common` first (owns the shared Python venv).
```bash
# Ubuntu/Debian
sudo dpkg -i meshbay-common_*.deb
sudo dpkg -i meshbay-hub_*.deb meshbay-node_*.deb
sudo dpkg -i meshbay-client_*.deb
# Fedora
sudo rpm -ivh meshbay-common-*.rpm
sudo rpm -ivh meshbay-hub-*.rpm meshbay-node-*.rpm
sudo rpm -ivh meshbay-client-*.rpm
```
### Hardware video acceleration is a weak dependency
Both the node and the client use the GPU for video where one works — the node to
re-encode a browser-hostile codec (`hwaccel.py`: VA-API on Linux, Quick Sync or
NVENC on Windows), the client to decode H.264 (`main.js`; only on Linux, since
Chromium already decodes in hardware on Windows and macOS). Neither requires it,
and both establish it by measurement rather than by assuming the packaged driver
is present and functional, so a machine without a GPU installs and runs exactly
as before.
Only Linux needs packaged drivers — on Windows the bundled ffmpeg carries its
own encoders and Chromium uses the OS's decoder. The drivers are therefore
`Recommends` (`va-driver-all` on Debian/Ubuntu;
`libva`, `intel-media-driver`, `mesa-va-drivers` on Fedora), which matters for
how the packages are installed: **`dpkg -i` and `rpm -ivh` both ignore weak
dependencies; `apt` and `dnf` install them.** On a low-power machine — an Intel
mini-PC serving and watching video is the case this exists for — use the
resolver, so the drivers come too:
```bash
sudo apt install ./meshbay-common_*.deb ./meshbay-node_*.deb ./meshbay-client_*.deb
sudo dnf install ./meshbay-common-*.rpm ./meshbay-node-*.rpm ./meshbay-client-*.rpm
```
Nothing has to be enabled afterwards and there are no flags to set by hand. The
node logs which encoder it found at the first stream (`hwaccel:`), and the
client records what it measured in its own `config.json` — the file that
already remembers the hub address — under `videoDecodeProbe`, and
`videoAcceleration: "off"` in that same file turns it back off.
## Architecture
meshbay-common owns the shared Python virtual environment at
`/opt/meshbay-common/venv/`. Hub and node install their code into that venv.
The client is a standalone Electron app at `/opt/meshbay-client/`.
No pip runs at install time — everything is pre-built.
## Post-install (hub)
```bash
sudo cp /etc/meshbay/hub.toml.example /etc/meshbay/hub.toml
sudo nano /etc/meshbay/hub.toml
sudo systemctl enable --now meshbay-hub
```
The hub generates its own Ed25519 keypair on first start if
`identity.private_key_path` does not exist yet (`app.py`, lifespan), so there is
no key-generation step to run by hand.
## Post-install (node)
```bash
meshbay-node init # copies default.env (with TMDB token)
nano ~/.config/meshbay/node.toml
systemctl --user enable --now meshbay-node
```
## Firewall
Two passive profiles: packaged, not activated.
**LAN casting (client)** — the Electron client runs an HTTP relay on
**TCP 19550-19553** to stream decrypted video to Chromecast / Smart TV devices
on the local network. Discovery uses **mDNS (UDP 5353)**.
**Peer connections (node)** — WebRTC binds an ephemeral UDP port per
connection, so there is no fixed port: the node profile opens **UDP
1024-65535** and must be scoped to the LAN. A node that refuses unsolicited
inbound UDP is unreachable from every browser it cannot itself call back,
because browsers publish an mDNS `.local` candidate the node cannot resolve.
See `docs/PACKAGING-GUIDE.md` for the reasoning.
### Fedora / RHEL (firewalld)
Pre-installed service files at `/usr/lib/firewalld/services/`:
```bash
sudo firewall-cmd --permanent --add-service=meshbay-cast
sudo firewall-cmd --permanent --zone=<your LAN zone> --add-service=meshbay-node
sudo firewall-cmd --reload
```
### Ubuntu / Debian (ufw)
Pre-installed application profile at `/etc/ufw/applications.d/meshbay`:
```bash
sudo ufw allow "MeshBay Cast"
sudo ufw allow from 192.168.0.0/16 app "MeshBay Node" # your LAN, not "anywhere"
```
## Systemd units
| File | Location |
|---|---|
| `systemd/meshbay-hub.service` | `/usr/lib/systemd/system/meshbay-hub.service` |
| `systemd/meshbay-node.service` | `/usr/lib/systemd/system/meshbay-node@.service` |
| `systemd/meshbay-node-user.service` | `/usr/lib/systemd/user/meshbay-node.service` |
The node user service is enabled by the person themselves:
```bash
systemctl --user enable --now meshbay-node
loginctl enable-linger $USER # keep serving when logged out
```
## Desktop launcher
The client installs a Gnome `.desktop` file at
`/usr/share/applications/meshbay.desktop` — the application appears as
**"MeshBay"** in the applications menu.
|