Agent Monitor runs on the machine where your agents execute (the agent machine). Since this machine is often not publicly accessible, a relay server on a public machine forwards all traffic through a WebSocket tunnel.
Phone / Laptop ──HTTP──▶ Public Server (Relay :3457) ◀──WS tunnel── Agent Machine (:3456)
| Component | Where it runs | What it does |
|---|---|---|
| AgentMonitor server | Agent machine (local) | Manages agents, spawns CLI processes |
| Relay server | Public server | Serves dashboard, forwards API + Socket.IO via tunnel |
| TunnelClient | Agent machine (local) | Connects outbound to relay, handles forwarded requests |
| Dashboard | Browser (any device) | Accesses relay URL — identical to local mode |
The tunnel is outbound-only from the agent machine — no inbound ports needed.
# From the AgentMonitor repo on any machine with SSH access:
bash relay/scripts/deploy.sh <your-secret-token> <your-dashboard-password>This builds the client + relay, rsyncs to the server, installs deps, and starts with pm2.
Important: Set a dashboard password to protect your relay from unauthorized access. Without it, anyone who knows the URL can control your agents.
RELAY_URL=ws://<public-ip>:3457/tunnel \
RELAY_TOKEN=<your-secret-token> \
npx tsx server/src/index.tsYou'll see:
[Tunnel] Connecting to ws://<public-ip>:3457/tunnel...
[Tunnel] Connected, authenticating...
[Tunnel] Authenticated successfully
Agent Monitor server running on port 3456
Navigate to http://<public-ip>:3457 in any browser.
You can run AgentMonitor on any machine — a desktop, a cloud VM, a lab server — as long as it can reach the relay. The relay doesn't care which machine connects; it just forwards traffic to whoever is currently tunneled in.
-
Clone the repo and install dependencies:
git clone <repo-url> && cd AgentMonitor npm install cd server && npm install
-
Install agent CLIs that you plan to use:
# Claude Code npm install -g @anthropic-ai/claude-code # Codex (optional) npm install -g @openai/codex
OpenCLI is auto-synced by Agent Monitor during
cd server && npm install(requires Node.js 20+ for OpenCLI runtime). -
Copy your
.envfile (or set environment variables):# Required for relay mode: RELAY_URL=ws://<public-ip>:3457/tunnel RELAY_TOKEN=<same-token-as-relay> # Optional (same as before): # SMTP_*, TWILIO_*, SLACK_WEBHOOK_URL, CLAUDE_BIN, CODEX_BIN
-
Start the server:
cd server && npx tsx src/index.ts
That's it. The relay automatically accepts the new tunnel connection (replacing any previous one). The dashboard at http://<public-ip>:3457 now talks to the new machine.
- One agent machine at a time. The relay accepts one tunnel connection. If a new machine connects, it replaces the old one.
- Agent data is local. Each machine has its own
server/data/directory with agent history, templates, and settings. These do not sync between machines. - Sessions are local. Claude Code sessions (for
--resume) live on the machine where they were created. - Git repos must exist locally. The working directory you specify when creating an agent must exist on the agent machine.
If you want to preserve agents, templates, and settings:
# On the old machine:
rsync -avz server/data/ new-machine:/path/to/AgentMonitor/server/data/curl http://<public-ip>:3457/api/relay/status
# {"tunnelConnected":true,"uptime":3600}ssh your-server "pm2 logs agentmonitor-relay --lines 20"ssh your-server "pm2 restart agentmonitor-relay"bash relay/scripts/deploy.sh <your-token>- Stop the relay:
ssh your-server "pm2 delete agentmonitor-relay" - Redeploy with new token:
bash relay/scripts/deploy.sh <new-token> - Update
RELAY_TOKENon the agent machine and restart
| Variable | Required | Description |
|---|---|---|
RELAY_URL |
Yes | WebSocket URL of relay (e.g., ws://1.2.3.4:3457/tunnel) |
RELAY_TOKEN |
Yes | Shared secret — must match relay |
PORT |
No | Local server port (default: 3456) |
| Variable | Required | Description |
|---|---|---|
RELAY_TOKEN |
Yes | Shared secret — must match agent machine |
RELAY_PASSWORD |
Recommended | Dashboard login password (if empty, no auth) |
RELAY_PORT |
No | Port relay listens on (default: 3457) |
RELAY_DOMAIN |
No | Domain name if using nginx reverse proxy |
When RELAY_URL is not set, the server runs in local-only mode with zero relay overhead. Access the dashboard at http://localhost:3456 directly.
| Scenario | What happens |
|---|---|
| Agent machine starts | Tunnel connects and authenticates automatically |
| Tunnel drops | Auto-reconnects with exponential backoff (5s → 60s) |
| Agent machine restarts | Tunnel reconnects; relay resumes forwarding |
| Relay restarts | Agent machine detects disconnect, reconnects when relay is back |
| Wrong token | Auth rejected, tunnel client stops (no retry) |
| API call while tunnel is down | Relay returns 502 Tunnel not connected |
If you see repeated [Socket] Connection error: websocket error in the browser console when accessing the relay URL, this is typically caused by a session authentication issue. The relay uses RELAY_PASSWORD to protect the Socket.IO connection.
How it works: The client establishes Socket.IO using the polling transport first (which carries the auth cookie), then upgrades to WebSocket. Connecting directly via WebSocket without the polling handshake will fail auth and close with code 1006.
Fix: Ensure you are logged in at the relay URL (/api/auth/login) before the socket connects. Clearing cookies and logging in again usually resolves stale-session errors.
The agent machine must be started with RELAY_URL and RELAY_TOKEN set. After a restart, the tunnel reconnects automatically within a few seconds. Check [Tunnel] Authenticated successfully in the server log.
The terminal requires the Socket.IO connection to be established. If the socket isn't connected (see above), terminal events won't be forwarded. Confirm the socket is connected ([Socket] Connected: ... in the browser console) before opening the terminal.
- The tunnel uses a shared token for authentication. Use a strong random token.
- The relay port is open to the internet. Consider using a firewall or nginx with HTTPS.
- Set
RELAY_PASSWORDto enable login authentication. Without it, the dashboard is open to anyone. - Sessions use JWT tokens stored in httpOnly cookies with 24-hour expiry.
server {
listen 443 ssl;
server_name monitor.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/monitor.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/monitor.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3457;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}Set RELAY_DOMAIN=monitor.yourdomain.com to have the relay log the domain name on startup.