Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
51e8ece
docs: reframe vision to ssh-first and detail two-track roadmap
sriinnu Feb 27, 2026
3229695
feat(ssh): add transport contract, tunnel runbook, and state machine
sriinnu Feb 27, 2026
25330f7
chore(ssh): mark open-tunnel script executable
sriinnu Feb 27, 2026
79570a2
feat(ssh): wire startup transport config contract and validation
sriinnu Feb 27, 2026
7648994
feat(ssh): harden target parsing and add startup preflight module
sriinnu Feb 27, 2026
a106845
feat(ssh): enable remote tmux runtime via ssh transport
sriinnu Feb 27, 2026
d902c79
feat(ssh): enforce batch mode and connect timeout wiring
sriinnu Feb 27, 2026
e28a9bb
feat(ssh): harden runtime args and expose transport health context
sriinnu Feb 27, 2026
477c029
feat(audit): add sanitized controlled-input audit coverage
sriinnu Feb 27, 2026
a8d4697
refactor runtime adapter factory and add ssh runtime validator
sriinnu Feb 27, 2026
18f2526
harden ssh contract and proxy negative-path coverage
sriinnu Feb 27, 2026
de085b5
add lane lease expiry and align protocol checkpoint evidence
sriinnu Feb 27, 2026
cbe6b3b
wire lane lease config and capture proxy dry-run evidence
sriinnu Feb 27, 2026
f571292
align lease docs and checkpoint blockers
sriinnu Feb 27, 2026
4576ca4
add audit hash metadata and lane release events
sriinnu Feb 27, 2026
b30944a
feat(bridge): harden replay audit semantics and split e2e helpers
sriinnu Feb 27, 2026
1705805
feat(observability): add lifecycle logging and proxy interop matrix
sriinnu Feb 27, 2026
f96c949
docs: add chitragupta status report and observability evidence contract
sriinnu Feb 27, 2026
892a594
test(proxy): add adapter interoperability matrices across fetch/undic…
sriinnu Feb 27, 2026
ebfa977
docs(proxy): complete package usage matrices and snapshot examples
sriinnu Feb 27, 2026
ad290d1
feat(startup): add remote-host startup profile and replay harness tests
sriinnu Feb 27, 2026
8ae6999
feat(runtime): harden recoverable failure modes and fixture evidence …
sriinnu Feb 27, 2026
5a75057
fix(fixtures): make replay evidence capture deterministic and passing
sriinnu Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 92 additions & 163 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,211 +4,140 @@
<img src="docs/brand/commandrelay-logo.svg" alt="CommandRelay logo" width="104" height="104" />
</p>

CommandRelay is a secure, bi-directional terminal control gateway for long-running coding sessions.
CommandRelay is a production-oriented gateway for remote terminal control of long-running coding sessions.

It lets you monitor and control home-machine terminal sessions (tmux and ghostty/cmux runtime) from remote clients, with replay, guarded input, and auditability.
## SSH-First Architecture

## Why This Exists
CommandRelay is oriented around an SSH-first operating model:

Long AI coding sessions run for hours while you are away from your main machine.
1. Run the gateway on the machine that owns the terminal runtime.
2. Keep runtime state in `tmux` so sessions survive client disconnects.
3. Reach the gateway from remote clients over an SSH transport path (for example, tunnel + WebSocket) while CommandRelay enforces terminal control policy.

CommandRelay gives you one control surface to:
`ghostty` is an optional local terminal UI. It is not the control backend for remote lane ownership, replay, or policy enforcement.

1. See active terminal sessions.
2. Reattach after disconnects.
3. Send commands safely when needed.
4. Keep read-only mode as default.
## Control Plane (Bi-Directional)

## What You Get
Web and native clients use the same v1 envelope and core flow:

1. WebSocket event protocol with strict envelope validation.
2. Replay-aware terminal output streaming (`streamSeq` + `attach(lastSeq)`).
3. Guarded input flow: `enable_input` -> `input` -> `disable_input`.
4. Kill switch and lane-ownership controls.
5. Runtime backend multiplexer (`tmux`, `cmux`) with backend-aware pane/session routing.
6. Proxy package ecosystem for reusable outbound proxy behavior.
1. `list_sessions`
2. `attach(paneId, lastSeq)`
3. `output` stream with ordered `streamSeq` replay behavior
4. guarded input: `enable_input` -> `input` -> `disable_input`

## Architecture
Safety controls:

### Runtime Topology
1. read-only by default
2. explicit input enable per client
3. global input kill switch
4. pane writer-lane ownership arbitration
5. message/input rate limits and max input bytes
6. audit logging for auth/input/policy events

