Subshell Docs
Get Started

Install the Control Plane

Three ways to get a server running: the one-liner, the Subshell Server desktop app, and doing it by hand.

The control plane ships as one self-contained binary per platform with its web UI embedded: no Bun, no checkout, no separate frontend to serve. Published targets are linux-x64, linux-arm64 and darwin-arm64; Intel Macs are refused by name, not silently mis-resolved. Pick the door that fits the machine:

The one-liner (headless)

From a fresh shell on the machine that will host the server:

curl -fsSL https://raw.githubusercontent.com/subshell-ai/subshell/main/install-server.sh | bash

The script detects your platform, finds the newest cli-server-vX.Y.Z GitHub release, downloads subshell-server-cli-<triple>, verifies the release's .sha256 sidecar before the first chmod +x, installs the binary to ~/.local/bin/subshell-server, and hands over to subshell-server init. Nothing it downloaded can execute until its bytes match the digest the release published.

Because a piped script has no arguments, every question it or init would ask can be pre-answered from the environment; every one except the database path, which keeps its default unless you run init --db-path yourself:

VariableEffect
SUBSHELL_SERVER_VERSIONinstall this exact X.Y.Z instead of the newest
SUBSHELL_SERVER_RELEASE_APIreleases listing URL (default: GitHub's)
SUBSHELL_SERVER_RELEASE_BASEdirectory holding the release assets
SUBSHELL_SERVER_PORT / _HOST / _BASE_URL / _TRUSTED_ORIGINSbecome init --port / --host / --base-url / --trusted-origins
SUBSHELL_NO_SERVICE=1skip the service question (init --no-service)

What init asks, and what it writes

init is the whole setup sequence. It creates the config home at ~/.config/subshell-server (0700) and writes config.env (0600), generating a BETTER_AUTH_SECRET of 32 random bytes exactly once; a second run keeps what is there. It then walks the configure questions (port, bind address, base URL, extra origins, database path; each defaults, so Enter is a valid answer) and finally asks the one that matters:

Run Subshell Server in the background and start it at login?

Yes (the default) installs a per-user service without sudo: a systemd user unit on Linux, a launchd agent on macOS. On Linux it also checks whether your account lingers, and prints loginctl enable-linger $USER only when it doesn't: without lingering, the server dies at logout and never comes back on a headless reboot. --yes and non-interactive runs take the default.

It ends by naming the address:

Open http://localhost:3080/setup in a browser to create the admin account.

Open it promptly

The setup endpoints are public until the first account exists; that is what lets you create it. On a LAN, the first browser through wins the admin account. The window closes the moment someone walks in.

And if you will browse from another machine: the default bind (0.0.0.0) makes the server reachable on the LAN, but a browser may sign in only from an address the instance knows. When that applies, init says so and prints the exact subshell-server configure --trusted-origins <address> line to run. Networking & Addresses owns the whole subject.

Subshell Server desktop app

For a Mac or Linux desktop where you want buttons: the Subshell Server app (a signed, notarized .dmg on macOS 13+, a .deb on Linux x86-64) ships the server binary inside it; nothing is downloaded on first run. On a machine with nothing installed it opens a setup assistant. Every first run shows "Welcome to Subshell", then Install tmux; with tmux present it costs one press of Continue, but the prerequisite gets named. It runs brew install tmux on a Mac with Homebrew, and pkexec with your desktop's password prompt on Debian/Ubuntu. On macOS a screen called What macOS Will Ask explains the permissions it will request, then Set Up Subshell Server asks the one real question (below) and, on its single press, installs the bundled subshell-server to ~/.local/bin, writes a config.env, and starts it, registering the service only in background mode. Port and addresses sit behind "Customize port and addresses…".

Two choices there mirror init's question: run In the background (a launchd agent or systemd user service, alive whether or not the app is open) or With the Subshell Server app (quitting the app stops the server; running subshells keep running), plus a Start at login checkbox. The dashboard then opens in the same window, pointed at the server's own loopback address, and the web wizard continues the assistant's dot row: your account, your agents, your first subshell. See Server service & autostart for changing your mind later.

Doing it by hand

Same artifacts, no script. Download the two files for your triple from the release page (here darwin-arm64; the cli in the name marks the bare binary, not the desktop app that wraps it):

gh release download cli-server-vX.Y.Z -p 'subshell-server-cli-darwin-arm64*'

The .sha256 sidecar is a bare 64-hex digest, not the <hash> <name> line checkers expect; pair the two yourself:

printf '%s  %s\n' "$(cat subshell-server-cli-darwin-arm64.sha256)" \
  subshell-server-cli-darwin-arm64 | shasum -a 256 -c -

Then install and initialize:

install -m755 subshell-server-cli-darwin-arm64 ~/.local/bin/subshell-server
subshell-server init
subshell-server status

status is the first thing to run when anything looks wrong: it prints the config file and the layer each setting came from (masking the secret), tmux presence, port liveness, the service definition, and whether the admin account exists yet. It reads only; it never boots anything.

Docker

For a container host, the repository ships a compose recipe with two prerequisites it will refuse without: a BETTER_AUTH_SECRET of at least 32 characters in .env, and a docker/gitconfig copied from the tracked example. Then docker compose build && docker compose up -d serves port 3080. Data bind-mounts from ~/.config/subshell-server so the database and pane logs survive, and the harness CLI is mounted in, not bundled: the image doesn't ship Claude Code or the others. The honest caveat: a container restart ends tmux state, so running subshells die with it. Docker covers the volumes and limits.

See also

Edit on GitHub

Last updated on

On this page