| .. | ||
| cmd/cmuxd-remote | ||
| go.mod | ||
| README.md | ||
cmuxd-remote (Go)
Go remote daemon for cmux ssh bootstrap, capability negotiation, and remote proxy RPC. It is not in the terminal keystroke hot path.
Commands
cmuxd-remote versioncmuxd-remote serve --stdiocmuxd-remote cli <command> [args...]— relay cmux commands to the local app over the reverse SSH forward
When invoked as cmux (via wrapper/symlink installed during bootstrap), the binary auto-dispatches to the cli subcommand. This is busybox-style argv[0] detection.
RPC methods (newline-delimited JSON over stdio)
hellopingproxy.openproxy.closeproxy.writeproxy.stream.subscribe- async
proxy.stream.data/proxy.stream.eof/proxy.stream.errorevents session.opensession.closesession.attachsession.resizesession.detachsession.status
Current integration in cmux:
workspace.remote.configurenow bootstraps this binary over SSH when missing.- Client sends
hellobefore enabling remote proxy transport. - Local workspace proxy broker serves SOCKS5 + HTTP CONNECT and tunnels stream traffic through
proxy.*RPC overserve --stdio, using daemon-pushed stream events instead of polling reads. - Daemon status/capabilities are exposed in
workspace.remote.status -> remote.daemon(includingsession.resize.min).
workspace.remote.configure contract notes:
port/local_proxy_portaccept integer values and numeric strings; explicitnullclears each field.- Out-of-range values and invalid types return
invalid_params. local_proxy_portis an internal deterministic test hook used by bind-conflict regressions.- SSH option precedence checks are case-insensitive; user overrides for
StrictHostKeyCheckingand control-socket keys prevent default injection.
Distribution
Release and nightly builds publish prebuilt cmuxd-remote binaries on GitHub Releases for:
darwin/arm64darwin/amd64linux/arm64linux/amd64
The app embeds a compact manifest in Info.plist with:
- exact release asset URLs
- pinned SHA-256 digests
- release tag and checksums asset URL
Release and nightly apps download and cache the matching binary locally, verify its SHA-256, then upload it to the remote host if needed. Dev builds can opt into a local go build fallback with CMUX_REMOTE_DAEMON_ALLOW_LOCAL_BUILD=1.
To inspect what a given app build trusts, run:
cmux remote-daemon-statuscmux remote-daemon-status --os linux --arch amd64
The command prints the exact release asset URL, expected SHA-256, local cache status, and a copy-pasteable gh attestation verify command for the selected platform.
CLI relay
The cli subcommand (or cmux wrapper/symlink) connects to the local cmux app through an SSH reverse forward and relays commands. It supports both v1 text protocol and v2 JSON-RPC commands.
Socket discovery order:
--socket <path>flagCMUX_SOCKET_PATHenvironment variable~/.cmux/socket_addrfile (written by the app after the reverse relay establishes)
For TCP addresses, the CLI dials once and only refreshes ~/.cmux/socket_addr a single time if the first address was stale. Relay metadata is published only after the reverse forward is ready, so steady-state use does not rely on polling.
Authenticated relay details:
- Each SSH workspace gets its own relay ID and relay token.
- The app runs a local loopback relay server that requires an HMAC-SHA256 challenge-response before forwarding a command to the real local Unix socket.
- The remote shell never gets direct access to the local app socket. It only gets the reverse-forwarded relay port plus
~/.cmux/relay/<port>.auth, which is written with0600permissions and removed when the relay stops.
Integration additions for the relay path:
- Bootstrap installs
~/.cmux/bin/cmuxwrapper and keeps a default daemon target (~/.cmux/bin/cmuxd-remote-current). - A background
ssh -N -Rprocess reverse-forwards a TCP port to the authenticated local relay server. The relay address is written to~/.cmux/socket_addron the remote. - Relay startup writes
~/.cmux/relay/<port>.daemon_pathso the wrapper can route each shell to the correct daemon binary when multiple local cmux instances or versions coexist. - Relay startup writes
~/.cmux/relay/<port>.authwith the relay ID and token needed for HMAC authentication.