TL;DR — SSH to your Linux box, launch a session with ctc, then drive it from the native Claude app.
ctc runs claude --remote-control in a detached tmux session, one per
project, and hands you an arrow-key TUI to manage them. Detached, so it
outlives the shell that started it; the session keeps running in the Claude
app after you disconnect. One bash file, no daemon, just the SSH you already
trust.
The session manager: launch a new backend, manage the live ones — attach, detach, flip launch/permission modes. Whatever shows up here also shows up in the Claude app.
Driving my box's claude from the app meant four steps, every time:
ssh mybox
cd ~/projects/whatever
claude
/remote-control # inside claude, to push it to the appAnd the session died with the SSH connection — drop the shell, lose claude.
Typing into a raw SSH terminal on a phone is its own punishment: no
autocomplete, no spellcheck, no dictation, no screenshots.
ctc collapses the launch to picking a project from a menu, and because the
backend's detached, the SSH session was only ever the ignition. Close it and
drive claude from the app — autocomplete, spellcheck, dictation, screenshots,
on whatever device you're holding.
Remote Control drives a running
claude from the app, but the process has to stay alive and there's no headless
mode. So: claude --remote-control in a detached tmux session, tmux as the
keepalive Claude Code doesn't ship.
That "doesn't ship" is the whole risk. ctc exists because of an open gap
(#30447). The day
--headless lands, the tmux trick is dead weight and ctc is just a launcher
UI. Built on that clock on purpose.
claude remote-controlserver mode (--spawn worktree,--capacity): one process, many sessions, QR to connect. Great when you're at the box.ctcis for when you're not: per-project backends spun up and managed over SSH. They compose; pointctcat server mode if you want one process.- Claude Code Channels
(official TG/Discord/iMessage, preview since 2026-03): messaging ergonomic, and
it has the same keepalive problem. Anthropic's docs literally say "combining
with tmux, screen, or a background process is the current workaround."
ctchosts exactly that session. They stack. - web/Electron UIs (claudecodeui, Codeman, …): browser file tree + shell, plus a service to run and expose. Opposite bet. Nothing to host.
Needs: an always-on Linux box (server, VPS, Pi, WSL, whatever) with claude
logged in, tmux, and bash 4+. A way to securely SSH into it from wherever
you are; ctc doesn't care how you get there.
ctc doesn't touch auth, it just execs claude with whatever login's already
on the box.
Single file, zero deps:
curl -fsSL https://raw.githubusercontent.com/badbread/ctc/main/bin/ctc \
-o ~/.local/bin/ctc && chmod +x ~/.local/bin/ctcRun ctc. First run autodetects your project dir (~/projects, ~/code,
~/src, …) or asks once. Config at ~/.config/ctc/config
(example), all of it live-editable in [o] options.
git clone / installer instead
git clone https://github.com/badbread/ctc && cd ctc && ./install.shCopies to ~/.local/bin, writes a starter config, checks for claude/tmux.
git pull to update.
Running ctc inside a container
ctc persists its config at ~/.config/ctc/config and history at
~/.local/state/ctc/. Inside a container, those live on the writable layer
unless you mount them — every restart wipes the projects dir setting and you
re-pick it on first run. Mount them (and your projects dir, and ~/.claude*
so Claude's login + per-project trust survive too):
docker run --rm -it \
-v "$HOME/.config/ctc:/root/.config/ctc" \
-v "$HOME/.local/state/ctc:/root/.local/state/ctc" \
-v "$HOME/.claude:/root/.claude" \
-v "$HOME/.claude.json:/root/.claude.json" \
-v "$HOME/projects:/root/projects" \
your-image ctcOr set CTC_CONFIG_DIR / CTC_STATE_DIR (and CLAUDE_CONFIG_DIR) to paths
that already live on a mounted volume.
ctc # session manager
ctc my-api # fuzzy-jump to the project matching "my-api"
ctc ~/some/path # explicit dirThe menu is a process manager for your backends, one tmux session per project. This is the part I actually live in.
- Real liveness.
● livevs◌ idle · claude exited, off the pane's actualpane_current_command(claude/node= live), not a barehas-session. A crashed backend reads idle, not falsely healthy. - Attach / detach.
[a]drops into the runningclaude;Ctrl-b ddetaches back to the menu, session still live and still on the app. Detach ≠ quit./exitends it. - Kill.
[k]on one, or the multi-select screen (Spacemark,Enterconfirm) to reap a batch. - Settings in
[o], persisted to config: where ctc looks for projects (projects dir + scan depth), plus the per-launch defaults applied to new sessions — launch mode (detached/attach), permission mode (acceptEdits/auto/bypassPermissions/default/plan, or Shift+Tab on the main menu to cycle), model,--remote-control,--continue. Every toggle maps to a realclaudeflag where one exists.
ctc is a TUI, so any SSH client works as-is. To make launching a single tap,
force a TTY and run it on login. RemoteCommand wants the full path if ctc
isn't on the non-interactive PATH (~/.local/bin usually isn't, which ctc):
Host ctc
HostName your.box.address
User you
RequestTTY force
RemoteCommand ctc # full path if needed: /home/you/.local/bin/ctc
Phone clients are where this shines, and they each have quirks:
- Termux (Android):
pkg install openssh, host block in~/.ssh/config,ssh ctc. One word?echo 'ctc(){ ssh ctc; }' >> ~/.bashrc && source ~/.bashrc.command not found= stale shell, re-sourceor reopen; if Termux ignores~/.bashrc, use~/.profile. Arrows/Tab/Ctrl on the extra-keys row (Ctrl forCtrl-b d). - Blink (iOS): best iOS TUI client. Host + startup command
ctc, orssh ctc -t ctc. Modifiers and Shift+Tab work. - Termius: host + startup snippet
ctc. Key row has Ctrl/arrows/Tab.
Keys: ↑↓/jk move, Enter select, q/Esc back, bracketed letter
(n o k a b /) jumps straight to the action.
Most of the code was written with Claude Code.
MIT, see LICENSE.
Independent, unofficial. "Claude"/"Claude Code" are Anthropic trademarks; not
affiliated or endorsed. It just launches the claude you already have.