```text
Remote Client (web/iOS/android/macos)
|
| WS (/ws)
v
+-----------------------------------+
| CommandRelay Gateway (Node/TS) |
| - Auth / policy / limits |
| - Replay + output stream engine |
| - Input lane arbitration |
+----------------+------------------+
|
| Runtime multiplexer
v
+-------------------+-------------------+
| |
+----+------------------+ +-----------+-----------+
| tmux backend adapter | | cmux backend adapter |
| pane ids: %1, %2 ... | | pane ids: surface-* |
+-----------------------+ +-----------------------+
```

### Event Flow (Condensed)
## Architecture Snapshot

```text
Client -> hello/auth -> list_sessions -> attach(paneId,lastSeq)
Server -> session_list -> output(snapshot/delta, streamSeq)
Client -> enable_input -> input -> disable_input
Server -> ack/error + policy_update
Remote Web / Native Client
|
| SSH transport path + WS (/ws)
v
+----------------------------------------+
| CommandRelay Gateway (Node/TS) |
| - auth + policy |
| - replay + stream sequencing |
| - input lane arbitration |
+----------------------+-----------------+
|
v
+------------------+
| tmux runtime |
| panes: %1, %2... |
+------------------+

Local optional UI: Ghostty (operator convenience only)
```

### Safety State Model
## UX Model (Operator View)

```text
DISCONNECTED
-> AUTHENTICATED_READ_ONLY
-> STREAMING_READ_ONLY
-> STREAMING_INPUT_ENABLED (explicit only)
-> READ_ONLY (disable_input / kill switch / disconnect)
+------------------------------------------------------+
| Session Tab: "backend-api" |
| +--------------------------+ +----------------------+ |
| | Pane %1 (read-only) | | Pane %2 (writer) | |
| | replay + live output | | input explicitly on | |
| +--------------------------+ +----------------------+ |
| Notifications: lane conflict, input enabled, |
| kill switch active, reconnect + replay complete |
+------------------------------------------------------+
```

## Runtime Backends (tmux + ghostty/cmux)
## Proxy Packages: Parallel Track

Configure runtime backends with:
The proxy packages (`@commandrelay/*`, `@termina/proxy-*`) are a parallel product track for outbound HTTP/proxy reuse.

```bash
COMMANDRELAY_RUNTIME_BACKENDS=tmux
# or
COMMANDRELAY_RUNTIME_BACKENDS=tmux,cmux
```
They are not mandatory for the core terminal-control path (`list/attach/replay/input`) and should be treated as adjacent infrastructure, not a prerequisite for SSH + tmux operation.

Optional cmux command override:
## Quick Start

```bash
COMMANDRELAY_CMUX_COMMAND=/opt/homebrew/bin/cmux
npm install
npm run check
npm start
```

Notes:

1. Default backend set is `tmux`.
2. In multi-backend mode, pane IDs are backend-namespaced (`tmux:%1`, `cmux:surface-1`).
3. Startup logs availability per backend.
4. Startup fails only when all configured backends are unavailable in non-tmux-only mode.

## Security Model

CommandRelay is secure-by-default:

1. Read-only mode on connect.
2. Explicit input enable required.
3. Global kill switch available.
4. Per-client input rate limits and max payload limits.
5. Pane ownership arbitration to prevent silent concurrent writers.
6. Audit logging support for auth/input/policy events.

## Quick Start
Optional SSH startup wiring (remote profile orchestration contract):

```bash
npm install
npm run check
export COMMANDRELAY_TRANSPORT_MODE=ssh
export COMMANDRELAY_SSH_PROFILE=primary
export COMMANDRELAY_SSH_TARGET="dev@relay-host"
export COMMANDRELAY_SSH_COMMAND=ssh
export COMMANDRELAY_SSH_PORT=22
export COMMANDRELAY_SSH_CONNECT_TIMEOUT_SECONDS=8
export COMMANDRELAY_SSH_STRICT_HOST_KEY_CHECKING=true
npm start
```

Default endpoints:
Current runtime data path remains the WS server (`/ws`) plus tmux runtime control.
In `ssh` mode, the bridge runs tmux operations on the remote target over SSH after startup preflight passes.
SSH runtime execution is non-interactive (`-T`, `BatchMode=yes`); when strict host key checking is disabled, runtime uses `UserKnownHostsFile=/dev/null` to suppress known_hosts writes.
`ssh` mode is tmux-only: set `COMMANDRELAY_RUNTIME_BACKENDS=tmux`.

