Remote Access
Access the AO dashboard from another device or over Tailscale. Covers ports, binding, reverse proxies, power management, and security.
Remote Access
By default the AO dashboard binds to localhost:3000 and is only accessible from the machine it runs on. This page explains how to access it from another device — your phone, a second laptop, or a remote machine — using Tailscale or direct network binding.
Tailscale (recommended)
Tailscale creates a private WireGuard mesh network between your devices. Every device gets a stable IP like 100.x.x.x and a DNS name like my-laptop.tail1234.ts.net. No port forwarding, no firewall rules required.
Setup
-
Install Tailscale on both the machine running AO and the device you want to access it from.
# macOS brew install --cask tailscale # Ubuntu / Debian curl -fsSL https://tailscale.com/install.sh | sh -
Start Tailscale and authenticate:
sudo tailscale up -
Find your machine's Tailscale IP:
tailscale ip -4 # e.g. 100.64.0.1 -
Bind AO to all interfaces so Tailscale traffic can reach it:
# agent-orchestrator.yaml port: 3000Then start AO with
HOST=0.0.0.0so it listens on all interfaces (not just localhost):HOST=0.0.0.0 ao startAccess the dashboard from another Tailscale device at:
http://100.64.0.1:3000Or using the MagicDNS hostname (if you have MagicDNS enabled in your Tailnet):
http://my-laptop.tail1234.ts.net:3000
Tailscale serve (optional — HTTPS)
For HTTPS with a valid certificate, use tailscale serve:
tailscale serve https:443 / http://localhost:3000This makes the dashboard available at https://my-laptop.tail1234.ts.net with a Let's Encrypt certificate managed by Tailscale. The WebSocket connections for the terminal also work through Tailscale serve.
Binding to a specific interface
AO uses Next.js for the dashboard. To bind to all interfaces (required for any remote access without Tailscale serve), set the HOST environment variable:
# Bind to all interfaces
HOST=0.0.0.0 ao startOr set it in your shell profile:
export HOST=0.0.0.0
ao startTo bind to a specific IP only (e.g. your Tailscale IP):
HOST=100.64.0.1 ao startThe terminal WebSocket ports (terminalPort and directTerminalPort) also need to be reachable. If you're using Tailscale, the mesh handles this transparently as long as the ports are not firewalled locally.
macOS: preventing sleep
If you run AO on a Mac, the machine going to sleep will kill the agents and the dashboard. Use caffeinate to prevent sleep while AO is running:
caffeinate -i ao startcaffeinate -i prevents idle sleep (triggered by inactivity) but still allows display sleep. For a machine you want to run headlessly overnight:
# Prevent all sleep (including display)
caffeinate -dims ao startTo run AO persistently in the background as a launchd service, create a plist at ~/Library/LaunchAgents/com.ao.orchestrator.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.ao.orchestrator</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/ao</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>HOST</key>
<string>0.0.0.0</string>
</dict>
</dict>
</plist>Load it with:
launchctl load ~/Library/LaunchAgents/com.ao.orchestrator.plistReverse proxy
If you want to expose AO over a domain name with TLS, or place it behind an authentication layer, you can front it with nginx or Caddy.
Important: AO uses two WebSocket servers (one for tmux-attached terminals, one for direct PTY terminals). Your proxy must forward HTTP upgrade headers for both.
Environment variables for proxied setups
| Variable | Purpose |
|---|---|
HOST=0.0.0.0 | Bind the Next.js dashboard to all interfaces |
TERMINAL_PORT | Override the tmux WS server port (server-side) |
DIRECT_TERMINAL_PORT | Override the direct PTY WS server port (server-side) |
NEXT_PUBLIC_TERMINAL_WS_PATH | Override the WebSocket base path the browser client dials — required when the proxy rewrites the path |
nginx
server {
listen 443 ssl;
server_name ao.example.com;
# SSL cert config here
# Dashboard
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Terminal WebSockets — tmux mux
location /ws/terminal/ {
proxy_pass http://127.0.0.1:14800/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
# Terminal WebSockets — direct PTY
location /ws/direct/ {
proxy_pass http://127.0.0.1:14801/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
}Then start AO with the matching path env var so the browser client dials through the proxy:
HOST=0.0.0.0 NEXT_PUBLIC_TERMINAL_WS_PATH=/ws/terminal ao startCaddy
Caddy handles WebSocket upgrades automatically — no explicit Upgrade headers needed:
ao.example.com {
reverse_proxy /ws/terminal/* 127.0.0.1:14800
reverse_proxy /ws/direct/* 127.0.0.1:14801
reverse_proxy 127.0.0.1:3000
}Start AO the same way:
HOST=0.0.0.0 NEXT_PUBLIC_TERMINAL_WS_PATH=/ws/terminal ao startPinning WebSocket ports
By default AO auto-detects available ports for the WebSocket servers starting at 14800/14801. To pin them (required when configuring a reverse proxy), set them in agent-orchestrator.yaml:
port: 3000
terminalPort: 14800
directTerminalPort: 14801Or pass them as environment variables when starting:
HOST=0.0.0.0 TERMINAL_PORT=14800 DIRECT_TERMINAL_PORT=14801 ao startAccessing the dashboard from mobile
The AO dashboard is a responsive web app — it works on mobile browsers. Connect over Tailscale and open the URL in Safari or Chrome. The Kanban board and session detail views are usable on a phone screen.
Limitations on mobile:
- The built-in terminal (xterm.js) works but is difficult to type in on a touch screen. Use it to read agent output; for sending messages use the session detail input field.
- There is no native mobile app. Notifications go through your configured notifiers (Slack, desktop, etc.) — there is no push notification to the browser.
Security considerations
AO has no authentication. Anyone who can reach the HTTP port can view all sessions, read terminal output, send messages to agents, and trigger merges. Never expose the dashboard port to the public internet.
Mitigations:
-
Use Tailscale — the mesh is authenticated and encrypted end-to-end. Only your devices can reach the IP.
-
If you must use a public host, put a reverse proxy with HTTP Basic Auth (nginx, Caddy) in front of AO. See the Reverse proxy section above.
-
Firewall the port at the OS level and only allow Tailscale traffic:
# UFW example — allow only Tailscale interface sudo ufw allow in on tailscale0 to any port 3000 sudo ufw deny 3000
Environment variables and secrets in agent processes are visible to anyone with dashboard access. Do not run AO on a shared machine without Tailscale or auth.
Webhook endpoint: If you expose the dashboard publicly and have GitHub (or another tracker) configured, the /api/webhooks endpoint receives push events from GitHub. This endpoint is protected by a webhook secret configured in your tracker settings — verify the secret is set before exposing the port publicly.
For more on where AO stores session data and secrets, see Storage.
Port reference
| Port | Default | Config key | Env var override | Purpose |
|---|---|---|---|---|
3000 | dashboard HTTP | port | PORT | Next.js app + API routes |
14800 | tmux terminal WS | terminalPort | TERMINAL_PORT | WebSocket for tmux-attached terminal |
14801 | direct terminal WS | directTerminalPort | DIRECT_TERMINAL_PORT | WebSocket for direct PTY terminal |
If you run multiple AO instances on the same machine, change all three ports to avoid EADDRINUSE errors:
# Second AO instance
port: 3001
terminalPort: 14810
directTerminalPort: 14811Note: When terminalPort and directTerminalPort are not set in the config or as env vars, AO auto-detects a free port pair starting from 14800. Set them explicitly whenever you configure a reverse proxy or firewall rules.
See also
- Storage — data directory layout and session files
- Projects — per-project config options
- Troubleshooting — connection and port issues