--- title: Remote Access description: 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](https://tailscale.com) 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 1. Install Tailscale on both the machine running AO and the device you want to access it from. ```bash # macOS brew install --cask tailscale # Ubuntu / Debian curl -fsSL https://tailscale.com/install.sh | sh ``` 2. Start Tailscale and authenticate: ```bash sudo tailscale up ``` 3. Find your machine's Tailscale IP: ```bash tailscale ip -4 # e.g. 100.64.0.1 ``` 4. Bind AO to all interfaces so Tailscale traffic can reach it: ```yaml # agent-orchestrator.yaml port: 3000 ``` Then start AO with `HOST=0.0.0.0` so it listens on all interfaces (not just localhost): ```bash HOST=0.0.0.0 ao start ``` Access the dashboard from another Tailscale device at: ``` http://100.64.0.1:3000 ``` Or 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`: ```bash tailscale serve https:443 / http://localhost:3000 ``` This 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: ```bash # Bind to all interfaces HOST=0.0.0.0 ao start ``` Or set it in your shell profile: ```bash export HOST=0.0.0.0 ao start ``` To bind to a specific IP only (e.g. your Tailscale IP): ```bash HOST=100.64.0.1 ao start ``` The 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: ```bash caffeinate -i ao start ``` `caffeinate -i` prevents idle sleep (triggered by inactivity) but still allows display sleep. For a machine you want to run headlessly overnight: ```bash # Prevent all sleep (including display) caffeinate -dims ao start ``` To run AO persistently in the background as a launchd service, create a plist at `~/Library/LaunchAgents/com.ao.orchestrator.plist`: ```xml Label com.ao.orchestrator ProgramArguments /usr/local/bin/ao start RunAtLoad KeepAlive EnvironmentVariables HOST 0.0.0.0 ``` Load it with: ```bash launchctl load ~/Library/LaunchAgents/com.ao.orchestrator.plist ``` ## Reverse 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 ```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: ```bash HOST=0.0.0.0 NEXT_PUBLIC_TERMINAL_WS_PATH=/ws/terminal ao start ``` ### Caddy 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: ```bash HOST=0.0.0.0 NEXT_PUBLIC_TERMINAL_WS_PATH=/ws/terminal ao start ``` ### Pinning 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`: ```yaml port: 3000 terminalPort: 14800 directTerminalPort: 14801 ``` Or pass them as environment variables when starting: ```bash HOST=0.0.0.0 TERMINAL_PORT=14800 DIRECT_TERMINAL_PORT=14801 ao start ``` ## Accessing 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](#reverse-proxy) section above. - Firewall the port at the OS level and only allow Tailscale traffic: ```bash # 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 project identity, local config, and runtime data, see [Configuration](/docs/configuration). ## 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: ```yaml # Second AO instance port: 3001 terminalPort: 14810 directTerminalPort: 14811 ``` Note: 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 - [Configuration](/docs/configuration) — global registry and local project config - [Projects](/docs/configuration/projects) — per-project behavior options - [Troubleshooting](/docs/troubleshooting) — connection and port issues