Saphan StudioDocs
Deployment

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

curl -fsSL https://saphan.ai/install.sh | sh

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 --:

curl -fsSL https://saphan.ai/install.sh | sh -s -- --prefix <dir>

⛔ | 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.

flagwhat 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-onlyinstall 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-servebring 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-downgradeinstall a version older than the one already installed
--verbose, --no-openshow every step on the terminal; never open a browser

3 · What it verifies before it writes anything

  1. SHA256SUMS from the install source;
  2. 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/workspace machine-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:

Finish your setup in the console. Open http://localhost:8788/setup

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.