2026-09-18 00:03:54 -04:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Local throwaway Synapse for driving the real UI (Playwright / a browser)
|
|
|
|
|
# against a homeserver you control — open registration, no rate limits,
|
|
|
|
|
# SQLite, media served. Everything lives in .dev-homeserver/ (gitignored).
|
|
|
|
|
#
|
|
|
|
|
# scripts/dev-homeserver.sh start # install (first run) + start on :8008
|
2026-09-18 22:29:47 -04:00
|
|
|
# scripts/dev-homeserver.sh calls # + LiveKit SFU, JWT issuer, voice-limit guard, https well-known
|
2026-09-18 00:03:54 -04:00
|
|
|
# scripts/dev-homeserver.sh stop
|
|
|
|
|
# scripts/dev-homeserver.sh reset # wipe the database and media
|
2026-09-18 22:29:47 -04:00
|
|
|
# python3 scripts/dev-seed.py 400 # alice/bob + "Busy Room" with images (+ "Voice Lounge" call room)
|
|
|
|
|
#
|
|
|
|
|
# Calls: `calls` downloads livekit-server (v1.13.7 release binary) into
|
|
|
|
|
# .dev-homeserver/, runs it on :7880 with key devkey, a minimal lk-jwt-service
|
|
|
|
|
# clone (scripts/dev-lk-jwt.py, :8071), the real voice-limit-guard from the
|
|
|
|
|
# sibling matrix repo in front of it (:8070, needs ../matrix checked out and an
|
|
|
|
|
# admin user — dev-seed makes alice one), and a self-signed https server on
|
|
|
|
|
# :443 serving /.well-known/matrix/client with the LiveKit focus (the client
|
|
|
|
|
# autodiscovers the server NAME, not :8008). Drive it with Playwright using
|
|
|
|
|
# --use-fake-device-for-media-stream and ignoreHTTPSErrors.
|
2026-09-18 00:03:54 -04:00
|
|
|
#
|
|
|
|
|
# Then `npm start` and log in at http://127.0.0.1:5173/login/http%3A%2F%2Flocalhost%3A8008/
|
|
|
|
|
# as alice / password123.
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
|
|
|
DIR="$ROOT/.dev-homeserver"
|
|
|
|
|
VENV="$DIR/venv"
|
|
|
|
|
CFG="$DIR/homeserver.yaml"
|
|
|
|
|
PIDFILE="$DIR/synapse.pid"
|
|
|
|
|
|
|
|
|
|
install() {
|
|
|
|
|
mkdir -p "$DIR"
|
|
|
|
|
if [ ! -x "$VENV/bin/python" ]; then
|
|
|
|
|
python3 -m venv --without-pip "$VENV"
|
|
|
|
|
curl -sS https://bootstrap.pypa.io/get-pip.py -o "$DIR/get-pip.py"
|
|
|
|
|
"$VENV/bin/python" "$DIR/get-pip.py" -q
|
2026-09-18 19:11:30 -04:00
|
|
|
"$VENV/bin/pip" install -q "matrix-synapse[url-preview]"
|
2026-09-18 00:03:54 -04:00
|
|
|
fi
|
|
|
|
|
if [ ! -f "$CFG" ]; then
|
|
|
|
|
(cd "$DIR" && "$VENV/bin/python" -m synapse.app.homeserver \
|
|
|
|
|
--server-name localhost --config-path homeserver.yaml --generate-config --report-stats=no >/dev/null)
|
|
|
|
|
# the generated listener only serves `client`; add media + open the door
|
|
|
|
|
python3 - "$CFG" <<'PY'
|
|
|
|
|
import sys, re
|
|
|
|
|
p = sys.argv[1]; s = open(p).read()
|
2026-09-18 22:29:47 -04:00
|
|
|
s = s.replace(" - client\n", " - client\n - media\n - federation\n", 1)
|
2026-09-18 00:03:54 -04:00
|
|
|
s += """
|
|
|
|
|
enable_registration: true
|
|
|
|
|
enable_registration_without_verification: true
|
|
|
|
|
rc_message: { per_second: 1000, burst_count: 10000 }
|
|
|
|
|
rc_registration: { per_second: 1000, burst_count: 10000 }
|
|
|
|
|
rc_login: { address: { per_second: 1000, burst_count: 10000 }, account: { per_second: 1000, burst_count: 10000 }, failed_attempts: { per_second: 1000, burst_count: 10000 } }
|
|
|
|
|
rc_joins: { local: { per_second: 1000, burst_count: 10000 }, remote: { per_second: 1000, burst_count: 10000 } }
|
|
|
|
|
rc_presence: { per_user: { per_second: 1000, burst_count: 10000 } }
|
|
|
|
|
max_upload_size: 50M
|
|
|
|
|
suppress_key_server_warning: true
|
2026-09-18 19:11:30 -04:00
|
|
|
# URL previews (embed facades use the homeserver's cached thumbnail)
|
|
|
|
|
url_preview_enabled: true
|
|
|
|
|
url_preview_ip_range_blacklist: ['127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '100.64.0.0/10', '169.254.0.0/16', '::1/128', 'fe80::/10', 'fc00::/7']
|
2026-09-18 22:29:47 -04:00
|
|
|
# scheduled messages (MSC4140 delayed events) + MatrixRTC transports (MSC4143) + room summaries (MSC3266)
|
2026-09-18 18:26:15 -04:00
|
|
|
max_event_delay_duration: 24h
|
|
|
|
|
experimental_features:
|
|
|
|
|
msc4140_enabled: true
|
2026-09-18 22:29:47 -04:00
|
|
|
msc4143_enabled: true
|
|
|
|
|
msc3266_enabled: true
|
|
|
|
|
matrix_rtc:
|
|
|
|
|
transports:
|
|
|
|
|
- type: livekit
|
|
|
|
|
livekit_service_url: http://127.0.0.1:8070
|
2026-09-18 00:03:54 -04:00
|
|
|
"""
|
|
|
|
|
open(p, "w").write(s)
|
|
|
|
|
PY
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start() {
|
|
|
|
|
install
|
|
|
|
|
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
|
|
|
|
echo "already running (pid $(cat "$PIDFILE"))"; return
|
|
|
|
|
fi
|
|
|
|
|
(cd "$DIR" && setsid nohup "$VENV/bin/python" -m synapse.app.homeserver --config-path homeserver.yaml \
|
|
|
|
|
> synapse.log 2>&1 < /dev/null & echo $! > "$PIDFILE")
|
|
|
|
|
for _ in $(seq 1 40); do
|
|
|
|
|
sleep 1
|
|
|
|
|
curl -sf http://127.0.0.1:8008/_matrix/client/versions >/dev/null && { echo "synapse up on http://localhost:8008"; return; }
|
|
|
|
|
done
|
|
|
|
|
echo "synapse did not come up — see $DIR/synapse.log" >&2; exit 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stop() {
|
|
|
|
|
if [ -f "$PIDFILE" ]; then kill "$(cat "$PIDFILE")" 2>/dev/null || true; rm -f "$PIDFILE"; echo stopped; fi
|
2026-09-18 22:29:47 -04:00
|
|
|
for f in "$DIR"/calls-*.pid; do [ -f "$f" ] && { kill "$(cat "$f")" 2>/dev/null || true; rm -f "$f"; }; done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calls() {
|
|
|
|
|
start
|
|
|
|
|
local LK="$DIR/livekit-server"
|
|
|
|
|
if [ ! -x "$LK" ]; then
|
|
|
|
|
curl -sL "https://github.com/livekit/livekit/releases/download/v1.13.7/livekit_1.13.7_linux_amd64.tar.gz" | tar xz -C "$DIR" livekit-server
|
|
|
|
|
fi
|
|
|
|
|
cat > "$DIR/livekit.yaml" <<'YAML'
|
|
|
|
|
port: 7880
|
|
|
|
|
bind_addresses: ["127.0.0.1"]
|
|
|
|
|
rtc:
|
|
|
|
|
tcp_port: 7881
|
|
|
|
|
port_range_start: 50000
|
|
|
|
|
port_range_end: 50100
|
|
|
|
|
use_external_ip: false
|
|
|
|
|
node_ip: 127.0.0.1
|
|
|
|
|
keys:
|
|
|
|
|
devkey: devsecretdevsecretdevsecretdevsecret
|
|
|
|
|
room:
|
|
|
|
|
auto_create: true
|
|
|
|
|
YAML
|
|
|
|
|
local bg
|
|
|
|
|
bg() { local name="$1"; shift; ( setsid nohup "$@" > "$DIR/calls-$name.log" 2>&1 < /dev/null & echo $! > "$DIR/calls-$name.pid" ); }
|
|
|
|
|
bg livekit "$LK" --config "$DIR/livekit.yaml"
|
|
|
|
|
bg jwt env PORT=8071 LIVEKIT_KEY=devkey LIVEKIT_SECRET=devsecretdevsecretdevsecretdevsecret LIVEKIT_URL=ws://127.0.0.1:7880 SYNAPSE_API=http://127.0.0.1:8008 python3 "$ROOT/scripts/dev-lk-jwt.py"
|
|
|
|
|
if [ ! -f "$DIR/wk.crt" ]; then openssl req -x509 -newkey rsa:2048 -nodes -keyout "$DIR/wk.key" -out "$DIR/wk.crt" -days 365 -subj "/CN=localhost" 2>/dev/null; fi
|
|
|
|
|
bg wellknown env CERT="$DIR/wk.crt" KEY="$DIR/wk.key" python3 "$ROOT/scripts/dev-wellknown.py"
|
|
|
|
|
local GUARD="$ROOT/../matrix/livekit/voice-limit-guard.py"
|
|
|
|
|
local TOKEN_FILE="$DIR/admin.token"
|
|
|
|
|
if [ -f "$GUARD" ] && [ -f "$TOKEN_FILE" ]; then
|
|
|
|
|
bg guard env GUARD_BIND_HOST=127.0.0.1 GUARD_BIND_PORT=8070 GUARD_UPSTREAM=http://127.0.0.1:8071 LIVEKIT_API=http://127.0.0.1:7880 LIVEKIT_KEY=devkey LIVEKIT_SECRET=devsecretdevsecretdevsecretdevsecret SYNAPSE_API=http://127.0.0.1:8008 MATRIX_TOKEN="$(cat "$TOKEN_FILE")" python3 "$GUARD"
|
|
|
|
|
else
|
|
|
|
|
echo "voice-limit-guard not started (need ../matrix checkout and $TOKEN_FILE from dev-seed.py); pointing the focus straight at the issuer"
|
|
|
|
|
bg guard env PORT=8070 LIVEKIT_KEY=devkey LIVEKIT_SECRET=devsecretdevsecretdevsecretdevsecret LIVEKIT_URL=ws://127.0.0.1:7880 SYNAPSE_API=http://127.0.0.1:8008 python3 "$ROOT/scripts/dev-lk-jwt.py"
|
|
|
|
|
fi
|
|
|
|
|
sleep 2
|
|
|
|
|
echo "calls stack up: livekit :7880, jwt :8071, guard :8070, well-known https://localhost/.well-known/matrix/client"
|
2026-09-18 00:03:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "${1:-}" in
|
|
|
|
|
start) start ;;
|
2026-09-18 22:29:47 -04:00
|
|
|
calls) calls ;;
|
2026-09-18 00:03:54 -04:00
|
|
|
stop) stop ;;
|
|
|
|
|
reset) stop; rm -f "$DIR"/homeserver.db* ; rm -rf "$DIR/media_store"; echo "database wiped"; ;;
|
|
|
|
|
*) echo "usage: $0 start|stop|reset" >&2; exit 2 ;;
|
|
|
|
|
esac
|