termdeck mirrors your iTerm2 sessions to your phone over Tailscale, so you can watch and type into whatever your Mac is running without walking over to it. It talks to iTerm2's own Python API rather than tmux, so it shows the exact windows, tabs and panes you already have open, scrollback included.
- Lists every iTerm2 window, tab and pane, grouped the way they sit on screen, with a dot on anything still running or waiting on you.
- Shows each session's scrollback, not just the current screen, and keeps fetching further back as you scroll.
- Lets you type into a session two ways: compose a line and send it, or go live and send every keystroke, for editors and prompts.
- Can turn a session into a read-only link at
/s/<token>. On its own that link only reaches the tailnet; publishing it further takes Tailscale Funnel, which exposes that one link prefix to the open internet and nothing else. - Runs under pm2 or a launchd LaunchAgent, so it starts with the Mac and comes back on its own after a crash or a reboot.
./run.sh
Creates a virtualenv, installs everything in requirements.txt — aiohttp
and iTerm2's own Python API package — and starts termdeck in the foreground
on port 7717.
To have it survive a reboot instead:
./install.sh
It hands the process to pm2 if that's on the machine, otherwise a launchd
LaunchAgent, configured from ecosystem.config.js. Either way, iTerm2's
Python API has to be turned on once: Settings → General → Magic → Enable
Python API. That takes effect immediately — no restart — and termdeck just
keeps retrying until it can see it.
The share button on a session opens a short explanation of what a link does before it makes one, on purpose: sharing is two taps, never one. Once made, the link works from any device — no Tailscale, no password — and shows that one session live, scrollback and all. It can only watch: nothing typed at a share link reaches the session. Stopping the share closes every viewer on the spot.
termdeck only ever binds to loopback and the Tailscale interface, never a
public address. A --lan flag also binds the Wi-Fi network, for reaching it
without Tailscale, but nothing about it opens a port to the internet.
Tailscale Funnel is the one thing that does: it publishes the /s prefix,
and only that prefix, at your node's address. The session list, typing,
everything else stays off the internet even with Funnel running. A share
link's security is its token — a long random value in the address, checked
as-is, with nothing worth guessing.
Set TERMDECK_DEMO=1 and termdeck serves a handful of made-up sessions
instead of talking to iTerm2, so it can be tried or shown off with no Mac
terminal behind it:
TERMDECK_DEMO=1 ./run.sh
It listens on a different port than the real thing by default, so trying it never collides with a termdeck that's already running.


