kojo
Remotely operate AI coding CLIs (Claude Code, Codex, Gemini CLI) on macOS from your mobile device.
┌─────────────────┐ Tailscale ┌──────────────────┐
│ macOS Machine │◄──────(P2P encrypted)──────►│ Mobile Browser │
│ │ │ │
│ kojo server │ WebSocket / HTTP │ Web UI │
│ ├─ PTY: claude │◄──────────────────────►│ ├─ xterm.js │
│ ├─ PTY: codex │ │ ├─ React │
│ └─ PTY: gemini │ │ └─ Web Push │
└─────────────────┘ └──────────────────┘
Features
- Single binary — Built with Go, web UI embedded
- Unified PTY — All CLIs handled uniformly via PTY. No SDK dependencies
- Tailscale P2P — No central server or database. Encrypted with WireGuard
- Zero config — Just run
kojo with Tailscale running
Requirements
- macOS
- Go 1.25+
- Node.js 20+
- Tailscale
- Supported CLIs:
claude, codex, gemini (at least one)
Build
# Production build
make build
# Development (two terminals)
make dev-server # Go server (--dev mode, proxies to Vite)
make dev-web # Vite dev server
# Hot reload (auto rebuild on Go file changes)
make watch
Usage
# Via Tailscale (default, auto HTTPS)
kojo
# Local only
kojo --local
# Custom port (auto-increments if busy)
kojo --port 9090
By default, kojo listens on the Tailscale network via tsnet with HTTPS.
Use --local or --dev to bind to localhost only.
Tailscale HTTPS Setup
kojo uses tsnet to join your Tailscale network directly as a node named kojo. All traffic is encrypted with WireGuard — no ports to open, no certificates to manage.
Prerequisites
- Install Tailscale on your macOS machine and your mobile device
- Sign in to the same Tailscale account on both devices
- Ensure both devices appear in Tailscale admin console
How it works
When you run kojo (without --local):
- kojo starts an embedded Tailscale node via tsnet
- The node registers itself as
kojo on your tailnet
- HTTPS is automatically provisioned using Tailscale's built-in Let's Encrypt integration
- Your server becomes reachable at
https://kojo.<tailnet-name>.ts.net
$ kojo
kojo v0.1.0 running at:
https://kojo.tail1234.ts.net
https://100.x.y.z:8080
First run
On the first launch, tsnet will print an authentication URL to stderr. Open it in your browser to authorize the node. This is a one-time step — credentials are cached in ~/.config/tsnet-kojo/.
Access from mobile
- Install the Tailscale app on your phone
- Sign in with the same account
- Open
https://kojo.<tailnet-name>.ts.net in your mobile browser
All communication is peer-to-peer via WireGuard. No data passes through a central server.
Security model
| Layer |
Protection |
| Network |
WireGuard encryption (Tailscale P2P) |
| TLS |
Auto-provisioned HTTPS via Let's Encrypt |
| WebSocket |
Origin restricted to Tailscale IPs (100.*), *.ts.net, and localhost |
| Access |
Only devices on your tailnet can reach kojo |
ACL (optional)
You can restrict which devices can access kojo using Tailscale ACLs:
{
"acls": [
{
"action": "accept",
"src": ["tag:mobile"],
"dst": ["tag:kojo:*"]
}
],
"tagOwners": {
"tag:mobile": ["autogroup:admin"],
"tag:kojo": ["autogroup:admin"]
}
}
Troubleshooting
| Problem |
Solution |
| Auth URL not appearing |
Check stderr output, or remove ~/.config/tsnet-kojo/ and restart |
| Cannot reach from mobile |
Ensure both devices are on the same tailnet and Tailscale is connected |
| Port conflict |
Use --port <number> (auto-increments up to 10 times if busy) |
| Want localhost only |
Use --local or --dev to skip Tailscale entirely |
What it does
- Manage multiple sessions simultaneously (newest first)
- Session persistence (
~/.config/kojo/sessions.json, auto-cleanup after 7 days)
- Session restart (keeps same ID,
claude auto-adds --continue)
- Real-time PTY output streaming (xterm.js)
- Text input (Enter for newline, Shift+Enter to send) and special keys (Esc, Tab, Ctrl, arrows)
- Working directory path completion
- File browser (syntax highlighting for text, image preview)
- File attachment (camera, images, text)
- Git panel (status, log, diff, command execution)
- Web Push notifications (permission prompts, completion alerts)
- Yolo mode (auto-approve permissions)
Tech Stack
| Layer |
Technology |
| Server |
Go, net/http, coder/websocket, creack/pty, tsnet |
| Web UI |
React 19, Vite, TypeScript, Tailwind CSS, xterm.js |
| Notifications |
Web Push (VAPID) |
| Network |
Tailscale WireGuard P2P |
License
MIT