Self-Hosting the Relay
The relay is a lightweight Node.js server. It only routes WebSocket traffic and serves the dashboard — no heavy compute needed.
Relay URL = Dashboard URL
The relay serves the React dashboard SPA on the same port as WebSocket and REST API. Open http://your-server:4000 in a browser to reach the dashboard directly. No separate web server configuration needed.
Deployment scenarios
Local (single Mac)
Run the relay and agent on the same Mac at once.
tapflow startTeam (separate relay server)
Run the relay on a Linux server or dedicated Mac; run the agent on each Mac with a simulator.
On the server (PM2 or plain Node.js — see below):
tapflow relay startOn each Mac:
tapflow agent start --relay wss://your-relay-urlNode.js
npm install -g tapflow
tapflow relay startThe relay reads tapflow.config.json from the working directory. See Configuration.
JWT_SECRET
Replace before deploying to a server
The default value (tapflow-dev-secret-change-in-production) is public in the source code. Leaving it unchanged lets anyone forge valid tokens.
Generate a secure random secret:
openssl rand -hex 32Inject the generated value as an environment variable:
# Node.js
JWT_SECRET=YOUR_JWT_SECRET tapflow relay start
# PM2
JWT_SECRET=YOUR_JWT_SECRET pm2 start tapflow --name relay -- relay startPM2 (recommended for servers)
Handles automatic restart on crash, restart on server reboot, and log management.
npm install -g pm2 tapflowInject the JWT_SECRET you generated above, then start:
JWT_SECRET=YOUR_JWT_SECRET pm2 start tapflow --name relay -- relay start
pm2 save
pm2 startupTo update tapflow:
npm update -g tapflow
pm2 restart relayNext step
Once the relay is running, create the first admin account with tapflow init. For team invitations and your first build upload, see First-time Setup.
External access
Regardless of how you run the relay, you need an external URL when teammates access the dashboard from outside your local machine, or when agents connect from a different network.
ngrok (quick start)
The fastest way to get a public URL without setting up a domain or server.
# Terminal 1: start the relay
tapflow relay start
# Terminal 2: expose with ngrok
ngrok http 4000ngrok prints a URL like https://abc123.ngrok-free.app. That URL is both the relay address and the dashboard address.
When connecting agents, use the wss:// scheme:
tapflow agent start --relay wss://abc123.ngrok-free.appngrok free plan limitations
- The URL changes on every restart (fixed URL requires a paid plan).
- All traffic — including video streams — passes through ngrok servers. This conflicts with tapflow's "data stays in your infrastructure" principle.
- Use ngrok for testing and demos only. Use a reverse proxy for team production environments.
nginx example
WebSocket upgrade headers required
Without Upgrade and Connection headers, the agent's WebSocket connection will fail.
server {
listen 443 ssl;
server_name tapflow.myteam.example.com;
ssl_certificate /etc/letsencrypt/live/tapflow.myteam.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tapflow.myteam.example.com/privkey.pem;
location / {
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}
}Caddy example
tapflow.myteam.example.com {
reverse_proxy localhost:4000
}Caddy handles TLS and WebSocket upgrades automatically.