acp-ws-bridge
WebSocket-to-stdio bridge for relaying Agent Client Protocol (ACP) JSON-RPC messages between a remote client (e.g., iOS app) and GitHub Copilot CLI.
┌─────────────────┐ stdio ┌─────────────────────┐ WebSocket ┌─────────────────┐
│ GitHub Copilot │◄──────────────────► │ acp-ws-bridge │◄──────────────────────►│ Remote Client │
│ CLI (Host) │ JSON-RPC/NDJSON │ (Rust Server) │ JSON-RPC/NDJSON │ (iOS App) │
└─────────────────┘ └─────────────────────┘ └─────────────────┘
Features
- Transparent relay — passes all ACP JSON-RPC messages without modification
- WebSocket + optional TLS — secure remote connections
- REST API — session history, stats, Copilot CLI usage metrics and capabilities
- Copilot CLI version detection — detects CLI version at startup, exposes via API
- Ping/pong keepalive — detects dead connections
- Session management — tracks active sessions with
--resumesupport
Compatibility
| Copilot CLI Version | Status |
|---|---|
| 1.0.x (GA) | ✅ Fully supported |
| 0.0.418 – 0.0.423 | ✅ Fully supported |
| < 0.0.418 (pre-GA) | ⚠️ Basic relay works; some features may be missing |
New ACP protocol methods (exitPlanMode.request, MCP elicitations, reasoning effort config) are transparently relayed without bridge changes.
Quick Start
# Build
# Run (starts Copilot CLI via stdio, listens on WebSocket port 8765)
# With TLS (enables wss:// for WebSocket and HTTPS for REST API)
# Generate cert.pem/key.pem with mkcert (after `mkcert -install`)
# With custom API port
# With an exact ACP command override (alias: --command)
# With a custom Copilot data directory
Install
# Install from crates.io
Prebuilt release binaries are published on GitHub Releases for:
x86_64-unknown-linux-gnux86_64-apple-darwinaarch64-apple-darwinx86_64-pc-windows-msvc
Configuration
| Flag | Default | Description |
|---|---|---|
--ws-port |
8765 | WebSocket listen port |
--api-port |
8766 | REST API listen port |
--tls-cert |
— | TLS certificate path (enables wss:// and HTTPS) |
--tls-key |
— | TLS private key path |
--generate-cert |
— | Generate self-signed certificate and exit |
--cert-hostnames |
localhost,127.0.0.1 |
Hostnames for self-signed certificate |
--copilot-path |
copilot |
Path to Copilot CLI executable when using the default spawned command |
--copilot-args |
— | Extra args appended to the default spawned Copilot CLI command |
--acp-command, --command |
— | Exact Copilot ACP command override, parsed without shell execution |
--copilot-mode |
stdio |
Copilot transport mode (stdio or tcp) |
--copilot-host |
127.0.0.1 |
Copilot CLI host in TCP mode |
--copilot-port |
3000 |
Copilot CLI port in TCP mode |
--spawn-copilot |
true |
Disable this to connect to an already-running Copilot CLI instance |
--copilot-dir |
~/.copilot |
Copilot data directory for session history, session-state, and stats cache files |
When --tls-cert and --tls-key are provided, both the WebSocket server (wss://) and the REST API (HTTPS) use the same TLS configuration.
If --acp-command / --command is set, it takes precedence over --copilot-path and --copilot-args. The provided command is treated as an exact override, so it must already include the ACP transport flags you want the spawned process to use, and in TCP mode it must match the configured --copilot-port.
REST API
The REST API runs on a separate port (default: --ws-port + 1).
| Endpoint | Description |
|---|---|
GET /health |
Health check with bridge version, Copilot CLI version, uptime |
GET /api/sessions |
List active WebSocket sessions |
GET /api/sessions/:id |
Get session details |
DELETE /api/sessions/:id |
Delete a session |
GET /api/sessions/:id/commands |
Get cached ACP commands for a session |
GET /api/stats |
Aggregate session statistics |
GET /api/copilot/info |
Copilot CLI version, path, mode, GA status, feature capabilities |
GET /api/copilot/usage |
Copilot CLI usage statistics (model usage, tool executions) |
GET /api/history/sessions |
Historical sessions from the configured Copilot data directory |
GET /api/history/sessions/:id/turns |
Session conversation turns |
GET /api/history/stats |
Aggregate historical statistics |
Project Docs
Generate TLS Cert/Key
If you installed acp-ws-bridge from crates.io or GitHub Releases and just need cert.pem / key.pem for local TLS, mkcert is the simplest option on macOS and Linux.
macOS
nss is only needed if you want Firefox and other NSS-based clients to trust the local CA.
Linux
Install mkcert from your distro package manager or the upstream release, plus the NSS tools package if you want Firefox and other NSS-based clients to trust the local CA.
# Debian / Ubuntu
# Fedora
# Arch Linux
# One-time local CA install
Generate a certificate and key for every hostname or IP your clients will use:
Replace 192.168.0.100 and bridge-host.example.com with the LAN IP and DNS name clients actually use.
If a client connects from another device, that device must also trust the mkcert root CA. Use mkcert -CAROOT to locate the CA files if you need to import them on another machine or device.
Once the files exist, start the installed binary with:
The bridge loads these PEM files directly and should not need macOS keychain identity access for the server certificate.
Run in background with:
&
Tailscale Serve (Reverse Proxy)
If the machine running the bridge is on a Tailscale tailnet, you can skip certificate management entirely and let Tailscale provide automatic HTTPS/WSS with a valid Let's Encrypt certificate.
Run the bridge without --tls-* flags (plain HTTP/WS):
Then use tailscale serve to expose both ports over HTTPS:
# Reverse-proxy HTTPS → local WebSocket port (wss:// for clients)
# Reverse-proxy HTTPS on /api → local REST API port
Clients on the tailnet connect to:
wss://<machine-name>.<tailnet-name>.ts.net/
https://<machine-name>.<tailnet-name>.ts.net/api/health
Tailscale handles TLS termination, certificate renewal, and access control — no mkcert, no self-signed certs, no keychain prompts.
To stop serving:
See tailscale serve docs for more options.
Release Process
Releases are tag-driven and deterministic:
- Bump
versioninCargo.toml(manual semver). - Merge to
main. - Create and push a matching tag:
vX.Y.Z.
The release workflow verifies the tag matches Cargo.toml, runs strict checks (fmt, clippy -D warnings, test, package dry-run), publishes to crates.io using the CARGO_REGISTRY_TOKEN GitHub secret, and uploads platform binaries to GitHub Releases.
Contributing and Support
- Please read CONTRIBUTING.md before opening a pull request.
- Use the GitHub bug report and feature request templates for public issues.
- Report vulnerabilities privately according to SECURITY.md.
- Community interactions in this repository follow CODE_OF_CONDUCT.md.
Roadmap
Near-term repository improvements are focused on OSS maintainability:
- keep contributor-facing docs current as the bridge evolves
- improve quality visibility through the dedicated coverage workflow
- evaluate future release automation and additional examples only after the contributor baseline is stable
License
MIT