An install, end to end
One command, what it verifies before it writes anything, what it sets up, and the page it hands you — the whole path in the order you meet it.
This is the whole path, in the order you meet it. Each step links the page that goes deeper.
1 · One command
That is the install. There is no address to look up, no port to pick and nothing to compose — the line is the same on every machine you install.
Run as an ordinary user, it installs for that user, under ~/.saphan/. Run as root —
curl -fsSL https://saphan.ai/install.sh | sudo sh — it installs machine-wide, under
/opt/saphan/. It says which before it starts.
2 · The shell is sh, and that is not a detail
The script is POSIX sh and self-contained, so | sh is right. It matters because sh exists
where bash does not: a minimal container, an Alpine image, a stripped host.
To pass a flag through the pipe you need -s --:
⛔ | sh --prefix … does not pass the flag to the script — the shell takes it. This is the one
place where a correct-looking line does something else.
| flag | what it does |
|---|---|
--prefix <dir> | where the binaries go. It moves the executables only; configuration, keys and logs follow the install mode (per user or machine-wide) |
--agent-only | install only saphan-agent, for a machine that only runs agent workloads |
--channel <name> | install the version a release channel names (stable or preview), over https only |
--from <path or URL> | install from a distribution tree you name: a directory, a file:// URL or an http(s) URL laid out as SHA256SUMS plus <os>-<arch>/<binary> |
--serve / --no-serve | bring the setup page up afterwards, or start nothing and print the commands instead |
--csr / --no-csr, --machine <id> | generate this machine's certificate request even without a terminal, never generate it, or name the machine id it carries |
--allow-downgrade | install a version older than the one already installed |
--verbose, --no-open | show every step on the terminal; never open a browser |
3 · What it verifies before it writes anything
SHA256SUMSfrom the install source;- each binary of the machine payload —
saphan,saphan-agent,saphan-gateway,saphan-server— fetched for this machine's platform.
Each binary is checked against the manifest before anything is installed, and a mismatch
refuses with nothing installed. When the source publishes a version manifest, a version
older than the one already installed is refused too, unless you pass --allow-downgrade.
⚠ The checksums are trusted because of TLS, and nothing more. SHA256SUMS comes from the
same host as the binaries, so a host able to serve a bad binary is able to serve its matching
sum. The releases are not independently signed today.
The copy into the install directory is a same-directory temp file and a rename — never an
in-place overwrite, which on macOS can poison the per-inode code-signature cache and kill the next
run. Every binary and the install directory are set to mode 0755 explicitly; a group- or
world-writable directory would make saphan refuse to start with custody-binary-unsafe.
4 · What you see, and what is written
A person at a terminal sees one progress line, and every step goes to a log —
~/.saphan/install.log (/opt/saphan/install.log machine-wide). A refusal, an interruption or
a step that did not complete is printed with the log's path; --verbose keeps every step on the
screen instead.
With no controlling terminal — a provisioning run, CI — nothing is started, nothing is quiet, and the script prints the commands that would set the machine up instead.
⚠ A running Saphan Studio of the same installation refuses the install on the path a person takes: the console or server of a previous install, still running, is named with its process ids and the line that stops them, and nothing is downloaded. Stop it, then run the line again.
5 · It sets the machine up underneath
On the path a person takes, after the binaries land it:
- generates this machine's certificate signing request the first time — when no device key exists
yet — beside the key, as
~/.saphan/machine/<id>.csr; the id is the short hostname unless you pass--machine. That request is the first act of admitting the machine to a fleet (Fleet machines); - creates the workspace —
~/saphan, or/opt/saphan/workspacemachine-wide — its bridge and its record, and places the deployment files (container compose files and a runbook) under~/.saphan/docker. A folder that is already a Saphan Studio workspace is set up again in place; any other non-empty folder is left alone; - records the console's port and this installation's act authority in its configuration;
- starts the console (
saphan-gateway) and the server (saphan-server).
Nothing here makes either process come back after a reboot — that is
saphan service, and the setup page offers it.
6 · It ends by naming the address you set the machine up at
The last line is the setup address:
It is https instead when the machine already holds a fleet intermediate certificate to issue
the console a certificate from. It says localhost, never 127.0.0.1, and that is not cosmetic: a passkey cannot be bound to an
IP address, so a console opened at the address form reads but can never sign. On a machine where no
browser can open — an ssh session — it also prints the port forward that reaches the page from
another computer. Under it, it prints the four acts that stand between a fresh machine and a
console that can sign; the console's Welcome screen carries the same list.
⚠ The port is 8788 unless something already listens there; then the install refuses before
creating anything and tells you to pick another with SETUP_PORT.
⇒ From there the machine is configured in a browser: First run: setting the machine up at /setup.
7 · Then check what you got
Verifying an install — and read that page rather than trusting a version string, because a version check has been observed passing on a binary that behaved as older code.
If the command refuses
Every refusal names what it measured and what to do about it. A refusal before the binaries land leaves nothing installed — you fix the thing it named and run the same line again.
⚠ The one case worth knowing in advance is a checksum mismatch. The installer verifies every binary against the manifest before it writes anything, and on a mismatch it refuses with nothing installed rather than continuing. That is the check working, not a broken download.
Machines that are not in the same room
Running work on machines elsewhere, over a private overlay network between them: what joining gives a machine, what it does not give, and what the fleet can see about each one.
What the installer does
What the one line fetches, what it verifies before anything lands, and the admission request it makes for this machine.