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
|
#!/usr/bin/env bash
# Build the meshbay-node package: node-specific files on top of meshbay-common.
#
# Expects build-common.sh to have run first (shared venv exists in staging).
#
# Usage: ./build-node.sh [staging-dir]
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
STAGING="${1:-/tmp/meshbay-build}"
COMMON_ROOT="$STAGING/meshbay-common-root"
ROOT="$STAGING/meshbay-node-root"
VENV="$COMMON_ROOT/opt/meshbay-common/venv"
PYVER=$(basename "$VENV"/lib/python3.*)
SITE="$VENV/lib/$PYVER/site-packages"
VERSION=$(python3 -c "
import tomllib, pathlib
p = pathlib.Path('$REPO/packages/meshbay-node/pyproject.toml')
print(tomllib.loads(p.read_text())['project']['version'])
")
echo "==> meshbay-node $VERSION"
[ -d "$VENV" ] || { echo "!! shared venv not found — run build-common.sh first" >&2; exit 1; }
rm -rf "$ROOT"
mkdir -p "$ROOT"
# --- Node code in the shared venv (owned by meshbay-node package) ---------
# MOVE (not copy) from the common staging tree — same rationale as build-hub.sh.
NODE_ROOT="$ROOT/opt/meshbay-common/venv/lib/$PYVER/site-packages"
mkdir -p "$NODE_ROOT"
mv "$SITE/meshbay_node" "$NODE_ROOT/"
mv "$SITE"/meshbay_node-*.dist-info "$NODE_ROOT/"
# Entry point
mkdir -p "$ROOT/opt/meshbay-common/venv/bin"
mv "$VENV/bin/meshbay-node" "$ROOT/opt/meshbay-common/venv/bin/"
# Symlink in PATH
mkdir -p "$ROOT/usr/bin"
ln -sf /opt/meshbay-common/venv/bin/meshbay-node "$ROOT/usr/bin/meshbay-node"
# --- Node-specific assets -------------------------------------------------
mkdir -p "$ROOT/opt/meshbay-node/share"
# Default env with the shared TMDB token, read at build time and copied to
# <config>/node.env by `meshbay-node init`.
#
# tmdb.py sends `Authorization: Bearer`, so this is the v4 *read access token*
# (a JWT, "eyJ..."), not the 32-char v3 API key that sits beside it in the same
# note file. Sources, in order: an explicit variable, an explicit file, the
# KEY=VALUE form, then QE/tmdb.txt -- which is free-form prose, so the token is
# matched by shape rather than by a label.
extract_tmdb_token() {
local file="$1" tok=""
[ -f "$file" ] || return 0
tok=$(sed -n 's/^[[:space:]]*MESHBAY_TMDB_DEFAULT_TOKEN[[:space:]]*=[[:space:]]*//p' \
"$file" | head -1)
[ -n "$tok" ] || tok=$(grep -oE '^eyJ[A-Za-z0-9._-]{40,}$' "$file" | head -1 || true)
printf '%s' "$tok" | tr -d '"'"'"'\r'
}
TMDB_TOKEN="${MESHBAY_TMDB_TOKEN:-}"
if [ -z "$TMDB_TOKEN" ] && [ -n "${MESHBAY_TMDB_TOKEN_FILE:-}" ]; then
TMDB_TOKEN=$(extract_tmdb_token "$MESHBAY_TMDB_TOKEN_FILE")
fi
[ -n "$TMDB_TOKEN" ] || TMDB_TOKEN=$(extract_tmdb_token "$REPO/QE/node.env")
[ -n "$TMDB_TOKEN" ] || TMDB_TOKEN=$(extract_tmdb_token "$REPO/QE/tmdb.txt")
if [ -n "$TMDB_TOKEN" ]; then
cat > "$ROOT/opt/meshbay-node/share/default.env" <<EOF
# Default environment for meshbay-node.
# Copied to <config>/node.env by 'meshbay-node init' if it does not exist.
# The operator may override any value there or in the systemd EnvironmentFile.
# TMDB API token for the Videos app (read-only, shared across installations)
MESHBAY_TMDB_DEFAULT_TOKEN=$TMDB_TOKEN
EOF
chmod 600 "$ROOT/opt/meshbay-node/share/default.env"
echo " TMDB token baked into default.env (${#TMDB_TOKEN} chars)"
elif [ "${MESHBAY_ALLOW_NO_TMDB:-0}" = "1" ]; then
echo " !! no TMDB token; default.env left empty (MESHBAY_ALLOW_NO_TMDB=1)" >&2
: > "$ROOT/opt/meshbay-node/share/default.env"
else
# Failing here is deliberate: an empty default.env is invisible until a user
# opens the Videos app and finds no metadata, which is exactly how this
# shipped empty on two platforms at once.
echo "!! TMDB token not found. Looked at:" >&2
echo " \$MESHBAY_TMDB_TOKEN, \$MESHBAY_TMDB_TOKEN_FILE," >&2
echo " $REPO/QE/node.env, $REPO/QE/tmdb.txt" >&2
echo " Set MESHBAY_ALLOW_NO_TMDB=1 to build without it." >&2
exit 1
fi
# --- Systemd units --------------------------------------------------------
mkdir -p "$ROOT/usr/lib/systemd/system"
mkdir -p "$ROOT/usr/lib/systemd/user"
cp "$REPO/packaging/systemd/meshbay-node.service" \
"$ROOT/usr/lib/systemd/system/meshbay-node@.service"
cp "$REPO/packaging/systemd/meshbay-node-user.service" \
"$ROOT/usr/lib/systemd/user/meshbay-node.service"
# --- Firewall profiles --------------------------------------------------------
# The node's admin surface is a loopback API (127.0.0.1, token-gated) and is
# never firewall-exposed. Its *peer* traffic is: WebRTC binds an ephemeral UDP
# port per connection, and a peer that publishes an unroutable address — every
# browser does, as an mDNS .local name aioice cannot resolve — can only be
# reached if it calls the node. A node refusing unsolicited inbound UDP is
# therefore unreachable from browsers on its own LAN. Both profiles are passive:
# packaged, not activated, and meant to be scoped to a LAN zone/source.
mkdir -p "$ROOT/etc/ufw/applications.d"
cp "$REPO/packaging/firewall/ufw/meshbay" \
"$ROOT/etc/ufw/applications.d/"
mkdir -p "$ROOT/usr/lib/firewalld/services"
cp "$REPO/packaging/firewall/firewalld/meshbay-node.xml" \
"$ROOT/usr/lib/firewalld/services/"
echo "==> meshbay-node staging ready at $ROOT"
|