sillyjoint
one agent session · every device
A security-first CLI that joins two computers around one tmux-backed coding-agent session. Start Claude, Codex, aider, or any terminal agent on one machine; attach to it from another over SSH on your Tailscale network. No public relay, no cloud, no daemons, no stored passwords.
Contents
Watch SillyJoint in 45 seconds
Two devices, one coding-agent session, security-first wiring. The full story.
Install in one command
Pick whichever fits your setup. Both paths are dependency-free.
Homebrew macOS · Linux
Tap the repo and install:
$ brew tap KiranChilledOut/sillyjointtunnel \ https://github.com/KiranChilledOut/sillyjointtunnel.git $ brew install KiranChilledOut/sillyjointtunnel/sillyjoint
Homebrew's short tap form expects homebrew-sillyjointtunnel; this repo lives at sillyjointtunnel, so the explicit URL is required.
From source macOS · Linux · WSL · Windows
Clone and run the installer:
$ git clone https://github.com/KiranChilledOut/sillyjointtunnel.git $ cd sillyjointtunnel $ python3 install.py
Adds sillyjoint to zsh, bash, and PowerShell profiles. Pass --no-shell-path to skip.
One-time setup & doctor
After install, run setup to create local state and doctor to verify dependencies. Both are idempotent.
Run once after install. Writes to ~/.sillyjoint/ and verifies the install state.
sillyjoint doctor checks tmux, ssh, tailscale, and the security posture. JSON via --json.
Guided 5-step setup
If you have zero networking experience, run the walkthrough first. It opens in a separate terminal so passwords and login flows stay outside any AI chat.
Five steps, each with auto-detected environment checks:
Install
SillyJoint, Tailscale, tmux, SSH on both machines.
Verify versions
Confirm everything is reachable on this terminal's PATH.
Local setup
Create ~/.sillyjoint/ and run doctor.
Host machine
Enable Remote Login/SSH and prepare a session.
Remote machine
Connect from the second device.
The walkthrough opens in its own window so that SSH passwords, Tailscale browser logins, and other secrets never appear in the AI agent's chat history.
Launch your agent through SillyJoint
Instead of typing claude or codex, type sillyjoint prepare. It starts your agent in a managed tmux session, drops you into it so you can start working, and registers it so any other device on your Tailscale network can attach later. Detach with Ctrl-b d when you're done; reconnect from anywhere with sillyjoint connect.
Three flows, same machinery underneath:
The "Claude" menu choice runs claude --proxy under the hood. Pass --agent from the CLI for the advanced variants (claude_resume, claude_proxy_resume, etc.).
Already in tmux?
Register the current tmux session instead of starting a new one. Same tmux keeps running; nothing restarts.
$ sillyjoint session import --name work \ --agent claude --cwd "$PWD" $ sillyjoint prepare --session work
Want to keep an existing conversation?
Start a fresh SillyJoint session, but pass the agent's resume flag as the launch command — it picks up where the previous run left off.
# Claude — continue last conversation $ sillyjoint session start work --agent claude \ --cwd "$PWD" --command "claude --proxy -c" # Codex — resume last session $ sillyjoint session start work --agent codex \ --cwd "$PWD" --command "codex resume"
Attach from any device
On a second machine on the same Tailscale tailnet — laptop, phone-via-iSH, another desktop — run probe then connect. SillyJoint discovers Tailscale devices, lets you search by name, and opens an SSH-backed tmux attach.
probe
Preflight check before attaching. Verifies SSH, tmux, the remote SillyJoint binary, and that the target session is alive.
$ sillyjoint probe --profile macbook
connect
Attach to the remote tmux session. Optionally probe first to refuse attaching if anything is off.
$ sillyjoint connect --profile macbook --probe
Save host, user, and remote SillyJoint path once with sillyjoint remote add <name>. Then every subsequent command just takes --profile <name>. Profiles live in ~/.sillyjoint/remotes.json — no passwords stored.
SSH ControlMaster bake-in
Every remote command opens a fresh SSH connection by default. Enable ControlMaster once and they share one connection per host for 10 minutes. One password prompt, ~10× faster send/capture/ask.
Opt in once:
$ sillyjoint ssh-config enable
SillyJoint writes a clearly-marked block into ~/.ssh/config — never touches anything outside its markers. Adding or removing a profile auto-refreshes the block. Disable any time with sillyjoint ssh-config disable.
Need a one-off raw user@host connection? Pass --no-controlmaster on any remote command.
Daily-use command reference
The commands you'll actually run. Full reference in COMMANDS.md.
What we don't do
SillyJoint is a coordinator, not a relay. Everything below is by design.
Full security model: SECURITY.md