Default local endpoints (current runtime path):

1. Health: `GET http://127.0.0.1:8787/health`
2. Web app (if enabled): `http://127.0.0.1:8787/app/`
3. WebSocket: `ws://127.0.0.1:8787/ws`
1. `GET http://127.0.0.1:8787/health`
2. `http://127.0.0.1:8787/app/` (when static app hosting is enabled)
3. `ws://127.0.0.1:8787/ws`

## Core Environment Variables
## Core Configuration

| Variable | Purpose |
| --- | --- |
| `COMMANDRELAY_AUTH_TOKEN` | Token auth for non-loopback binds |
| `COMMANDRELAY_RUNTIME_BACKENDS` | Runtime backend list (`tmux,cmux`) |
| `COMMANDRELAY_CMUX_COMMAND` | cmux executable/path override |
| `COMMANDRELAY_RUNTIME_BACKENDS` | Runtime backends (`tmux` default, optional `tmux,cmux`). Must be `tmux` when `COMMANDRELAY_TRANSPORT_MODE=ssh`. |
| `COMMANDRELAY_CMUX_COMMAND` | Optional `cmux` command/path override |
| `COMMANDRELAY_TRANSPORT_MODE` | Startup transport selector (`ws` default, `ssh` enables remote tmux execution over SSH) |
| `COMMANDRELAY_SSH_PROFILE` | SSH profile name (`primary` when unset). If set, must be non-empty and match `[A-Za-z0-9._-]+`. |
| `COMMANDRELAY_SSH_TARGET` | SSH target (required in `ssh` mode) in `[user@]host` format, where host is `name` or bracketed IPv6 (`[2001:db8::1]`). |
| `COMMANDRELAY_SSH_COMMAND` | SSH executable/command override used for preflight and runtime SSH execution (`ssh` default). |
| `COMMANDRELAY_SSH_PORT` | SSH target port override (`22` default) |
| `COMMANDRELAY_SSH_CONNECT_TIMEOUT_SECONDS` | SSH connect/runtime command timeout in seconds (`8` default, allowed `1..60`). |
| `COMMANDRELAY_SSH_STRICT_HOST_KEY_CHECKING` | SSH strict host key checking policy (`true` default) |
| `COMMANDRELAY_INPUT_KILL_SWITCH` | Global input disable switch |
| `COMMANDRELAY_ALLOW_INPUT_OVERRIDE` | Allow explicit pane ownership takeover |
| `COMMANDRELAY_MAX_INPUT_BYTES` | Max input payload bytes |
| `COMMANDRELAY_ALLOW_INPUT_OVERRIDE` | Allow/deny forced lane takeover |
| `COMMANDRELAY_MAX_INPUT_BYTES` | Max input payload size |
| `COMMANDRELAY_MAX_MSG_PER_MIN` | Per-client message rate limit |
| `COMMANDRELAY_MAX_INPUT_PER_MIN` | Per-client input rate limit |
| `COMMANDRELAY_STRICT_PROTOCOL_PARSING` | Strict envelope parsing toggle |
| `COMMANDRELAY_APP_STATIC_ENABLED` | Enable/disable static web app hosting |
| `COMMANDRELAY_APP_STATIC_DIR` | Static app root |
| `COMMANDRELAY_STRICT_PROTOCOL_PARSING` | Strict v1 envelope parsing |
| `COMMANDRELAY_AUDIT_LOG` | Audit JSONL path |
| `HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY`/`NO_PROXY` | Outbound proxy settings |

## Protocol and Behavior

Primary protocol docs:

1. `docs/protocol-v1.md` (normative contract)
2. `docs/protocol.md` (operator-facing summary)
3. `docs/security.md` (threat model + controls)

`list_sessions` behavior in multi-backend mode:

1. `payload.panes[]` include backend-aware pane ids.
2. `payload.sessions[]` are grouped by `(backendId, sessionName)` to avoid cross-backend session-name collisions.

## Validation

Use these for repeatable validation (not date-bound):

```bash
npm run check:root
npm run test:root
npm run ci:all
```

Targeted protocol/runtime checks:

```bash
node --import tsx --test src/protocol.conformance.test.ts
node --import tsx --test src/server/ws-contract-matrix.test.ts
node --import tsx --test src/server/bridge-server.policy.test.ts
```

## Project Structure

