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).GET /api/environment returns it
without a credential:
Reach this machine with Tailscale
Another device first needs an address that reaches this machine. Tailscale is the recommended route: it puts your machines and phone on one private network and serves the dashboard over HTTPS at the machine’s tailnet name, such ashttps://desk.tailnet-1234.ts.net.
Settings → Anywhere → Set up Tailscale (or pan tailscale setup) installs
Tailscale, signs this machine in, turns on Tailscale Serve HTTPS for the
dashboard, trusts the new address, and turns on token sign-in. It then lists
the other Overdeck machines on your tailnet. See
Tailscale for each step.
Pair a device
From the dashboard
- On the machine, open Settings → Anywhere (or click Machines in the sidebar) and click Pair a device.
- 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.
- 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.
From the terminal
-
On the machine, run
pan pairwith an address the other device can reach:It prints a URL such ashttps://desk.tailnet.ts.net/#pair=odp_…, the expiry time, and the raw credential for manual entry. Use--jsonfor{ url, credential, expiresAt }.--label <name>records a name for the pairing in the activity log. - Open that URL on the other device. The dashboard exchanges the credential for a device session, removes it from the address bar, and loads.
- 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.
--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 the running dashboard with this machine’s internal
token, read from ~/.overdeck/internal-token. Dashboards started by
pan up and by npx @overdeck/core both use that file. If pan pair
reports that the dashboard rejected the token, the dashboard was started
with a different OVERDECK_HOME or before the file existed: restart it
and run pan pair again. A paired device cannot create more pairing
credentials.
From the desktop app
The desktop app can show one other machine’s dashboard instead of running its own, for example your desktop’s dashboard from a laptop over Tailscale.- On the machine, create a pairing link from Settings → Anywhere → Pair a
device, or with
pan pair --url <its Tailscale address>. - In the desktop app, choose Connect to a Machine… from the Overdeck menu or the tray, paste the whole link, and click Connect.
remote-machine.json in its user-data folder, with the credential encrypted by
the operating system’s credential store (Keychain, Windows credential
protection, GNOME Keyring or KWallet). It refuses to pair when no such store is
available. While it shows the other machine, the desktop app runs no local
server; Use This Computer switches back. It sends the credential only over
HTTPS, a Tailscale address, or this computer’s loopback, and refuses any other
http: address. See
Open another machine
for the launch checks, Forget, and the error messages.
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:
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:
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 aspan devices revoke. From the terminal:
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 paired browsers and Overdeck app phones are active (not revoked).
- Tailscale: “Not installed”, “Signed out”, “Needs HTTPS”, “Ready” with the Serve address, or “Set up unfinished”. See Tailscale.
- 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.
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.
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. The table folds the
one access token each running agent holds behind a collapsed Show agent
tokens (N) toggle, and a Sign-ins list follows it with every MCP OAuth
client currently signed in — its access level, timestamps and a Revoke button
that ends the sign-in at once.
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.
What the internet can reach
Nothing, unless you publish it. Two opt-in features use Tailscale Funnel on port 8443: webhook triggers (POST /hooks/*) and
Public MCP (/mcp,
/.well-known/* and /oauth/mcp/*, off by default). A Funnel request never
counts as this machine, can never mint a dashboard session, and gets 404 on
every other path. Over Funnel, /mcp accepts only OAuth sign-ins you approved,
with rate limits.
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).
- 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-HostorForwarded) counts as remote, so it needs a credential.
#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.
Set up Tailscale turns this setting on automatically when it enables
Tailscale Serve, before Serve starts forwarding. While Serve forwards to the
dashboard, a Settings save that turns it off keeps it on; turn Serve off first
(tailscale serve --https=443 off). See
Serve HTTPS and token sign-in.
On automatically
Since v0.73.0require_token_mint is on by itself whenever this machine has any
remote access: a trusted address that is not loopback, an active paired device, or
a machine link in either direction. The toggle in
Settings then shows On automatically because this machine has … and cannot be
turned off. A false in config.yaml cannot turn it off either while remote access
exists; the effective value can only be widened, never narrowed. Remove the last
trusted address, device or link and the YAML value applies again.
Use HTTPS
A browser on another device that opens the dashboard over plainhttp:// gets a
Use HTTPS page (status 403) instead of the app, and a pairing exchange that would
set the session cookie over plain HTTP is refused the same way. Cookies and tokens
never cross the network in the clear; open the dashboard through an HTTPS address
(Tailscale Serve, a reverse proxy with a certificate). Browsers on the machine itself
and linked machines (whose traffic is already inside the secure channel) are not affected.
Find your machines through overdeck.ai
A machine signed in to overdeck.ai (with Sign in on the Account card in Settings → Anywhere, orpan account login) registers in your account’s directory at overdeck.ai and keeps one
connection to it. From any signed-in machine you can then see all of
your machines, link two of them without a pairing code, and reach a linked machine with no
Tailscale. A machine that is not signed in opens no connection and nothing else changes.
- What the directory stores: each machine’s environment id, label, public machine key, fingerprint, whether it is online (with the time it was last seen) and the LAN and Tailscale addresses it advertises. It also passes the short connection setup messages between two of your machines. It never carries your conversations and never vouches for a key.
-
The Account card (Settings → Anywhere → Account) shows the GitHub account this machine
is signed in as, the plan (Free account or Tester), this month’s relay use against the 50 GB
allowance with its reset date, and Manage account, which opens your account page at
overdeck.ai. Sign in appears only in a browser on this machine; it opens overdeck.ai in a
new tab and returns to
http://127.0.0.1:<port>/oauth/account/callback. Other devices see thepan account loginhint. -
See your machines: Settings → Anywhere → Directory, or
pan machines directory. -
Link two machines (approval): on the new machine, click Link on the other machine’s
row (or run
pan machines join). The other machine shows the request; click Review there (or runpan machines approve <id>). Both screens now show the same six-digit code and the other machine’s fingerprint. Approve on one machine and accept on the other only if the two codes match. A different code means something sits between them; reject it. Links made this way are operator-level both ways. -
Direct connections: once linked, the two machines open a peer-to-peer WebRTC path
through the directory’s introduction when no LAN or Tailscale route answers. The link shows
the route as
direct(rtc://…). The secure channel runs over it, so only ciphertext crosses the network. -
The free account includes 3 machines. A fourth machine is refused with “The free account
includes 3 machines”. Remove from directory (or
pan machines forget) frees a slot. -
The relay plan. When no direct path exists (for example strict firewalls on both sides),
a relay plan connects the two machines through a Cloudflare TURN relay, still carrying the
same secure channel. Without it, the link says “No direct path; install Tailscale or use the
relay plan”. Relay traffic counts against a 50 GB monthly allowance per account (UTC calendar
month). While a relay route is in use the Anywhere card shows Using relay and this
month’s traffic; at 50 GB the relay stops until the next month, the card shows “Relay
allowance used; install Tailscale or add 50 GB” and an Add 50 GB button that opens
overdeck.ai. On a machine signed in with the relay plan,
pan doctorshows aTURN relayrow:okwhen this machine can gather a TURN relay candidate right now,warnwith the reason otherwise. -
Self-hosting the directory: run
services/connectyourself and pointanywhere.directory_urlin~/.overdeck/config.yamlat it:It must behttps:, orhttp:to a loopback host. It is read at every directory (re)connect.
What pairing is not
- The relay is optional reachability. The overdeck.ai relay (see Find your machines through overdeck.ai) 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 LAN and Tailscale endpoints shipped with machine links in v0.73.0 (PAN-4403, PAN-4645); see Linked machines.
- Launching Overdeck on an SSH host from the desktop app: PAN-4404.