Skip to main content

Quickstart: your first cell

A cell is a complete instance of System9: its own databases, its own object store, its own service processes, its own identity. It is the unit of deployment, not a worker node, and one cell is a whole working platform.

This page takes you from nothing to a cell that is serving, using the real startup contract. When it is done, attach a device with Quickstart: your first device.

How System9 is distributed​

System9 ships as machine images, not containers. A cell is a full instance of the platform baked into a bootable image, so the unit of distribution is a cloud image or an on-premises VM image rather than a container you compose. That is a deliberate consequence of the sovereignty posture: an image boots into a complete platform with no registry to reach and no orchestrator to depend on.

TargetForm
CloudA machine image in your own account (AWS, Azure, or GCP)
On-premisesA VM image for your own hypervisor
Edge siteThe same cell image, racked locally at the site for devices to connect to

Access is controlled. The image and the licence for your target platform come from your System9 account team.

This is not an open-source install

You are not cloning a repository and running a binary. The cell is a licensed, sealed image: security-critical modules are compiled to native extensions before the image is sealed, and the licence verification key is embedded at build time.

What you need​

RequirementDetail
A hostA cloud instance or a VM that can run the image
A licenceAn s9lic. key, as the S9_LICENSE_KEY environment variable or a file at config/license.s9lic
An administrator passwordS9_ADMIN_PASSWORD. Bring-up refuses to proceed without it
NetworkNone required. A cell needs no egress to start, license itself, or enforce policy

The environment contract​

The cell reads its whole first-run configuration from the environment. Set these before first boot.

# Required
export S9_LICENSE_KEY="s9lic.…" # or place the licence at config/license.s9lic
export S9_ADMIN_PASSWORD="…" # required; bring-up exits without it

# Optional, with the defaults the cell will use
export S9_ADMIN_USERNAME="admin" # default: admin
export S9_ADMIN_NAME="Administrator" # default: Administrator
export S9_ADMIN_EMAIL="" # default: empty
export S9_NODE_NAME="" # default: derived
The administrator password is not optional

On the licensed path the cell logs S9_ADMIN_PASSWORD environment variable is required for bootstrap and exits rather than minting an estate with no human root of authority. This is deliberate: a cell that provisioned itself an unreachable administrator would be an estate nobody owns.

Boot it​

Start the orchestrator. The image does this for you as a service; the explicit form is:

python main.py start      # also: stop | restart

start is single-instance guarded. A second concurrent start fails loudly with the remedy rather than silently contending for the same ports.

What happens on first boot​

A startup controller runs before the orchestrator launches any service. It inspects the environment and decides what kind of start this is.

Figure: first-boot decision. The controller detects its cloud environment, then its own startup state, and takes one of five paths. A production image with no licence and no join endpoint refuses to start rather than running ungoverned.

Table equivalent

Detected stateTriggerAction
Fresh with licenceA licence key is present and no identity is on diskFull bootstrap: mint the organization, node, cell, administrator, and crypto
Fresh with joinS9_JOIN_ENDPOINT is setJoin an existing node as an additional cell
Already initializedA cell identity is on diskVerify and refresh the licence, then start normally
Licence upgradeIdentity on disk, a new licence presentActivate the new licence without a restart
Fresh, nothingNo licence, no join endpointA development build boots unlicensed. A production image logs the remedy and exits

Cloud detection is automatic. The controller probes the instance metadata service (AWS IMDSv2, Azure IMDS, or the GCP metadata server) for provider, region, and zone, and falls back to local hostname and address detection on bare metal. Downstream consumers read the result rather than re-probing, so placement, residency, and provider configuration start from a real answer.

The setup flow​

On the bootstrap path the cell runs an ordered setup flow. It is worth reading the order once, because it is the order the estate comes into existence in.

StageSteps
Identity and directoryValidate the licence, create the organization from it, register the node, resolve the default entity, register the administrator, load types, resolve the cell profile, register the cell
Join ceremonyRuns only when this cell is joining an existing node, and self-skips otherwise. It runs before crypto provisioning so the joining cell's enrollment certificate and keys are in place when subordinate material starts being issued
CryptoProvision organization crypto, then node, cell, and administrator crypto, then the join-authorization signing key
RuntimeAuto-populate endpoints, rebuild runtime state, seal an identity snapshot, backfill entity workspaces, finalize sync
ActivateApply the licence and activate the cell into the fabric

The cell profile resolved in that first stage sets what the cell is for. The default is Stem (General Purpose), which carries everything. The others are eight single-service profiles and four resource-constrained tactical templates (Drone/UAV, Vehicle/Mobile Platform, Forward Operating Base, Tactical Operations Center).

The setup gate is real

Until setup completes, authenticated routes are held closed. Only a tight allowlist is reachable without a session, and it exists so setup itself can run. The gate is not cosmetic: a cell is never live but ungoverned.

Confirm it is serving​

Two probe routes are reachable in every phase, without a session, by design.

curl -fsS http://localhost:8080/healthz     # liveness, constant by contract
curl -fsS http://localhost:8080/readyz # readiness, the real dependency verdict

The distinction matters. /healthz answers whether the process is alive and is a constant. /readyz is the route that reports a broken dependency, and it returns 503 when the cell is not actually serving. If you wire one probe, wire that one.

Then read the setup phase and sign in:

curl -fsS http://localhost:8080/api/public/setup/status

Open the node's front door in a browser and sign in with the administrator you provisioned. Ports 8080 and 8443 are the node gateway; see ports and protocols for the full list.

Grow the node​

A second cell joins the node you just minted rather than minting its own.

export S9_JOIN_ENDPOINT="https://<first-cell-host>:8443"
export S9_CELL_NAME="cell-02"
export S9_CELL_PROFILE="stem" # default: stem (general purpose)
python main.py start

The joining cell runs the join ceremony inside the same setup flow. A failed join does not exit; the cell retries on the next start, because a transient network failure during a fleet expansion should not require an operator to rebuild an instance.

What you now have​

One cell, which means: a catalog, engines, open lakehouse formats, object storage, messaging, the agent platform, and the full security plane. Also an organization, a node identity that will survive this cell being replaced, an administrator with crypto material, and a cell that has attested itself into the fabric.

Next steps​