```text
src/
bridge/ replay + delta streaming engine
server/ ws/http gateway, policies, contract tests
runtime/ runtime mux + cmux adapter
tmux/ tmux adapter
net/ proxy routing and agent factory adapters
packages/
cli-proxy/
proxy-core/
proxy-agent/
proxy-fetch/
proxy-http-client/
proxy-undici/
docs/
protocol, security, operations, roadmap, proxy ecosystem
apps/
ios/, android/, web/
```

## Documentation Map

1. `docs/README.md` - full docs index
2. `docs/getting-started.md` - setup and runbook
3. `docs/operations.md` - operations and runtime handling
4. `docs/roadmap-native.md` - iOS/Android/macos/web rollout
5. `docs/proxy-ecosystem-roadmap.md` - proxy package expansion + discovery/use strategy

## Project Status

The core TypeScript gateway is implemented, tested, and production-oriented for the tmux/cmux runtime path.

Active work continues on:
## Docs

1. Native client parity and UX hardening.
2. Multi-runtime and control-lane reliability.
3. Externalized `@commandrelay` / `@termina` proxy package line, with P1 (`@termina/proxy-undici`, `@termina/cli-proxy`, `@termina/proxy-fetch`) implemented and validated.
1. `docs/protocol-v1.md` - normative wire contract
2. `docs/security.md` - controls and threat notes
3. `docs/operations.md` - deployment and operator runbook
4. `docs/roadmap-native.md` - web/native parity roadmap
5. `docs/proxy-ecosystem-roadmap.md` - proxy package track

## License

Expand Down
21 changes: 16 additions & 5 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This folder contains project documentation for users, contributors, and operator
## Runtime Snapshot

1. Gateway runtime: TypeScript on Node.js `>=22` (`tsx` execution, `tsc --noEmit` checks).
2. Gateway transport package: `ws`.
2. SSH-first transport with current WS runtime path: data plane remains WebSocket (`/ws`), and `ssh` mode executes tmux runtime operations on the remote target after startup preflight passes (see `ssh-transport-contract.md` and ADR-001).
3. Outbound proxy package set: `http-proxy-agent`, `https-proxy-agent`, `socks-proxy-agent`, `pac-proxy-agent`.
4. Client ecosystem direction: iOS (Swift) first, Android (Kotlin) second, web fallback last.

Expand All @@ -33,6 +33,13 @@ Local MCP note:
5. Planner: read [Native Roadmap](roadmap-native.md) and [Execution TODO](TODO.md).
6. Release owner: read [Proxy Publish Runbook](release/proxy-publish.md) and capture outputs in weekly checkpoints.

## Quickstart References

1. [getting-started.md](getting-started.md): runtime quickstart and live environment setup.
2. [operations.md](operations.md): operator runbook and runtime behavior details.
3. SSH startup config keys: `COMMANDRELAY_TRANSPORT_MODE`, `COMMANDRELAY_SSH_PROFILE`, `COMMANDRELAY_SSH_TARGET`, `COMMANDRELAY_SSH_COMMAND`, `COMMANDRELAY_SSH_PORT`, `COMMANDRELAY_SSH_CONNECT_TIMEOUT_SECONDS`, `COMMANDRELAY_SSH_STRICT_HOST_KEY_CHECKING`.
4. Tunnel helper runbook: [../scripts/ssh/README.md](../scripts/ssh/README.md).

## Current Execution Baselines

1. iOS protocol mock package path: `apps/ios/M0ProtocolMockClient` (`swift test`).
Expand All @@ -58,7 +65,11 @@ Local MCP note:
9. [roadmap-native.md](roadmap-native.md)
10. [macos-menu-bar-control-lane-spec.md](macos-menu-bar-control-lane-spec.md)
11. [control-lane-parity-checklist.md](control-lane-parity-checklist.md)
12. [proxy-ecosystem-roadmap.md](proxy-ecosystem-roadmap.md)
13. [research-next-opportunities.md](research-next-opportunities.md)
14. [TODO.md](TODO.md)
15. [release/proxy-publish.md](release/proxy-publish.md)
12. [controlled-input-audit.md](controlled-input-audit.md)
13. [proxy-ecosystem-roadmap.md](proxy-ecosystem-roadmap.md)
14. [research-next-opportunities.md](research-next-opportunities.md)
15. [TODO.md](TODO.md)
16. [release/proxy-publish.md](release/proxy-publish.md)
17. [ssh-transport-contract.md](ssh-transport-contract.md)
18. [adr/ADR-001-ssh-first-transport.md](adr/ADR-001-ssh-first-transport.md)
19. [architecture/host-state-authority-plan.md](architecture/host-state-authority-plan.md)
Loading
Loading