Skip to main content

Remote access

Each machine runs its own Overdeck. That machine owns its projects, agents, conversations, terminals and credentials, and its dashboard serves only its own state. Another device (a laptop, a tablet, a second desktop) reaches a machine by pairing with it. Pairing gives that device its own revocable session. The machine’s root internal token never leaves the machine.
Pairing from the terminal (pan pair, pan devices) is in v0.64.0. Settings → Anywhere, Settings → Access Tokens and scoped API tokens (pan token) arrive in the next release (v0.65.0).
Every machine has a stable public identity. GET /api/environment returns it without a credential:
The descriptor contains no paths, usernames or tokens.
Do not expose a dashboard on a network until GET /api/environment reports capabilities.terminalAuth: true. That value means every /ws/* connection (terminals, RPC, voice) requires a credential.

Pair a device

From the dashboard

  1. On the machine, open Settings → Anywhere and click Pair a device.
  2. Pick the address the other device opens, for example your Tailscale name. If it is not in the list, choose Another address…, type it, and click Add to trusted addresses. It is trusted at once, with no restart. Use this machine only makes a link that works only on this machine.
  3. Click Create pairing link. Scan the QR code with the other device, or copy the link and open it there. The dialog names the device when it pairs.
Only a browser on the machine itself can create pairing links or add addresses. A paired device that opens the dialog sees an explanation instead of a link.

From the terminal

  1. On the machine, run pan pair with an address the other device can reach:
    It prints a URL such as https://desk.tailnet.ts.net/#pair=odp_…, the expiry time, and the raw credential for manual entry. Use --json for { url, credential, expiresAt }. --label <name> records a name for the pairing in the activity log.
  2. Open that URL on the other device. The dashboard exchanges the credential for a device session, removes it from the address bar, and loads.
The pairing credential:
  • works once, and expires 10 minutes after it was created;
  • lives only in the running dashboard’s memory, so a dashboard restart invalidates every credential that has not been used yet;
  • always travels in the URL fragment (#pair=), never in a query string, so it never reaches a server or proxy log.
Without --url, pan pair uses this machine’s own dashboard address. When that address is localhost (or 127.0.0.1, ::1, *.localhost), the URL only works on this machine, and pan pair says so. pan pair talks to a dashboard started by pan up, which shares this machine’s internal token. A paired device cannot create more pairing credentials.

Open one conversation on another screen

To look at one conversation on your phone or another computer while this machine keeps running it, open the conversation’s ⋮ menu (in its header or on its row) and choose Continue on another device. The Open on another screen tab shows a link to that conversation, a Copy button and a QR code. The tab offers only trusted addresses other devices can reach, never a loopback address. If there is none yet, type one and click Add an address. In a browser on this machine, Also pair the device is checked by default. Click Create pairing link to add a one-time pairing credential to the link (…/conv/<id>#pair=odp_…): an unpaired device that opens it pairs and lands on the conversation in one step. The credential follows the same rules as above: it works once, expires after 10 minutes, and travels only in the URL fragment. On a paired device the option is not shown, and the link works for devices that are already paired. Available since v0.65.0.

Open the dashboard at a non-default address

The dashboard only accepts browser requests from origins it trusts. To open it at an address other than the default (for example through Tailscale), add that address in the pair dialog with Add to trusted addresses. It takes effect on the next request, with no restart, and is stored in ~/.overdeck/trusted-origins.json:
Loopback addresses (localhost, 127.0.0.1, ::1, *.localhost) cannot be added: they are already trusted for this port and never help another device. To remove an address, edit the file and restart the dashboard. OVERDECK_TRUSTED_ORIGINS still works and is merged with the file. Separate several origins with commas, and set it before starting the dashboard:
A later release discovers and advertises endpoints automatically (PAN-4403).

List and revoke devices

Settings → Anywhere lists paired devices with their scopes, when each was paired and when it was last seen. Revoke there has the same effect as pan devices revoke. From the terminal:
Revoking a device stops its next HTTP request and closes its open WebSocket and live event-stream connections at once. If the dashboard is not running, pan devices revoke writes the revocation straight into the registry; no connections can be open then. The running dashboard also rereads the registry every 5 seconds, so a revocation from any process takes effect within 5 seconds. Device sessions are stored in ~/.overdeck/access-tokens.json (mode 0600). The file keeps only a SHA-256 hash of each token. If the file is corrupt, the dashboard accepts no device session and logs an error naming the file.

Anywhere status

The Anywhere card shows whether other devices can use this machine. It appears in Settings → Anywhere and on the Health page, and shows:
  • This machine: its label and the start of its environment id.
  • Reachable at: the trusted addresses another device can open, or “Only this machine”.
  • Paired devices: how many device sessions are active (not revoked).
  • Session Vault: “Not set up”, “Locked on this machine”, “Key rotation unfinished”, or “Ready” with its backend. Set up opens Settings → Session Vault, which shows the pan vault setup <git-url> command to run in a terminal. Setting up from the dashboard is planned (PAN-4446). Not using the vault is a valid choice, not a problem.
Below that, the card lists each problem with its fix: The card reads GET /api/anywhere/status, which any dashboard credential may call.

Scoped API tokens

A paired device can do everything the dashboard can. A program that needs only part of that (an event sidecar, Hermes, a script that sends messages to agents) should hold a scoped API token instead. A leaked scoped token exposes only the routes its scopes name, and it can never create a session, a pairing credential or another token. Create one on the machine:
pan token create prints the token (odk_…) once. Store it right away; it cannot be shown again. A running dashboard accepts a new token within 5 seconds. The scopes: Anything not in the table needs admin. A request whose token lacks the scope gets 403 with { "error": "insufficient_scope", "missingScope": "<scope>" }. Send the token on every request as Authorization: Bearer odk_…. It works over HTTP, the event stream and WebSocket upgrades. It does not work as a cookie or a URL parameter, and a request carrying it needs no Origin header or CSRF token.
List and revoke tokens:
Revoking a token stops its next request and closes its open WebSocket and event-stream connections at once. If the dashboard is not running, pan token revoke writes the revocation into the registry, and a dashboard that starts later never accepts the token. Only this machine’s internal token or root session can create or revoke tokens. The dashboard API is GET/POST /api/access-tokens and DELETE /api/access-tokens/:id. You can also manage tokens in Settings → Access Tokens: the table shows each token’s name, scopes, created, last used and revoked times; Create token shows the plaintext once with a Copy button; Revoke asks for confirmation. A browser signed in as a paired device can see the list but cannot create or revoke tokens; the section explains why.

What the LAN can reach

The dashboard binds every interface. Every /api/* and /events/* request that does not come from this machine must carry a credential (the root session, a device session, a scoped API token, or the internal token), or it gets 401. A scoped token is always judged by its scopes, even from this machine. “From this machine” means a loopback address, or the host-local Traefik and Docker networks that front overdeck.localhost. These routes answer without a credential: GET /events/stream also accepts its OVERDECK_EVENTS_TOKEN bearer token when that variable is set, so an external consumer such as a TTS sidecar keeps working. A read:events scoped token works there too, set or not. The dashboard’s static page files stay public.

Local reverse proxies: require_token_mint

A local reverse proxy (Tailscale Serve, cloudflared, a Traefik route) connects to the dashboard from 127.0.0.1, so every visitor looks like a local caller. Turn on require_token_mint whenever such a proxy forwards outside traffic. Idle dashboard terminals stay connected behind proxies with idle timeouts of 30s or more because the terminal WebSocket exchanges a ping/pong control frame every 20s; programmatic clients opt in with ?heartbeat=1 (PAN-4434).
With it on:
  • A browser can only get a root session by presenting the internal token (for example the one-time #overdeck_token=<token> URL fragment, where the token is the contents of ~/.overdeck/internal-token). Being local no longer counts.
  • A local request that carries a proxy header (X-Forwarded-For, X-Forwarded-Host or Forwarded) counts as remote, so it needs a credential.
A browser that already holds a session keeps working. A new browser behind the proxy needs the #overdeck_token= fragment or pairing. You can also turn it on in Settings → Access Tokens → Require a token to sign in. A Settings change applies at once. A hand edit of config.yaml needs a dashboard restart. The key is read only from the global ~/.overdeck/config.yaml, never from a project .pan.yaml.

What pairing is not

  • The relay is optional reachability. The Overdeck relay (PAN-2356) is one way to reach a machine. It is not where multi-machine state lives: each machine keeps its own.
  • Moving a conversation to another machine is Session Vault. Pairing lets a device use a machine’s dashboard. To resume a conversation on a different machine, use Session Vault.

What comes next

  • A desktop connection list, environment switcher and version gating: PAN-4402.
  • Discovered HTTPS and Tailscale endpoints and advertised origins: PAN-4403.
  • Launching Overdeck on an SSH host from the desktop app: PAN-4404.