Skip to main content

Remote control

Mirror a running session to a web page and drive it from another device — the same page for every agent and every provider. Your terminal keeps working; sharing mirrors the session rather than taking it away.

alc --share claude
alc share opencode -- --mini
alc session claude-7QK2M9XB4T (claude@all-code)
open http://127.0.0.1:8787/#k=…
hub 127.0.0.1:8787 · loopback only (pid 48213) · this link grants input; keep it to yourself
keys ctrl-\ then d detaches; the session keeps running

Remote control works on Windows 10 and 11 as it does on macOS and Linux; --tmux there needs the native Windows port of tmux, covered in Who owns the size.

Reaching it from a phone

The default binds to loopback. Nothing is exposed until you say so.

Tailscale — alc stays on loopback and Tailscale does the exposing. HTTPS, no third party in the middle.

alc remote allow-host box.tail1a2b.ts.net
tailscale serve 8787
alc claude --share

Your own Wi-Fi — nothing to install, but plain HTTP, so the token crosses the network in clear. Fine at home; use a tunnel on café Wi-Fi.

alc claude --share --bind-lan

Cloudflare Tunnel — works from anywhere, including cellular. Cloudflare terminates the TLS, so put Access in front of it if that matters. A quick tunnel mints a new hostname every run, hence the wildcard.

alc remote allow-host '*.trycloudflare.com'
cloudflared tunnel --url http://127.0.0.1:8787
alc claude --share

alc answers only to names you allowed: it checks the Host header against a list and allows an Origin whose host is on the same list. An attacker's page can point evil.com at 127.0.0.1 and have your own browser drive your agent, and the name the browser thinks it is talking to is the part it cannot forge.

alc remote allow-host box.tail1a2b.ts.net # exact
alc remote allow-host '*.trycloudflare.com' # any subdomain
alc remote status # what it answers to now

The link scrolls away as soon as the agent draws its interface.

alc remote url # just the link
alc sessions # the link, then what is running
page http://192.168.1.42:8787/#k=…
https://box.tail1a2b.ts.net/#k=…

claude-7QK2M9XB4T claude running ask ~/src/all-code
codex-68B8XMJ6F5 codex running plan ~/src/api

Every allowed name gets a line. alc remote token --rotate invalidates every link handed out so far.

Sharing by default

alc remote auto-share on # `alc claude` now behaves like `alc claude --share`
alc --no-share claude # opt one launch out

It is also on the Sharing & remote screen of alc config. A scripted run — one with redirected input or output — never shares whatever this is set to, so a standing preference cannot make a cron job start failing. An explicit --share there still fails loudly.

A page that types into a coding agent is remote code execution on your machine.

  • The fragment is the credential. Anyone with the #k=… part can type. A fragment never reaches a server, so it stays out of access logs and proxies, but it does land in your clipboard. Treat it like a password.
  • Host and Origin are checked, down to the port, and tokens are compared in constant time.
  • A shared session is screen sharing. alc masks the API keys it put into the environment, in every view including your own terminal. Anything else the agent prints, a viewer sees.
  • alc never answers a clipboard read, so a hostile file an agent prints cannot pull what you last copied into the model's context.
  • alc reads no configuration from the working repository, so a checked-in file can never turn sharing on.

Permission modes

The eight agents disagree about what a permission mode is, what the modes are called, and whether one can change after launch. The page renders what the agent in front of it can actually do: a dropdown where a mode can be set outright, a cycle button where it can only be cycled, and a disabled control carrying the reason where the agent has no such concept. It shows alc's rung and the agent's own word for it, because a shared label misleads — auto is the most permissive setting Goose has and a mid-tier one for Claude Code.

AgentFlag alc passesChanging it mid-session
claude--permission-mode plan|manual|acceptEdits|auto|bypassPermissionsShift+Tab cycling only, so the page offers "cycle"
codex-s read-only|workspace-write|danger-full-access and -a on-request|never/permissions opens Codex's own picker
opencode--agent plan|build, --autoTab toggles build ↔ plan
gooseGOOSE_MODE=chat|approve|smart_approve|auto/mode <name>
qwen--approval-mode plan|default|auto-edit|auto|yolo/approval-mode <name>
kimi--plan, --yolorelaunch only
copilot--mode plan|interactive, --allow-all-tools/permissions opens its picker
pinot supported: Pi has no permission modes or sandbox, by design

alc injects a permission flag only for agents it has confirmed against a real --help; for the rest it changes nothing unless you ask with --permission. Each card says how much alc trusts what it shows: launched, reported, or a ? for a guess.

max_permission in remote.toml (default auto-edit) is the loosest mode the page can reach on its own. Tightening is always free; anything looser returns a ticket:

$ alc confirm 7QK2M9XB4T
granted: auto
the page can apply it once, within the next minute.

alc confirm refuses to run without a terminal, so the confirmation comes from a person at the machine rather than from whoever holds the link. Every rung past the ceiling is gated every time, because alc's idea of the current mode is a belief rather than a fact.

Sessions and the hub

Shared sessions belong to a hub, a background process alc starts the first time you share. That is what makes one page show every session and lets a session outlive the terminal it started in.

