Skip to content

Remote Access

github-actions[bot] edited this page Aug 18, 2026 · 1 revision

Remote Access

Reaching your Codeman from a phone, a laptop on the other side of the house, or a hotel network. This is the page to read carefully, because Codeman's dashboard is a remote-code-execution surface by design: it starts agents with permission prompts skipped, so whoever can reach it can run code on your machine.

Start from the default

codeman web binds 127.0.0.1. It is reachable from the machine running it and nothing else, which is why the no-password default is safe out of the box. Every option below is a deliberate step away from that.

Two rules that make the rest of this page simple:

  1. Never expose Codeman on a network without CODEMAN_PASSWORD. Binding a non-loopback host without one starts, but prints a loud warning with the fixes.
  2. Prefer keeping the loopback bind and putting an authenticated tunnel in front of it, over binding wide and relying on a password alone.

Pick an approach

Approach Good for Cost
Tailscale Phone access, permanently. The recommended setup. Install Tailscale on both devices.
Cloudflare tunnel A public URL, quickly, from anywhere. Public URL, so a password is mandatory.
LAN + password Home network only, no extra software. Every device on your LAN can reach the login page.
SSH port forward You already SSH to the box. Manual, per session, terminal-bound.

Tailscale (recommended)

Your devices join a private network, and Codeman stays bound to loopback. Nothing is published to the internet, and you get real HTTPS with a real certificate.

The installer sets this up for you, including installing Tailscale, logging in, enabling tailnet HTTPS, and verifying the result end to end. To retrofit it onto an existing install:

install.sh tailscale

By hand:

tailscale serve --bg 3000
tailscale serve status

Then open https://<machine>.<tailnet>.ts.net from any device on your tailnet.

Notes:

  • Keep the loopback bind. tailscale serve connects to 127.0.0.1:3000 locally, so binding wider adds exposure and buys nothing.
  • Your tailnet is the authentication boundary. Setting CODEMAN_PASSWORD as well is reasonable defence in depth, especially if other people have devices on your tailnet.
  • Codeman's Host-header allowlist already accepts .ts.net, so no extra configuration is needed.
  • The installer never resets or rewrites serve mappings other than the one pointing at Codeman's port, so unrelated serve configuration is left alone.

Cloudflare tunnel

A free quick tunnel gives you a public HTTPS URL with no port forwarding, no DNS, and no static IP:

Browser → Cloudflare edge (HTTPS) → cloudflared → localhost:3000

Prerequisites: cloudflared installed, and CODEMAN_PASSWORD set.

./scripts/tunnel.sh start     # starts the tunnel, prints the public URL
./scripts/tunnel.sh url
./scripts/tunnel.sh status
./scripts/tunnel.sh stop

The quick-tunnel URL is a random *.trycloudflare.com address that changes every time the tunnel restarts. For a stable hostname, ./scripts/tunnel.sh named setup walks through a named tunnel.

To survive reboots:

systemctl --user enable codeman-tunnel
loginctl enable-linger $USER

There is also a toggle in App Settings → System → Remote access.

The tunnel refuses to start without a password. That is on purpose: a public URL with no authentication is a terminal on your machine handed to the internet. Acknowledging the risk explicitly is possible from the UI toggle, and only from there; the API will not do it for you.

LAN plus password

export CODEMAN_PASSWORD='something long'
codeman web -H 0.0.0.0 --https

Every device on your local network can now reach the login page. --https generates a self-signed certificate into ~/.codeman/certs/, which your browser will warn about once.

CODEMAN_USERNAME defaults to admin.

The installer offers this path and prompts for the password. On re-runs it preserves whichever binding you already chose.

SSH port forward

No configuration at all, if you already have SSH access:

ssh -L 3000:localhost:3000 you@your-box

Then open http://localhost:3000 on the local machine. Codeman keeps its loopback bind and sees a local connection. Good for occasional access, awkward as a permanent arrangement because it dies with the SSH session.

Logging in from a phone

Typing a long password on a phone keyboard is miserable, so Codeman issues single-use QR tokens. The desktop dashboard shows a QR code; scan it and the phone is authenticated.

How it behaves:

  • The code rotates every 60 seconds, with a 90 second grace window so scanning during a rotation still works.
  • Each token is single use. The moment a phone consumes it, a new one is generated.
  • The URL contains a 6-character lookup code, not the secret, so it does not leak through browser history, Referer headers, or the tunnel provider's logs.
  • The desktop shows a toast naming the device and browser that just authenticated, with a one-click revoke.
  • QR attempts are rate limited separately from password attempts, so a mistyped password cannot lock out your QR login and vice versa.

Someone holding only the tunnel URL still meets the normal password prompt. The QR is the fast path, not a bypass.

Design detail and the threat analysis it is built against: docs/qr-auth-plan.md.

Behind a reverse proxy

Codeman enforces a Host-header allowlist on every request to block DNS rebinding, and the same allowlist gates the cross-site Origin check. It accepts localhost, IP literals, the bind host, .ts.net, .trycloudflare.com, .cfargotunnel.com, and the active managed tunnel.

Your own domain is not on that list. Add it:

CODEMAN_ALLOWED_HOSTS='codeman.example.com,.internal.example.com'

A bare entry matches that exact host; a leading dot matches subdomains. Without this, a correctly configured proxy still gets 403 host not allowed, which reads like a proxy bug and is not one.

Also make sure the proxy forwards WebSocket upgrades. The terminal is a WebSocket, and the upgrade runs the same Host and Origin checks, closing with code 4003 on failure.

Session cookies and rate limits

The first request prompts for HTTP Basic credentials. On success the server issues an opaque codeman_session cookie (24 hour lifetime, extended on activity, validated server-side so it cannot be forged offline). Ten failed attempts from one IP produce a 429 with a 15 minute decay.

A valid cookie or a correct password recovers immediately even while an attacker is hammering the same IP, which matters because all tunnel traffic arrives from one loopback address.

Terminal alternatives

You do not have to use a browser. sc is a thumb-friendly session chooser for SSH clients like Termius or Blink:

sc          # interactive chooser
sc 2        # attach to session 2
sc -l       # list

Detach with Ctrl+A D. The sessions are the same ones the dashboard shows.

Common problems

Symptom Cause and fix
403 host not allowed Your domain is not in the allowlist. Set CODEMAN_ALLOWED_HOSTS.
Phone shows the login page but the terminal never connects The proxy is not forwarding WebSocket upgrades.
Browser warns about the certificate Expected with --https and its self-signed certificate. Tailscale gives you a real one instead.
LAN IP does not respond, but a tunnel to the same box works The server is bound to loopback. That is the default. A tunnel reaches it; a LAN browser cannot.
Hooks stopped working after switching to HTTPS Hook callbacks need -k for the self-signed certificate. Recent versions self-heal existing cases; if yours predates that, recreate the case's hooks.
Everything is slow over the tunnel Quick tunnels route through Cloudflare's edge. Tailscale is usually a direct connection and much faster.

Read next

Clone this wiki locally