Persistent process server for AI coding agents. Like tmux, but purpose-built for long-running LLM processes — with native terminal scrolling, copy/paste, and no weird key chords.
Codewire runs as a background node inside your development environment (e.g., a Coder workspace). You launch AI agent sessions with a prompt, and they keep running even if you disconnect. Reconnect anytime to pick up where you left off.
Works with any CLI-based AI agent: Claude Code, Aider, Goose, Codex, or anything else.
brew install codewiresh/codewire/codewirecurl -fsSL https://raw.githubusercontent.com/codewiresh/codewire/main/install.sh | bashThis downloads the latest binary, verifies its SHA256 checksum (and GPG signature if gpg is installed), and installs cw to /usr/local/bin.
Options:
# Install a specific version
curl -fsSL .../install.sh | bash -s -- --version v0.1.0
# Install to a custom prefix
curl -fsSL .../install.sh | bash -s -- --prefix ~/.localgo build -o cw ./cmd/cw
sudo mv cw /usr/local/bin/cwOr use make:
make install# Launch a session (node auto-starts)
cw launch -- claude -p "fix the auth bug in login.ts"
# Use a different AI agent
cw launch -- aider --message "fix the auth bug"
cw launch -- goose run
# Specify a working directory
cw launch --dir /home/coder/project -- claude -p "add tests"
# List running sessions
cw list
# Attach to a session
cw attach 1
# Detach: Ctrl+B then d
# View what the agent did while you were away
cw logs 1Start a new session running the given command in a persistent PTY. Everything after -- is the command and its arguments. An optional positional name before -- gives the session a stable identifier for messaging. Tags enable filtering and coordination.
cw launch -- claude -p "refactor the database layer"
cw launch planner -- claude -p "plan the refactor"
cw launch --dir /home/coder/project -- claude -p "add unit tests for auth"
cw launch --tag worker --tag build -- claude -p "fix tests"
cw launch -- bash -c "npm test && npm run lint"Options:
- Positional name (before
--) — Unique name for the session (alphanumeric + hyphens, 1-32 chars). Used for addressing in messaging. Equivalent to--name. --name— Alternative to positional name (useful for programmatic/MCP use)--dir,-d— Working directory (defaults to current dir)--tag,-t— Tag the session (repeatable)
Show all sessions with their name, status, age, and command.
cw list
# ID NAME COMMAND STATUS AGE
# 1 planner claude -p "plan the refactor" running 2m ago
# 2 coder claude -p "implement changes" running 45s ago
cw list --json # machine-readable outputTake over your terminal and connect to a running session. You get full terminal I/O — native scrolling, native copy/paste, everything your terminal emulator supports.
Detach with Ctrl+B d (press Ctrl+B, release, then press d). The session keeps running.
View captured output from a session without attaching.
cw logs 1 # full output
cw logs 1 --follow # tail -f style, streams new output
cw logs 1 --tail 100 # last 100 linesWorks on completed sessions too — review what the agent did after it finished.
Terminate a session. Supports tag-based filtering.
cw kill 3
cw kill --all
cw kill --tag worker # Kill all sessions tagged "worker"Send input to a session without attaching. Useful for multi-agent coordination.
cw send 1 "Status update?" # Send text with newline
cw send 1 "test" --no-newline # No newline
echo "command" | cw send 1 --stdin # From stdin
cw send 1 --file commands.txt # From fileMonitor a session in real-time without attaching. Perfect for observing another agent's progress.
cw watch 1 # Watch with recent history
cw watch 1 --tail 50 # Start from last 50 lines
cw watch 1 --no-history # Only new output
cw watch 1 --timeout 60 # Auto-exit after 60 secondsSend a direct message to a session. Target can be a session ID or name.
cw msg coder "start with the auth module" # by name
cw msg 2 "start with the auth module" # by ID
cw msg -f planner coder "start with the auth module" # with sender
cw msg --delivery pty coder "check this out" # PTY injection only
cw msg --delivery both coder "review please" # inbox + PTY injectionDelivery modes:
inbox— write to inbox/message log only (safe default for shell sessions)pty— inject a formatted prompt into the recipient's PTY onlyboth— inbox logging + PTY injectionauto(default) — usesbothwhen called from inside another cw session (--fromorCW_SESSION_IDset), otherwiseinbox
Read messages from a session's inbox. Shows direct messages and pending requests.
cw inbox coder # latest 50 messages
cw inbox planner -t 10 # last 10 messagesSend a request to a session and block until a reply arrives. Like msg but synchronous — the caller waits for a response.
cw request -f planner coder "ready for review?"
# [reply from coder] yes, PR #42 is up
cw request coder "status?" --timeout 30 # 30s timeout (default: 60s)
cw request --delivery both coder "need approval" # inbox + PTY injectionUses the same delivery modes as cw msg. When --delivery pty or both is used, the recipient sees a formatted prompt in their terminal with a reply hint.
Reply to a pending request. The request ID comes from cw inbox or from a message.request event.
cw reply req_x7y8z9 "yes, PR #42 is up" -f coderStream all message traffic on the node in real-time. Shows direct messages, requests, and replies as they happen.
cw listen # all message traffic
cw listen --session planner # only messages involving plannerOutput format:
[planner → coder] start with the auth module
[coder → planner] REQUEST (req_x7y8): need the DB schema?
[planner] REPLY (req_x7y8): use the existing users table
Get detailed session status including PID, output size, and recent output.
cw status 1 # Human-readable format
cw status 1 --json # JSON outputSubscribe to real-time session events. Events stream until you disconnect.
cw subscribe --tag worker # Events from sessions tagged "worker"
cw subscribe --event session.status # Only status change events
cw subscribe dev-1 --tag build # Events from remote nodeBlock until sessions complete.
cw wait 3 # Wait for session 3 to complete
cw wait --tag worker --condition all # Wait for ALL workers to complete
cw wait --tag worker --condition any --timeout 60 # Wait for ANY worker, 60s timeoutList all nodes registered with the relay.
cw relay nodesAuthorize this node with a relay using the device authorization flow.
cw relay setup https://relay.codewire.shRun a relay server. The relay provides SSH gateway access, node discovery, and shared KV storage.
cw relay serve --base-url https://relay.example.com --data-dir /data/relayStart an MCP (Model Context Protocol) server for programmatic access.
cw mcp-serverSee MCP Integration section below for details.
Start the node manually. Usually you don't need this — the node auto-starts on first CLI invocation.
cw startStop the running node gracefully.
cw stopManage saved remote server connections.
cw server add my-gpu ws://gpu-host:9100 --token <token> # Save a server
cw server remove my-gpu # Remove it
cw server list # List saved serversSaved servers can be referenced by name with --server:
cw --server my-gpu list
cw --server my-gpu attach 1Codewire is a single Go binary (cw) that acts as both node and CLI client.
Node (cw node): Listens on a Unix socket at ~/.codewire/codewire.sock. Manages PTY sessions — each AI agent runs in its own pseudoterminal. The node owns the master side of each PTY and keeps processes alive regardless of client connections.
Client (cw launch, attach, etc.): Connects to the node's Unix socket. When you attach, the client puts your terminal in raw mode and bridges your stdin/stdout directly to the PTY. Your terminal emulator handles all rendering — that's why scrolling and copy/paste work natively.
Logs: All PTY output is teed to ~/.codewire/sessions/<id>/output.log so you can review what happened while disconnected.
Pass the exact command you want to run after --. No magic — what you type is what runs:
cw launch -- claude -p "fix the bug" # Claude Code
cw launch -- aider --message "fix the bug" # Aider
cw launch -- goose run # Goose
cw launch -- codex "refactor auth" # CodexCommunication between client and node uses a frame-based binary protocol over the Unix socket:
- Frame format:
[type: u8][length: u32 BE][payload] - Type
0x00: Control messages (JSON) — launch, list, attach, detach, kill, resize - Type
0x01: Data messages (raw bytes) — PTY I/O
~/.codewire/
├── codewire.sock # Unix domain socket
├── codewire.pid # Node PID file
├── token # Auth token (for direct WS fallback)
├── config.toml # Configuration (optional)
├── servers.toml # Saved remote servers (optional)
├── sessions.json # Session metadata
└── sessions/
├── 1/
│ ├── output.log # Captured PTY output
│ └── events.jsonl # Metadata event log
└── 2/
├── output.log
└── events.jsonl
All settings via ~/.codewire/config.toml or environment variables:
[node]
name = "my-node" # CODEWIRE_NODE_NAME
listen = "0.0.0.0:9100" # CODEWIRE_LISTEN — direct WebSocket (optional)
external_url = "wss://host/ws" # CODEWIRE_EXTERNAL_URL
relay_url = "https://relay.codewire.sh" # CODEWIRE_RELAY_URL — opt-in remote accessWhen no config file exists, codewire runs in standalone mode (Unix socket only, no relay).
Codewire uses an SSH gateway for remote access. Nodes establish persistent WebSocket connections to a relay server — no root required, works behind NAT.
# Register your node with a relay
cw relay setup https://relay.codewire.sh
# Or with an invite token
cw relay setup https://relay.codewire.sh <token>
# That's it. Your node is now accessible remotely via SSH.The setup flow registers the node, receives a node token, and persists the relay config. The node then maintains a persistent WebSocket connection to the relay.
All commands accept an optional node prefix for remote access:
# Local (no prefix)
cw list # Local sessions
cw attach 3 # Local session
# Remote (node prefix)
cw nodes # List all nodes from relay
cw list dev-1 # Sessions on dev-1
cw attach dev-1:3 # Session 3 on dev-1
cw launch dev-1 -- claude -p "fix bug" # Launch on dev-1
cw kill dev-1:3 # Kill on dev-1You can also connect directly via WebSocket without a relay:
cw server add my-server wss://remote-host:9100 --token <token>
cw --server my-server list
cw --server my-server attach 1 INTERNET
|
+--------+--------+
| cw relay |
| HTTPS :443 | <- Clients connect here
| SSH :2222 | <- SSH into any node
| /node/connect | <- Nodes connect here (WS)
+--------+--------+
| |
WS agent | | WS agent
(outbound) | | (outbound)
| |
+-------+--+ +---+-------+
| cw node | | cw node |
| "dev-1" | | "gpu-box" |
+----------+ +-----------+
- Relay = SSH gateway + HTTP API (node discovery, shared KV, device auth)
- Nodes = connect outward via persistent WebSocket agents (no inbound ports needed)
- Clients = SSH into
<node>@relay:2222; same PTY experience
The repo includes a Docker Compose stack:
- Relay — SSH gateway + HTTP API on
localhost:8080 - Caddy — TLS reverse proxy on
localhost:9443with wildcard subdomain support - Codewire — containerized node (
docker-test) onlocalhost:9100
# Start the stack
docker compose up -d --build
# List nodes
cw nodes
# Tear down
docker compose downhelm install my-relay oci://ghcr.io/codewiresh/charts/codewire-relay \
--set relay.baseURL=https://relay.example.comSee charts/codewire-relay/values.yaml for full configuration. Verify with helm test my-relay.
For multi-tenant clusters or automated provisioning. Install the operator, then create a CodewireRelay CR:
kubectl apply -f operator/config/crd/codewire.io_codewirerelays.yaml
kubectl apply -f operator/config/manager/deployment.yamlapiVersion: codewire.io/v1alpha1
kind: CodewireRelay
metadata:
name: production
spec:
baseURL: https://relay.example.com
authMode: token
ingress:
className: nginx# Install the binary
curl -fsSL https://codewire.dev/install.sh | sh
# Configure
sudo cp deploy/systemd/codewire-relay.service /etc/systemd/system/
sudo cp deploy/systemd/codewire-relay.env /etc/codewire-relay/env
sudo editor /etc/codewire-relay/env # set CW_BASE_URL, CW_AUTH_TOKEN
# Start
sudo systemctl enable --now codewire-relaySee Local Development (Docker Compose) above for a ready-to-use docker-compose.yml.
CodeWire is designed for LLM-driven multi-agent workflows. Tags, subscriptions, and wait provide structured coordination primitives.
Label sessions at launch for filtering and coordination:
cw launch --tag worker --tag build -- claude -p "fix tests"
cw launch --tag worker --tag lint -- claude -p "fix lint issues"
# List sessions by tag (via MCP or API)
# Kill all workers
cw kill --tag workerStream structured events from sessions:
# Watch all status changes for "worker" sessions
cw subscribe --tag worker --event session.status
# Subscribe to all events from session 3
cw subscribe --session 3Event types: session.created, session.status, session.output_summary, session.input, session.attached, session.detached, direct.message, message.request, message.reply
Block until sessions finish — replaces polling:
# Wait for session 3 to complete
cw wait 3
# Wait for ALL workers to complete
cw wait --tag worker --condition all
# Wait for ANY worker to complete, 60s timeout
cw wait --tag worker --condition any --timeout 60Supervisor pattern — one orchestrator coordinates workers:
# Launch tagged workers
cw launch --tag worker -- claude -p "implement feature X"
cw launch --tag worker -- claude -p "write tests for X"
# Wait for all workers to finish
cw wait --tag worker --condition all --timeout 300
# Check results
cw listAgent messaging — named agents exchanging structured messages:
# Launch named agents
cw launch planner -- claude -p "plan the refactor"
cw launch coder -- claude -p "implement changes"
# Send a direct message
cw msg -f planner coder "start with the auth module"
# Request/reply — synchronous coordination
cw request -f planner coder "ready for review?"
# [reply from coder] yes, PR #42 is up
# Monitor all message traffic
cw listenAgent swarms — parallel agents with cross-session communication:
cw launch --tag backend -- claude -p "optimize queries"
cw launch --tag frontend -- claude -p "optimize bundle"
# Send coordination message
cw send 1 "Backend ready for integration"
# Monitor in real-time
cw watch 2 --tail 100Multiple attachments — multiple clients can attach to the same session:
# Terminal 1
cw attach 1
# Terminal 2 (both see output)
cw attach 1Install the codewire skill so Claude Code knows how to use cw for session management:
curl -fsSL https://raw.githubusercontent.com/codewiresh/codewire/main/.claude/skills/install.sh | bashThis installs two skills to ~/.claude/skills/:
- codewire — teaches Claude Code to launch, monitor, and coordinate sessions
- codewire-dev — development workflow for contributing to the codewire codebase
For programmatic tool access, add CodeWire as an MCP server:
# User-level (available in all projects)
claude mcp add --scope user codewire -- cw mcp-server
# Or project-level
claude mcp add codewire -- cw mcp-serverThis exposes 18 tools:
| Tool | Description |
|---|---|
codewire_launch_session |
Launch new session (with name and tags) |
codewire_list_sessions |
List sessions with enriched metadata |
codewire_read_session_output |
Read output snapshot |
codewire_send_input |
Send input to a session |
codewire_watch_session |
Monitor session (time-bounded) |
codewire_get_session_status |
Get detailed status (exit code, duration, etc.) |
codewire_kill_session |
Terminate session (by ID or tags) |
codewire_subscribe |
Subscribe to session events |
codewire_wait_for |
Block until sessions complete |
codewire_msg |
Send a direct message to a session |
codewire_read_messages |
Read messages from a session's inbox |
codewire_request |
Send a request and block for reply |
codewire_reply |
Reply to a pending request |
codewire_list_nodes |
List nodes from relay |
codewire_kv_set |
Set key-value (shared KV store) |
codewire_kv_get |
Get value by key |
codewire_kv_list |
List keys by prefix |
codewire_kv_delete |
Delete key |
# Build
make build
# Run unit tests
make test
# Run all tests (unit + integration)
go test ./internal/... ./tests/... -timeout 120s
# Lint
make lint
# Manual CLI test
make test-manual
# Run with debug logging
cw node # slog outputs to stderr by defaultRelease binaries are signed with GPG. The public key is committed to this repository at GPG_PUBLIC_KEY.asc.
To verify a release:
# Import the public key
curl -fsSL https://raw.githubusercontent.com/codewiresh/codewire/main/GPG_PUBLIC_KEY.asc | gpg --import
# Verify checksums signature
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify binary checksum
sha256sum --check --ignore-missing SHA256SUMSMIT
Latest release: v0.2.26 — 2026-02-21