alc claude --share # starts a hub if one is not running
# ctrl-\ then d # detach; the session keeps running
alc sessions # what is running
alc attach 7QK2 # back on it, from any terminal
alc kill 7QK2 # stop one
alc rename 7QK2 review # rename its card
alc hub status
alc hub stop [--drain] # refuses while sessions run unless --drain

Ids look like claude-7QK2M9XB4T; any unambiguous prefix or the tail alone works, case-insensitively.

Each launch carries the working directory and environment of the shell that asked for it, so a session started in one repository never edits another. If the hub is killed outright the agents keep running detached, and the next hub cleans up what the last one could not. It keeps no log file; when one will not start, run alc hub start --foreground and watch.

Who owns the size

A terminal has one size, and it belongs to the terminal you launched from. So the page does not resize the agent: it draws the real grid as large as it fits, centred, with black where the ratio does not match. Resize your terminal and the page follows within a few seconds.

On a screen 900px or wider with a session open, the Hide sessions button beside Back folds the session list away so the terminal gets the page's full width, and Show sessions brings it back. A plain session is drawn larger; a --tmux session (below) actually gets the extra columns, because the page owns its size. The browser remembers the choice. There is no keyboard shortcut for it on purpose: every key belongs to the terminal, and ctrl-b is tmux's prefix. A phone already shows one pane at a time, so nothing changes there.

--tmux is for when the page is the side you will actually use. It runs the agent inside tmux, so your terminal and the hub each attach as their own client with their own size — and this time the page decides the agent's.

alc --share --tmux --codex claude # or -t
Without --tmuxWith --tmux
SizeYour terminal's; the page scales itThe page's; your terminal shows what fits
Detachctrl-\ then dctrl-b then d
ScrollbackThe page'sThe page's, plus tmux copy mode locally
Your terminal's viewMirrored through the hub, keys maskedA direct tmux client, raw

That last row matters: with --tmux your terminal shows what the agent actually printed, including a key it echoes. The browser still sees those masked.

Needs tmux 3.2 or newer and applies only to a shared session. alc starts its own tmux server per session with no configuration file, so your own tmux is never touched and an agent cannot reach a session's server through a ~/.tmux.conf. Keystrokes from the page go to the agent's pane directly, so a viewer cannot reach tmux's command prompt; your own terminal is a full client and can.

On Windows, --tmux needs the native Windows port of tmux (tested: tmux 3.6a-win32). The alc installer checks for it and tries to install it automatically; see installation and opt-outs. If setup was skipped or failed, use this manual fallback, then open a new terminal so PATH picks it up. The tmux row of alc doctor says whether it was found.

winget install --id arndawg.tmux-windows --exact

psmux also installs a tmux.exe, but alc cannot drive it (it cannot run the command sequence alc creates a session with), so alc looks past it on PATH for the native port and both can stay installed. MSYS2, Cygwin and WSL builds of tmux are not used on Windows either.

The Windows port passes command lines, environment and working directory through the ANSI code page, so the pane runs a small alc launcher that collects the agent's exact launch from the hub over loopback. Non-ASCII folder names, arguments and environment values therefore work, and the provider API key never enters tmux's own environment. If alc itself is installed under a path that is not plain ASCII, it uses the Windows 8.3 short path; on a drive with short names turned off, --tmux refuses and says to install alc under an ASCII path.

Stopping a --tmux session on Windows (alc kill or alc hub stop --drain) ends its tmux server and the agent with it. Windows has no hangup signal, so the card says the session ended without an exit status, where macOS and Linux show the signal.

What this does not do

  • Approval prompts arrive as terminal text, not as mobile dialogs.
  • Every operator link can type at once. There is no take-control arbitration; hand out viewer links for anything you are not driving.
  • Sessions do not survive a reboot, and alc cannot attach to a session it did not start.
  • Permission state is believed, not known, for most agents. The confidence marker on each card says which case you are looking at.
  • Full-screen agents have no browser scrollback. codex --no-alt-screen and opencode --mini are dramatically better on a phone, and the page says so.

Commands

alc --share <agent> # mirror this session
alc --share --tmux <agent> # ...and let the page own the agent's size
alc share <agent> -- <args> # the unambiguous form
alc share <agent> --name x # name the card (after `--` it is the agent's own)
alc --no-share <agent> # never mirror, whatever the settings say

alc remote status
alc remote on | off
alc remote token --rotate
alc remote url
alc remote auto-share on
alc remote allow-host <host>

alc --share --permission plan <agent>
alc confirm <ticket>

--share is alc's own flag, so it comes before the agent's arguments; put it after and alc says so rather than passing it on.

Settings

remote.toml lives beside config.toml; alc remote status prints the path. It is a separate file because config.toml refuses keys it does not recognise.

KeyDefaultWhat it does
enabledtrueMaster switch. alc remote off sets this.
auto_sharefalseShare every session without --share.
bind"loopback"loopback or lan.
port87870 picks an ephemeral port; a busy one falls back.
allowed_hosts[]Names to answer to besides this machine's own.
max_permission"auto-edit"The loosest mode the page can reach alone.
scrollback_bytes1048576How far back a reconnecting viewer is caught up.
max_connections64Connections served at once.