- TypeScript 77.1%
- Svelte 9.2%
- Rust 6.5%
- Shell 4.4%
- JavaScript 1.2%
- Other 1.6%
| backend | ||
| docs | ||
| frontend | ||
| infra | ||
| mk | ||
| .dockerignore | ||
| .gitignore | ||
| AGENTS.md | ||
| current-hypotheses.md | ||
| Dockerfile | ||
| failed-approaches.md | ||
| README.md | ||
Web 1:1 video chat
Single-VPS deployment
This repo builds a mobile-friendly, link-based WebRTC chat where one Rust container serves the SPA, signaling API, room/token endpoints, and an embedded turn-rs relay on the same VPS. Nginx only terminates HTTPS for the web app and websocket traffic.
1. Prerequisites
- One Ubuntu 24.04+ VPS (minimum 2 vCPU, 4 GiB RAM) with ports
22,80,443,3478/udp, and3478/tcpopen in your firewall. - One DNS A record pointing your chat domain (for example
meet.example.com) to the VPS IP. - Docker, Node 20+, Rust 1.83+ toolchain, and Certbot with the
nginxplugin installed.
2. Obtain TLS certificates via Certbot
sudo certbot --nginx -d meet.example.com
Certbot writes /etc/letsencrypt/live/meet.example.com/{fullchain.pem,privkey.pem} and nginx reuses those files directly.
3. Build the frontend + backend
cd frontend
npm ci
npm run build
frontend/scripts/postbuild.mjs copies the Svelte build output into frontend/public and backend/public so Axum can serve the SPA from the Rust binary.
cd ../backend
cargo build --release
4. Configure and launch the container
The repo already includes ./mk/run and ./mk/deploy-single-vps. For a single-VPS deployment, the container publishes:
- your HTTP app port, for example
8011 -> 8000 - embedded TURN on
3478/udp - optional TURN-over-TCP on
3478/tcp(enabled by default)
If you prefer a systemd wrapper around ./mk/run, use something like:
[Unit]
Description=catchup single-vps app
After=network.target
[Service]
WorkingDirectory=/home/ubuntu/src/repos/video-chat
ExecStart=/home/ubuntu/src/repos/video-chat/mk/run 8011 single 0
Environment=TURN_SECRET=<hex from openssl rand -hex 32>
Environment=CATCHUP_RELAY_PORT=3478
Environment=CATCHUP_TURN_ENABLE_TCP=1
Restart=on-failure
[Install]
WantedBy=multi-user.target
Reload systemd and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now catchup
4.1 Runtime environment variables
TURN_SECRET: HMAC key used by the embedded relay for TURN-style credentials. Generate it once withopenssl rand -hex 32. Do not commit it.CATCHUP_RELAY_PORT: TURN bind port inside the container. Default3478.CATCHUP_TURN_ENABLE_TCP: advertise and serve TURN over TCP on the same port. Default1.TURN_HOSTS: optional comma-separated TURN hostnames advertised to the browser. For single-VPS deploys, leaving this unset is fine because the backend derives the host fromCATCHUP_DOMAIN.STUN_SERVERS: optional comma-separated STUN URLs. Defaults to empty so the app stays self-sovereign and uses only the embedded relay unless you explicitly opt into extra STUN infrastructure.
5. Deploy nginx for meet.example.com
You only need one nginx config from this repo:
infra/turn.nginxfor HTTPS web traffic to127.0.0.1:8011
5.1 HTTP reverse proxy
Copy infra/turn.nginx into /etc/nginx/sites-available/meet.example.com and symlink it into /etc/nginx/sites-enabled. Update the upstream 127.0.0.1:8011 if you deploy to a different host port.
That file does the following:
- Redirects all HTTP → HTTPS.
- Proxies all HTTP requests, including static assets, websocket upgrades, and API endpoints, to the catchup container.
- Adds HSTS headers and sane timeouts for long-lived signaling.
After installing the HTTP proxy file:
sudo nginx -t
sudo systemctl reload nginx
Verify from the VPS:
curl -I https://meet.example.com/healthz
nc -vu meet.example.com 3478
nc -vz meet.example.com 3478
6. Firewall and operations
sudo ufw allow 22
sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow 3478/udp
sudo ufw allow 3478/tcp
Renew certs with sudo certbot renew and reload nginx after renewal. If you change stream includes, always run sudo nginx -t before reloading.
7. Deploy with the existing helper
Your current deploy command stays the same:
./mk/deploy-single-vps myvpshostname 8011 0 0
That copies the repo to the VPS and runs ./mk/run 8011 single 1 remotely. After deploy, nginx should proxy HTTPS to 127.0.0.1:8011, and TURN should be reachable directly on 3478/udp (and 3478/tcp when enabled).
8. Test before and after deploy
cd frontend
npm run test:unit
cd ../backend
cargo test
Both commands certify the Svelte UX and Axum server behave after configuration changes.