Execute commands on a remote host over an authenticated WebSocket connection.
Primary use case: You are using ssh remote development with VSCode or Zed and
want to execute commands on the host running VSCode/Zed UI. For example to copy
things to clipboard with pbcopy. Ex. start the server on macOS and execute the
client on your linux devbox.
Features:
- stdin/stdout/stderr streaming
- command allow listing
- built-in daemon mode
- easy ssh port-forwarding with
--ssh-forward - optional local environment forwarding with
hostcmd exec --forward-env
# Start a server
hostcmd server --secret s3cret --port 8080 --host 127.0.0.1
# Execute a command through it
hostcmd exec --secret s3cret --port 8080 --host 127.0.0.1 uname -aDownload latest release binary to ~/.local/bin with:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/esamattis/hostcmd/refs/heads/main/scripts/bin-install.sh)"The client initiates a WebSocket connection to the server's /ws endpoint,
authenticating via a shared secret passed in the Authorization: Bearer <secret>
HTTP header during the upgrade request. If the secret is invalid the server
rejects the upgrade with HTTP 401 — a successful connection implies
authentication passed.
Once connected, the client sends an Exec frame containing the command and
arguments to run. The server spawns the command and begins streaming: stdin data
flows from client to server to the spawned process, while stdout and stderr are
streamed back to the client. When the process exits, the server sends the exit
code and the client terminates with the same code.
sequenceDiagram
participant Client as hostcmd exec
participant Server as hostcmd server
participant Cmd as Spawned Command
Client->>Server: WebSocket upgrade + Bearer auth
Note over Client,Server: Auth via Authorization header
Client->>Server: Execute command + args
loop Stdin forwarding
Client->>Server: Stdin data
Server->>Cmd: Stdin data
end
Client->>Server: Stdin EOF
Server->>Cmd: Close stdin
loop Output streaming
Cmd-->>Server: Stdout/Stderr
Server-->>Client: Stdout/Stderr
end
Cmd-->>Server: Exit code
Server-->>Client: Exit code
Note over Client: Exits with remote exit code
Start a WebSocket command execution server.
hostcmd server --secret <secret> --port <port> --host <host> [OPTIONS]| Flag | Env | Description |
|---|---|---|
--secret <secret> |
HOSTCMD_SECRET |
Required. Shared secret for client authentication. |
--port <port> |
HOSTCMD_PORT |
Required. TCP port to listen on. |
--host <host> |
HOSTCMD_HOST |
Required. Host address to bind to. |
--log-file <file> |
HOSTCMD_LOG_FILE |
Optional. File that receives server log output instead of stdout. |
--pid-file <file> |
HOSTCMD_PID_FILE |
Optional. File that receives the running server process id. |
--daemon |
- | Optional. Start the server in the background as a daemon process. |
--ssh-forward <hostname> |
HOSTCMD_SSH_FORWARD |
Optional. SSH hostname for reverse port forward (runs ssh <hostname> -R <port>:<host>:<port> -N -o ExitOnForwardFailure=yes; server exits if SSH exits). |
--ssh-forward-port <port> |
HOSTCMD_SSH_FORWARD_PORT |
Optional. SSH port used with --ssh-forward (adds -p <port> to the ssh command). |
--allow <cmd> |
HOSTCMD_ALLOW |
Optional. Command names allowed for remote execution (repeatable). |
--pid-file defaults to ~/.local/share/hostcmd/server.pid.
When --daemon is used and no --log-file is set, logs are written to ~/.local/share/hostcmd/server.log.
The --ssh-forward flag starts an SSH reverse port forward alongside the server. The server runs ssh <hostname> -R <port>:<host>:<port> -N -o ExitOnForwardFailure=yes, making the server reachable from the remote SSH host. The server and SSH process are coupled: if the SSH process exits for any reason (connection drop, authentication failure, remote shutdown), the server exits immediately as well. Use --ssh-forward-port to specify a non-default SSH port (adds -p <port> to the command).
The --allow flag can be repeated to allow multiple commands. Via the environment variable, use comma-separated values: HOSTCMD_ALLOW=pbcopy,uname. When any allow rule is set, the server rejects commands whose executable name is not listed.
# Foreground server
hostcmd server --secret my-secret --port 8080 --host 127.0.0.1
# Daemon with SSH forwarding and restricted commands
hostcmd server --secret my-secret --port 8080 --host 127.0.0.1 \
--daemon --ssh-forward MY_HOST --ssh-forward-port 2222 --allow pbcopy --allow unameExecute a command through a remote WebSocket server.
hostcmd exec --secret <secret> --port <port> --host <host> [--] <command> [args...]| Flag | Env | Description |
|---|---|---|
--secret <secret> |
HOSTCMD_SECRET |
Required. Shared secret for authenticating with the server. |
--port <port> |
HOSTCMD_PORT |
Required. TCP port to connect to. |
--host <host> |
HOSTCMD_HOST |
Required. Host address to connect to. |
-f, --forward-env <name> |
- | Optional. Forward one local environment variable to the spawned server-side command. Repeat to forward multiple variables. |
[--] <command> [args...] |
HOSTCMD_COMMAND |
Required. Command and arguments to execute on the server. |
Stdin is forwarded to the remote command. Stdout and stderr are mirrored locally. The exit code of the remote command becomes the exit code of hostcmd exec.
Use --forward-env <name> to copy a local environment variable into the spawned server-side command. Each named variable must exist in the local client environment. Repeat the flag to forward multiple variables.
Pressing Ctrl-C sends a cancellation request to the server and exits with code 130.
# Run a command
hostcmd exec --secret my-secret --port 8080 --host 127.0.0.1 uname -a
# Pipe stdin to the remote command
echo -n "copy me" | hostcmd exec --secret my-secret --port 8080 --host 127.0.0.1 pbcopy
# Forward local environment variables to the remote command
DISPLAY=:0 SSH_AUTH_SOCK=/tmp/ssh.sock \
hostcmd exec --secret my-secret --port 8080 --host 127.0.0.1 \
-f DISPLAY -f SSH_AUTH_SOCK -- printenv DISPLAY
# Using environment variables for connection defaults
export HOSTCMD_SECRET=my-secret HOSTCMD_PORT=8080 HOSTCMD_HOST=127.0.0.1
hostcmd exec ls -laStop a daemon server using its pid file.
hostcmd stop [OPTIONS]| Flag | Env | Description |
|---|---|---|
--pid-file <file> |
HOSTCMD_PID_FILE |
Optional. File that contains the daemon server process id. |
--pid-file defaults to ~/.local/share/hostcmd/server.pid.
Use --pid-file when the daemon was started with a custom pid file path.
Most connection and server flags have a corresponding environment variable. Export them once and run commands without repeating those settings:
export HOSTCMD_SECRET=my-secret
export HOSTCMD_PORT=8080
export HOSTCMD_HOST=127.0.0.1
export HOSTCMD_SSH_FORWARD_PORT=2222Then the server and client commands reduce to:
hostcmd server --daemon
hostcmd exec uname -a
echo "hello" | hostcmd exec pbcopy
hostcmd stopThis is especially useful when hostcmd exec is invoked by other tools that are unaware of the connection details.
hostcmd exec --forward-env is intentionally CLI-only because it copies variables from the current local client environment by name.
Commands spawned by the server receive these additional environment variables, plus any local variables explicitly forwarded with hostcmd exec --forward-env:
| Variable | Value | Description |
|---|---|---|
HOSTCMD_CLIENT_HOSTNAME |
Client's hostname | Hostname of the machine that initiated the execution request |
HOSTCMD_CLIENT_USERNAME |
Client's username | Username of the user that initiated the execution request |
HOSTCMD_CLIENT_CWD |
Client's current working directory | Working directory of the client process that initiated the execution request |
HOSTCMD_CLIENT_EXEC |
true |
Marker indicating the command was launched through hostcmd |
Yes, this project was made with the help of AI agents.