https://github.com/badbread/ctc
Start Claude Code sessions on your home server from your phone... one bash file, no server, drive from the Claude app.
https://github.com/badbread/ctc
bash claude-code cli remote-control ssh tmux tui
Last synced: about 9 hours ago
JSON representation
Start Claude Code sessions on your home server from your phone... one bash file, no server, drive from the Claude app.
- Host: GitHub
- URL: https://github.com/badbread/ctc
- Owner: badbread
- License: mit
- Created: 2026-05-26T23:58:18.000Z (about 1 month ago)
- Default Branch: master
- Last Pushed: 2026-05-29T17:48:03.000Z (about 1 month ago)
- Last Synced: 2026-05-29T19:20:21.635Z (about 1 month ago)
- Topics: bash, claude-code, cli, remote-control, ssh, tmux, tui
- Language: Shell
- Size: 872 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ctc: Claude Terminal Connect
**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.
### Demo

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.
## Why
Driving my box's `claude` from the app meant four steps, every time:
```bash
ssh mybox
cd ~/projects/whatever
claude
/remote-control # inside claude, to push it to the app
```
And 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.
## How (and the clock on it)
[Remote Control](https://code.claude.com/docs/en/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](https://github.com/anthropics/claude-code/issues/30447)). The day
`--headless` lands, the tmux trick is dead weight and `ctc` is just a launcher
UI. Built on that clock on purpose.
## vs. the alternatives
- **`claude remote-control` server mode** (`--spawn worktree`, `--capacity`):
one process, many sessions, QR to connect. Great when you're *at* the box.
`ctc` is for when you're not: per-project backends spun up and managed over
SSH. They compose; point `ctc` at server mode if you want one process.
- **[Claude Code Channels](https://www.macstories.net/stories/first-look-hands-on-with-claude-codes-new-telegram-and-discord-integrations/)**
(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." `ctc`
hosts 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.
## Setup
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 `exec`s `claude` with whatever login's already
on the box.
Single file, zero deps:
```bash
curl -fsSL https://raw.githubusercontent.com/badbread/ctc/main/bin/ctc \
-o ~/.local/bin/ctc && chmod +x ~/.local/bin/ctc
```
Run `ctc`. First run autodetects your project dir (`~/projects`, `~/code`,
`~/src`, …) or asks once. Config at `~/.config/ctc/config`
([example](ctc.config.example)), all of it live-editable in `[o] options`.
git clone / installer instead
```bash
git clone https://github.com/badbread/ctc && cd ctc && ./install.sh
```
Copies 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):
```bash
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 ctc
```
Or set `CTC_CONFIG_DIR` / `CTC_STATE_DIR` (and `CLAUDE_CONFIG_DIR`) to paths
that already live on a mounted volume.
```bash
ctc # session manager
ctc my-api # fuzzy-jump to the project matching "my-api"
ctc ~/some/path # explicit dir
```
## Managing sessions
The menu is a process manager for your backends, one tmux session per project.
This is the part I actually live in.
- **Real liveness.** `● live` vs `◌ idle · claude exited`, off the pane's actual
`pane_current_command` (`claude`/`node` = live), not a bare `has-session`. A
crashed backend reads idle, not falsely healthy.
- **Attach / detach.** `[a]` drops into the running `claude`; **`Ctrl-b d`**
detaches back to the menu, session still live and still on the app. Detach ≠
quit. `/exit` ends it.
- **Kill.** `[k]` on one, or the multi-select screen (`Space` mark, `Enter`
confirm) 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 real `claude` flag where one exists.
## One-tap launch
`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-`source` or reopen; if Termux ignores
`~/.bashrc`, use `~/.profile`. Arrows/Tab/Ctrl on the extra-keys row (Ctrl for
`Ctrl-b d`).
- **Blink** (iOS): best iOS TUI client. Host + startup command `ctc`, or
`ssh 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.
## AI
Most of the code was written with Claude Code.
## License
MIT, see [LICENSE](LICENSE).
*Independent, unofficial. "Claude"/"Claude Code" are Anthropic trademarks; not
affiliated or endorsed. It just launches the `claude` you already have.*