CLI reference
Run space COMMAND --help=plain to print the help for one command.
Several commands below name the marketplace. The publishing
chapter explains what space publish mints and how far
an order against it gets.
Commands
| Command | Description |
|---|---|
space init [--color=WHEN] [--dir=DIR] [OPTION]… [TEMPLATE] | Start a new project from a template. |
space build [COMMAND] … | Build your application into a signed image. |
space run [COMMAND] … | Run an image locally, or start it as a named service. |
space prune [OPTION]… [AREA]… | Report and reclaim regenerable build caches. |
space publish [OPTION]… [IMAGE]… | Publish your application to the marketplace. |
space deploy [COMMAND] … | Ship a signed release to a satellite or any other target. |
space status [OPTION]… [NAME] | Show what is running. |
space logs [OPTION]… [NAME] | Show the logs from your running application. |
space show [OPTION]… [ADDRESS] [VERSION] | Show what the last save did underneath. |
space version [--arch=ARCH] [--color=WHEN] [--root=DIR] [OPTION]… | Show this CLI's version and what it was built from. |
space up [OPTION]… | Start the local runner. |
space down [--color=WHEN] [--socket=PATH] [--timeout=SECONDS] [OPTION]… | Stop the local runner. |
space runner COMMAND … | Manage this box's membership in hosted runner pools. |
space image [COMMAND] … | List, pull, push, check and retire images. |
space target [COMMAND] … | Connect and manage the machines you deploy to. |
space keys [COMMAND] … | Manage the keys that sign your builds and decide on your deploys. |
space login [OPTION]… URL | Sign in to a ground station with OIDC. |
space init
Synopsis: space init [--color=WHEN] [--dir=DIR] [OPTION]… [TEMPLATE].
Writes the named template into DIR, creating DIR if it is missing. A short name resolves in the parsimoni-labs namespace, so space init hello-world writes parsimoni-labs/hello-world. Run space build and space run inside the new directory.
A template is read from this machine's cache, and downloaded into that cache once when it is not there yet, so naming it again writes it with no network. With nothing cached and no network, init refuses naming the repository and the cache entry; it never substitutes a copy of its own.
Run from inside a Space source checkout that publishes the template, init fills that cache entry from the checkout before reading it, so what it writes is what the tree holds now. The result line says whether the template came from the checkout or this machine's cache.
Omitted, --dir is the template name without its organisation: hello-world for parsimoni-labs/hello-world.
init overwrites no existing file.
| Argument | Description |
|---|---|
TEMPLATE | Template name, for example hello-world or parsimoni-labs/hello-world. |
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--dir=DIR | Directory to scaffold into, created if missing. Defaults to the template basename. |
space init also accepts the observability options
and the common options listed below.
space build
Synopsis: space build [COMMAND] ….
Builds the project in this directory into a signed image. With no argument, build reads space.yaml.
Dockerfile (Docker shape): space build -f Dockerfile -t name --project-root DIR. -f/--file selects the recipe, -t/--tag names the image space run resolves, and --project-root is the directory shared with the builder as the COPY root. space build path/to/Dockerfile is shorthand: the Dockerfile's directory becomes the project root and its basename the tag.
space.yaml build: a local YAML config file or an OCI image reference as the positional argument; if omitted, space.yaml must exist. An OCI image is read from the image store first and downloaded on a cache miss.
space build builds the image once, for any target. --target names one machine instead: the launch plan then carries that target profile's uplink and its serving posture, so an image assembled for a deploy-tier target stays up serving the control plane after activation. --signing-key selects a named build signing identity; omitted, the current development identity from space keys use is used and its fingerprint is printed. Release-looking tags such as :release and :prod require an explicit --signing-key, or the --signing-key-ed25519 and --signing-key-mldsa65 pair. Every build signs. The board, device tree, and secure-boot key belong to the deploy target, so the per-board bootable image (kernel + initramfs + DTB + signed boot.img) is assembled by space deploy for the named target, keyed by (image, board). See space deploy --help.
space build --plan (alias --dry-run) prints the build dependency graph and exits without building. Each content-addressed derivation (the Linux payloads, each app image, each partition disk) is shown with its key and whether it is already in the local build cache, with the cached artifact's digest and size, or would be built. A derivation keyed on a dependency's built output is shown deferred; its key resolves while that dependency is built.
Every build appends to the host build record: one provenance row per completed build, giving tag, image digest, source, completion time, status, and signing identity fingerprint. Failures are recorded too; a failed build carries no digest and records the failure cause.
Lifecycle
A build leaves a durable provenance row. space build ls lists every completed build, successful or failed, with the digest, the source and the signing identity of each.
| Subcommand | Description |
|---|---|
space build ls [--color=WHEN] [--limit=N] [--signer=FINGERPRINT] [OPTION]… | List completed builds. |
Run space build SUBCOMMAND --help=plain for a subcommand's options.
| Argument | Description |
|---|---|
SOURCE | With -f/--file, the optional project root shared with the builder (the COPY root). Without it, the build config source: a Dockerfile, a local YAML file, or an OCI index reference. Defaults to space.yaml. |
| Option | Description |
|---|---|
-C DIR, --directory=DIR | Change to DIR before the project is resolved, as make -C and tar -C do. Everything the project declares and everything this command resolves is then resolved there, so the command can be driven from anywhere and still writes into the project and nowhere else. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--display=MODE (absent=auto) | Select display rendering. Defaults to auto, a live TTY display on terminals. tty forces it. raw disables cursor-control redraws and emits append-only output. |
-f FILE, --file=FILE | Dockerfile recipe to build. The positional argument is then the build project root (the COPY root), defaulting to the Dockerfile's own directory. |
--guest-ftrace=DIR | Read the builder guest's kernel records from this tracefs (typically /sys/kernel/tracing) into the same capture. Requires --guest-trace. |
--guest-trace | Capture the builder VM guest's own runtime spans to *-guest.fxt and *-guest.db beside the build console log, readable with obs query. This is the ocaml-probe capture --trace is not. |
--issuer=KEY.pem | The publisher's public key, as space keys public writes it, to pin for a published IMAGE, replacing whatever this target has recorded. A target with no publisher recorded resolves one from the signing identity this machine has selected and records it beside the target manifest, or in this configuration's keys directory when it declares none. This flag is needed only for a deliberate re-pin: the publisher rotates its key, or the image comes from somewhere the recorded key does not answer for. |
-k KERNEL, --kernel=KERNEL | Kernel image to boot instead of the one the product supplies: a local file path or an OCI reference. The board a project runs on is the target's, named in its profile; this names another one for this run. |
--keystore=POSTURE | Secret-keystore posture baked into the measured launch plan: dev boots space-secret on the plaintext development keystore, sealed requires a platform-sealed KEK source at boot. Defaults to dev; a --release build defaults to sealed and rejects dev. |
-o DIR, --output=DIR | Output directory for build artifacts. Overrides the config file. |
--plan, --dry-run | Print the build dependency graph and exit without building. Each content-addressed derivation (the Linux payloads, each app image, each partition disk) is shown with its key and whether it is already in the local build cache (with the cached artifact's digest and size) or would be built. --dry-run is an alias. |
--profile-metrics | Report where this run's wall clock went. Each phase has one line showing its entry count. The Prometheus text exposition of the same numbers is written beside the build manifest on every build. |
--project-root=DIR | Project root shared with the builder. A Dockerfile build uses it as the COPY root, and defaults to the Dockerfile's directory. An app build uses it as the staged source tree, and defaults to the app.yaml's directory. A space.yaml build resolves local Dockerfile services under it, uses it as their COPY root, and crunches its files into every partition's boot initramfs. |
--release | Build a production release. It bakes the sealed secret-keystore posture into the measured launch plan and requires an explicit, non-development signing identity (--signing-key, or --signing-key-ed25519 and --signing-key-mldsa65). A release-looking tag (:release, :prod) without this flag is refused. |
--rollback-index=N | Anti-rollback index published by this signing act. It is stamped into every capability the build mints and into the launch-trust bundle, and a target refuses a launch whose index is below the highest it has already admitted. Advance it on a release that must supersede its predecessor. It is a deliberate counter, so rebuilding the same commit publishes the same index and the same bytes. Defaults to 0, the development domain. A --release build publishes 1 or more, and without this flag the index its signing identity declares (space keys advance). |
--signing-key=NAME | Named signing key from space keys. Defaults to the current development identity, and names one that does not exist yet into being. --release requires it or the two --signing-key-* flags, refuses the development identity, and refuses to create the one it is given. |
--signing-key-ed25519=PEM | PEM file holding the classical (Ed25519) half of the release identity that signs the OCI service manifests recorded in the image. It is the artifact provenance key runner admission checks. It is not the board secure-boot key of space target and space deploy. The post-quantum half is named by --signing-key-mldsa65 and is never looked for beside this file. |
--signing-key-mldsa65=PEM | PEM file holding the post-quantum (ML-DSA-65) half of the release identity. Required with --signing-key-ed25519 and named separately, so the two halves can be held under separate custody. |
-t NAME, --tag=NAME | Name for the produced image. A Dockerfile build uses it as the name space run resolves, and defaults to the project-root basename. An app build uses it as the app name. A space.yaml build registers it in the image store for space run and space image push, and defaults to a generated build id. |
--target=TARGET | Assemble for the named target: the machine this image will run on. Its profile (profile: in targets/NAME.yaml, or the pin space target connect recorded) is what the launch plan takes its uplink and its serving posture from, so an image assembled for a deploy-tier target stays up serving the control plane after activation instead of powering off. Without it the image is assembled for no machine, and an image may not name one itself. |
--uefi-disk[=STUB] (default=) | Also assemble a UEFI host disk beside each partition disk. STUB may be a local stub file, a directory containing a platform stub, or an OCI image reference. With no STUB, use uniboot's digest-pinned default systemd-boot stub. |
--vm-type=TYPE (absent BUILDR_VM env) | VM backend for VM launches, vz (Apple Virtualization.framework, macOS) or qemu. Defaults to vz where the framework is available, qemu otherwise. |
space build also accepts the observability options
and the common options listed below.
space build files an observability option of its own.
| Option | Description |
|---|---|
--trace[=PATH] (default=) | Print the path of the trace document this command writes, and with PATH write a copy there. The document is written on every run, including one that fails, beside the artefact it describes: a build's in the build output directory, a run's in its run directory. Its path is printed on any failing exit whether or not this flag was passed. It is a Trace Event Format document, the shape every trace this program writes has. |
space run
Synopsis: space run [COMMAND] ….
Run this directory's project in a virtual machine on this computer. At a terminal, saving a file restarts the service that reads it with your change.
space run IMAGE runs a particular image instead.
A save to space.yaml or a Dockerfile boots the virtual machine again. With no terminal, --headless or --timeout, the run boots once.
CPU, memory and partitions come from the run: section of space.yaml.
--name NAME gives a run a name. space status lists named runs, and space run stop NAME ends one.
space run --name NAME IMAGE starts a signed, supervised service when IMAGE produces a staged Solo5 or host-style launch. A runc service runs only from a baked SpaceOS image partition; boot it with space run IMAGE, or deliver a release with space deploy (--target local for this machine).
--name needs a local runner. Starting one reads this machine's hardware root: a TPM it presents is sealed to, and a machine with none keeps the runner's floor in a private file and says so. --tpm-device and --tpm-socket point at a specific root instead, and one that does not answer is a refusal. A runner that is already listening was started against a root that answered then, so the flags are accepted and unused.
Lifecycle
A run is a durable thing on this machine: a directory of everything it printed and a record holding its name. space run ls lists the runs on this machine, and -a adds the ones that have ended. space run stop NAME ends one. space run rm NAME removes what it left and frees its name for the next run.
| Subcommand | Description |
|---|---|
space run ls [--all] [--color=WHEN] [--socket=PATH] [OPTION]… | List the runs on this machine. |
space run rm [--color=WHEN] [--socket=PATH] [OPTION]… [NAME] | Remove a run you have finished with. |
space run stop [--color=WHEN] [--ground=URL] [--socket=PATH] [OPTION]… [NAME] | Stop a service you started with space run. |
Run space run SUBCOMMAND --help=plain for a subcommand's options.
| Argument | Description |
|---|---|
IMAGE | An OCI image reference or SpaceOS build id/reference. With --name, a staged IMAGE runs as a signed, supervised service on this machine's runner or in the pool named by --ground. An IMAGE without --name boots locally. Without IMAGE, space run boots this directory's project or the image built here from its Dockerfile. |
| Option | Description |
|---|---|
--bootstrap=FILE | Private bootstrap document to hand the guest, as a provider would. It is attached as a raw config drive and selected on the kernel command line, so the image itself names no provider and the image booted here is the image space target apply ships. Without it a local target fetches nothing and its services start without credentials. |
-C DIR, --directory=DIR | Change to DIR before the project is resolved, as make -C and tar -C do. Everything the project declares and everything this command resolves is then resolved there, so the command can be driven from anywhere and still writes into the project and nowhere else. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--dashboard-port=PORT (absent SPACE_DASHBOARD_PORT env) | Loopback HTTP port for the local dashboard. When none is named the runner takes 8080, or the first free port after it when that one is held. A port named is used exactly, and a refusal names what holds it. 0 starts the runner with no dashboard. Read from SPACE_DASHBOARD_PORT when set. |
--display=MODE (absent=auto) | Select display rendering. Defaults to auto, a live TTY display on terminals. tty forces it. raw disables cursor-control redraws and emits append-only output. |
--ground=URL | The ground station to act through, by the URL it answers on, which space login signs in to. |
--headless | Run without the interactive boot display. Applies to the local VM boot form. |
--isolation=BACKEND | Override the isolation backend for --name: one of auto, runc, spt, hvt, firecracker, qemu or host. Defaults to the image's classified backend (a unikernel image runs under its solo5 tender). |
-k KERNEL, --kernel=KERNEL | Kernel image to boot instead of the one the product supplies: a local file path or an OCI reference. The board a project runs on is the target's, named in its profile; this names another one for this run. |
--name=NAME | Name this run NAME. space status lists named runs. space run stop and space logs take their names. A name already in use is refused. |
-o DIR, --output=DIR | Output directory for build artifacts. Overrides the config file. |
-p HOST:GUEST, --publish=HOST:GUEST | Publish a guest port on the host as HOST:GUEST. Repeatable. Forwarded through the VM's user networking on both backends (QEMU's own hostfwd, and the slirp gateway that drives the Virtualization.framework network attachment), so curl http://localhost:HOST reaches the guest. A host port already in use is refused by name before the VM starts, including one held only on 127.0.0.1, which a wildcard bind would otherwise shadow. |
--plan, --dry-run | Resolve the image/build id and runtime constraints, print the local boot plan, then exit without starting it. |
--profile=PROFILE | Resolve the local run against the named target profile, an operator's own document winning over the packaged catalog. A host-VMM profile's kernel is booted directly; a UEFI profile's image is booted through EDK2 under QEMU, with the run's parameters in the SMBIOS OEM strings; a profile whose first stage is a board ROM is refused, and space deploy is the verb for it. Defaults to the profile describing this host. |
--progress=MODE (absent=auto) | Select the local boot progress renderer. Defaults to auto. Your services' output is shown as it arrives and the guest's own console only when the run fails; raw prints that console as it arrives, as -v does under any renderer. --name services report progress through space status and space logs. |
--scenario=SCENARIO | Fly the run through SCENARIO, a space-sim scenario: a world goes behind the spacecraft's frame bus, publishing where the vehicle is, when the next pass is and what it can carry, and answering what the application asks of it. A service reaches it only by declaring the bus (interfaces: [{type: frame_bus}]). SCENARIO is a path to a scenario.json or its directory, or a name. The name written in the project's own scenario.json selects that file; any other name is a published scenario, read from this machine's cache and downloaded once when the cache does not hold it. Without this flag a project that names a scenario under local: flies that one, and any other has no world behind its bus. |
-t SECONDS, --timeout=SECONDS | Stop the run after SECONDS seconds and exit. The deadline covers the wait for services to report healthy and for guests to exit. It also bounds each service's running time. A service still running at the deadline is stopped, and the run names it. Without this option, a run has no timeout. |
--target=TARGET | Run on the named target. Its writable disk is materialized once under the build output's targets/TARGET directory on first boot and booted in place on every later run, so guest writes survive a restart: naming a machine is what asks for its state to be kept. Without this flag each run boots a fresh disposable copy of the built disk on the default local target. Remove the directory to reprovision. |
--tpm-device=DEV | Seal this command's secrets to TPM character device DEV. Mutually exclusive with --tpm-socket. Given neither, this machine's own hardware root is used when it presents one. Naming a device the machine does not have is a refusal, never a silent fall back to a plaintext secret. |
--tpm-socket=PATH | Seal this command's secrets to the TPM at Unix socket PATH, a swtpm simulator or a TPM this machine does not present at a device path. Mutually exclusive with --tpm-device. Naming an endpoint says it is there, so a socket that does not answer is a refusal and never a silent fall back to a plaintext secret. |
--vm-type=TYPE (absent BUILDR_VM env) | VM backend for VM launches, vz (Apple Virtualization.framework, macOS) or qemu. Defaults to vz where the framework is available, qemu otherwise. |
--volume=NAME | Bind project volume NAME into the --name service. Repeatable. |
space run also accepts the observability options
and the common options listed below.
space run files an observability option of its own.
| Option | Description |
|---|---|
--trace[=PATH] (default=) | Print the path of the trace document this command writes, and with PATH write a copy there. The document is written on every run, including one that fails, beside the artefact it describes: a build's in the build output directory, a run's in its run directory. Its path is printed on any failing exit whether or not this flag was passed. It is a Trace Event Format document, the shape every trace this program writes has. |
space run exits with a status of its own as well as the shared ones.
| Status | Condition |
|---|---|
0 | on success. |
1 | A VM crashed, the headless run did not shut down cleanly, or a declared service never reported healthy. |
2 | The target announced it will start no service, update or secret service, and said why. Booting the same image again refuses identically. |
3 | --timeout expired with no verdict from the target. What the console did and did not carry is printed with it. |
123 | on a refusal, printed on standard error with what to do about it. |
124 | on a command line this command could not read: an option, a value or an argument it does not have. |
125 | on a defect in this program, printed on standard error. |
space prune
Synopsis: space prune [OPTION]… [AREA]….
prune reports what a SpaceOS build can regenerate and, with --yes, removes it. Everything it collects costs build time to rebuild and nothing else: a cache disk is reformatted and reseeded, a seed archive is repacked from the store it copies, a backend directory belonged to a process that is gone, an unreferenced OCI blob is pulled or built again.
The default is a report. Its first line is what the cache root holds, each top-level entry sized once by the area that owns it, names the setting the root was read from (SPACE_CACHE_HOME, XDG_CACHE_HOME or HOME), and names any entry no area of the run sized. Then what the areas together could reclaim, and one line per area, as docker system df prints its stores: what the area holds, what of it could go, how many of its entries were not measured, and the reason the most of the rest is kept for. -v is the answer to why the disk is full: each area listed under its own root as it is measured, biggest entry first, with anything a rule holds back and the reason it was kept, then the root's own entries biggest first under its total.
It answers from wherever you are standing. Every area but two is under the cache root, which is one directory for the machine: the report is the same from a project, from a checkout, or from your home directory. bootstrap and seeds are the two, because the trees they collect and the engines that say which seed universe is live sit in the SpaceOS build that made them; run from anywhere else they are listed as out of reach, with nothing for you to do about it, and the rest of the report is unaffected.
cache-home is what makes that answer complete. It collects nothing: it lists every top-level entry of the cache root with a sentence, so a directory under no rule at all is reported as exactly that on the day it appears, with its size. A cache root reached 269 GiB with every rule in this command correct, because five directories were under none of them and nothing said so. An entry another area owns is sized in that area's section, or by the command its row names when this run left that area out; it is never measured twice and never measured here.
The store directory
data is the one area whose argument is not that what it collects can be built again. A build record is reproducible from nothing on this machine, so the live store is never a candidate and is listed with what it holds. What data collects is what nothing can read: an sqlite3 recovery file whose database is gone, and a file named after a store that this build never opens. Each is named for which of those it is.
Each store is opened through a read-only handle, so a report writes no byte of any store it reports on. A file under a store's name that is not a database this build can open answers neither way, and is held as exactly that. An unreadable file is not presumed old.
A disk a live build holds is never removed. prune probes buildr.lock and the per-cache seed locks with the same non-blocking lock a build takes, and keeps them for as long as it is removing, so a build cannot claim a disk between the report and the removal. A lock file left behind by a build that died holds nothing and does not block reclamation.
The two dune areas
toolchains and dune-cache are dune's, not SpaceOS's, and they are shared with every dune project on the machine. A prefix-baked compiler tree is not an ordinary cached package, so dune cache trim does not cover toolchains. Dune exposes no owner lease for those trees, so deleting one could race another project starting its compiler. This command reports toolchain sizes but does not remove them; Space's isolated guest cache safely reclaims its own superseded toolchains between builds.
prune does not walk dune's cache. dune cache size is the figure and dune cache trim the removal, which is also the safety: dune counts and removes only files that no build directory hardlinks, so a running build's artifacts are excluded by construction and no lock is needed.
dune-cache is the one area that collects nothing dead. An unused cache entry is the working set of a cache doing its job, so it is bounded. Name the area explicitly: dune's exact measurement stats every cache entry and can take noticeable time on a large shared store. It removes nothing at all until the cache is past --dune-cache-size, by which point it has stopped being a working set.
Recreating a cache disk
--disk CACHE is the one operation here that is not a removal. A builder cache disk is a sparse file, and mounting one costs what it has ever had allocated: an OCI cache disk with 356 GiB allocated behind a store of 25 GB spans 2850 block groups, and at 96 milliseconds for each it takes 274 seconds to mount. Nothing in the build path returns a freed block to the host file, and a size only ever reaches a disk that does not exist yet, so starting a new disk is what makes a mount short again.
The old disk is moved aside under a dated name and never removed. disks reports that orphan cache disk and reclaims it when you choose, so the operation is reversible for as long as you want it to be. What replaces it is empty, sparse and at the size its cache derives, and the next build formats it and fills it from wherever that cache's contents come from.
WHICH IS THE CACHE'S OWN ANSWER, and the two are not one operation. A cache whose host directory the build cannot ask for by name ships a packed archive, and that archive is the only way its contents get back: this packs it first. It holds the same locks. Every build ends with the store ahead of it (the host packs before its VM boots and the guest's cache import invalidates the store's content root after it), so an archive behind its store is the normal state and not a reason to send you elsewhere. The default reports the delta the pack would fold in and packs nothing, since packing writes the archive. The oci cache ships none: its host directory is this machine's image store, served to the build as a registry for the whole of a run, so the empty disk fills from that store again and there is nothing to pack.
It refuses, naming what it saw, when a build holds buildr.lock or the cache's own lock, and when the cache has no disk to replace. For a cache that ships an archive it also refuses when the pack fails, and when what the pack produced still does not cover the store, since without that the disk comes back cold and the store's content is only on the disk being replaced.
The seed store
seeds is the one area whose contents are not cheap to produce again: an entry is a minted seed universe, and a miss costs a full Docker mint. So the rule is not age. Each entry is keyed on two sets of input digests, and the engine in place records exactly those two sets beside itself, so the universe this checkout came out of is a fact the tree carries. Everything else is a universe no engine under this bootstrap root records, and the row says which ones they do.
Where no engine records anything, the whole area is held and says so. There is nothing to compare against, and hours of mint is not something to spend on an assumption.
Every build holds the cache root to a size, the way git gc --auto keeps a repository packed. The first build to end a day or more after the last hold started starts the next one in the background, space prune --cache-size=SIZE --yes, and writes its output to prune.log at the cache root. A cache root with no prune.log has never been held: the build that finds none writes it and holds nothing, so the first hold is a day after the first build.
The size is 600GB unless the machine states its own as cache_size in its configuration file, a size such as 400GB, in ~/.config/space/config.toml or the file SPACE_CONFIG names.
The appliance store
A build holds the staged appliance store to a bound and leaves it under that bound at its end, retiring the least recently resolved entries aside and reclaiming what it retired. The bound is this binary's default unless the machine states its own as appliance_store_size in its configuration file, a size such as 64GiB, in ~/.config/space/config.toml or the file SPACE_CONFIG names; the report's appliance line says which of the two every build reads. prune is not that rule: it reclaims what no build has staged in --older-than, whatever the store weighs.
The engine store
One builder engine is published per set of inputs this machine builds, in the machine's engine store, and under a live vendor symlink that is a new key at every upstream landing. bootstrap keeps every engine a checkout on this machine names, and 2 more per architecture, the ones a build resolved most recently, which is 3.4GiB of disk at the 1.7GiB a published engine weighs. An engine past that is reclaimable however recently a build resolved it, and each row says how many were resolved more recently than it.
Two are never reached by that bound: the engine a checkout still on this machine names, which is what that checkout's next build mounts, and the one a build resolved last. Both are held whatever their rank, and the row names the checkout.
A reclaimed engine costs the build that keys it again a rebuild from the generation in place, never a mint, and never a refusal. The separate thirty-day floor still retires an engine no build here has resolved for a month, whatever the store is holding.
What it leaves alone
OCI manifests and tags. An image nobody wants any more is an image lifecycle decision, not garbage: dropping a tag would turn its blobs into garbage on the next run, and no rule here owns that choice. On a store carrying years of build tags most of the bytes are reachable from a tag, so oci reclaims the blobs behind rewritten manifests and no more.
A target's boot assembly under provision. It holds the state key the target's data partition is encrypted under, so removing one is a target lifecycle decision and not a reclamation. The ground release store under uplink is durable for the same kind of reason: the lineage and its payloads are not regenerable from anything on this machine. The cache-home report lists both with their sizes and the reason each is kept.
An engine a rebuild held aside, while the published engine is incomplete. It is then the only engine on the machine, and the next run puts it back. Once the published one is whole it is reclaimable, which is also when that run would drop it itself.
Areas
| Area | Description |
|---|---|
disks (default) | Builder cache disks, seed archives, build mounts, and publications and locks left by writers that are gone. |
seeds (default) | Minted seed universes no engine here records. |
bootstrap (default) | Backend directories a finished build left behind, and what an engine rebuild left beside the engine. |
dune-cache (explicit) | Dune's shared build-artifact cache. Name it explicitly because measuring a shared tree costs what it contains. |
data (explicit) | The host's own stores and what nothing there can read. Name it explicitly because it walks the store directory. |
toolchains (explicit) | Report-only Dune compiler trees. Name them explicitly because measuring a shared tree costs what it contains. |
checkouts (default) | Image layers extracted from the store. The default run reports them and leaves them in place. To remove them, run space prune checkouts --yes. |
appliance (default) | Staged builder rootfs trees and packed initramfs bodies. |
oci (default) | Blobs in the local OCI store no manifest references. |
cache-home (default) | Every entry of the cache root, reported and never removed. |
| Argument | Description |
|---|---|
AREA | Report the named areas. Omit AREA for the default set. See AREAS for what each name covers and which areas must be named explicitly. |
| Option | Description |
|---|---|
-C DIR, --directory=DIR | Change to DIR before the project is resolved, as make -C and tar -C do. Everything the project declares and everything this command resolves is then resolved there, so the command can be driven from anywhere and still writes into the project and nowhere else. |
--cache-size=SIZE | Hold the cache root at or under SIZE, the way dune cache trim --size holds dune's cache: remove entries least recently used first, across every area of the run, until the root's total in allocated bytes is at or under SIZE. Last use is the stamp an area records where it records one: appliance and checkouts (a build stamps an entry each time it resolves it) and the engines of bootstrap (by last resolution). disks, oci, seeds and the other leftovers of bootstrap rank by modification time. What no rule may take is the floor: a target's boot assembly, the ground release store, a disk a build holds, a checkout a running command uses or a build is writing, and anything an area keeps. It stays in the total and is never removed. A builder cache disk is laid out again, empty, and counted at what the new one allocates; nothing is moved aside. --older-than still applies, and --reclaim-budget bounds the bytes the whole run removes. With no AREA, the run takes the default areas and every area that sizes an entry of the cache root, since a total that leaves an entry out is refused. The last line is how the run ended: at or under SIZE, stopped by its budget, or stopped by the floor, each with its own exit status. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--disk=CACHE | Recreate the named builder cache disk: move the one it has aside under a dated name, and lay out an empty one at the size its cache derives for the next build to format and fill. Repeatable. A mount costs what a disk has ever had allocated, and no size ever reaches a disk that exists, so this is the only way back to a small one. A cache that ships a packed archive is reseeded from it, and refuses unless the archive covers the store; one served to the build from its host directory refills from that directory and packs nothing. Refuses while a build holds the disk. |
--dune-cache-size=SIZE (absent=50GiB) | Bytes to leave in dune's shared cache. Defaults to 50GiB, deliberately generous: every entry dune-cache evicts costs a full rebuild to replace, and holding entries long enough for another workspace or a later branch to reuse them is the whole point of the cache. A single package closure's working set runs to tens of gigabytes, so a tight budget would evict live reuse on the first run and cancel the feature. Name a smaller SIZE when disk is genuinely the scarce resource. |
--older-than=DURATION (absent=1h) | Leave anything modified in the last DURATION alone. Defaults to 1h, which keeps a running build's working set and a fetch whose manifest has not landed yet out of range. 0 removes everything eligible. Does not reach dune-cache, which dune evicts by size and last use; see --dune-cache-size. |
--reclaim-budget=SIZE (absent=8GiB) | Bytes one run removes from one area before it stops and says what it left. Defaults to 8GiB. This is the verb's own bound, and it is here because removal is the one part of a prune proportional to the machine's whole backlog: a store 244 GiB over its bound is 10 200 s of unlinking at the 75 s a staged appliance tree costs, and a run killed by a timeout before it finishes gives no account of what it removed. What a run leaves stays in place, so the next run takes the same entries in the same order; naming a larger SIZE is how an operator with the afternoon spends it. Under --cache-size it bounds the whole run, which removes across areas. |
--root=DIR | Bootstrap artifact root, which must already exist. Defaults to SPACE_BOOTSTRAP_ROOT when that is set, else to .space-bootstrap in the checkout this command is run from, else to the bootstrap_root the machine's configuration records, which a completed bootstrap writes. |
-y, --yes | Remove what the report lists. Without it space only reports, and changes nothing. |
space prune also accepts the observability options
and the common options listed below.
space prune exits with a status of its own as well as the shared ones.
| Status | Condition |
|---|---|
0 | on success. |
3 | when --cache-size stopped at --reclaim-budget with the cache root still over its size. The last line says how much is left; the next run resumes there. |
4 | when --cache-size removed everything a rule lets it take and the cache root is still over its size. The last line names what is left, which no run of this command removes. |
123 | on a refusal, printed on standard error with what to do about it. |
124 | on a command line this command could not read: an option, a value or an argument it does not have. |
125 | on a defect in this program, printed on standard error. |
space publish
Synopsis: space publish [OPTION]… [IMAGE]….
Validates the application manifest locally, assembles the signed launch material for the declared architecture from that image's signed build, uploads the blobs it names, and asks the ground station --ground names to mint one immutable marketplace revision pinned to it. space build then space publish is the whole chain: nothing has to have been deployed first. This command does not push an OCI image, start a service, or make a qualification claim. Human-issued qualification assertions are attached separately to immutable artifacts.
| Argument | Description |
|---|---|
IMAGE | Image to publish, as ARCH=REF or a bare REF taking the architecture the manifest declares. Repeat once per architecture; the declared one must be among them, because the revision's launch material is assembled from its signed build. |
| Option | Description |
|---|---|
--app=APP | Marketplace listing to publish onto, by id. The server mints the next immutable revision on that listing, naming the one it replaces as its previous. Omitted, the publication mints a new listing. |
--category=CATEGORY | Catalogue category. Omitted, the listing keeps its own. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--description=TEXT | Full catalogue description. Omitted, the listing keeps its own. |
--ground=URL | The ground station to act through, by the URL it answers on, which space login signs in to. |
--manifest=FILE | Application manifest JSON. Declares the supported target profile and architecture, runtime isolation, input and result schemas, execution limits, cancellation policy, and support owner. |
--name=NAME | Listing name. Defaults to the hosted project name when omitted. |
--plan, --dry-run | Validate the application manifest and the image bindings, then print the publication plan without building, reading credentials, or writing remote state. |
--price-usd-c=CENTS | Price in integer USD cents. Omitted, the listing keeps its own price, and a listing that never had one is free; negative prices are refused. |
--short-description=TEXT | One-line catalogue summary. Omitted, the listing keeps its own. |
--tag=TAG | Catalogue tag. Repeat for more than one tag. Given none, the listing keeps its own. |
space publish also accepts the observability
options and the common options
listed below.
space deploy
Synopsis: space deploy [COMMAND] ….
space deploy IMAGE --target TARGET puts an image on a target as a signed desired release state. It assembles the release for the target, signs it, and enqueues a durable deploy record. What moves that record afterwards is delivery, and nothing but the target's own signed word marks it activated. Deploy delivers to a target that already exists and never provisions one: space target apply provisions and re-images.
Deploy returns once an always-reachable target reports that the release runs, and refuses naming the bound in seconds when it has not by then: 300 seconds for the target, and the time the release takes to cross at the target's contact rate; that bound is waited only once the record is queued for the target, never while it rests at published. On a target reached in passes it returns once the release is queued, naming the next pass. Ctrl-C ends the command and leaves the deploy going. A refusal that leaves the deploy going names space show TARGET VERSION, the verb that reads its record. -v prints the release descriptor, the packed release, the ground and the record id as well.
With neither argument, deploy reads the project it is standing in, as space build and space run do: the image is what build:'s output in space.yaml holds. A project that has not built, whose recipe sources moved since the image was written, or that was built for another target than the one it ships to, is built first, as space run builds it, for the target and under the signing the deploy signs with; a line names what is built and why. The plan a release commits carries the boot posture its target profile decides, whether the target stays up serving and on which network device, and a release whose plan carries another is refused before anything ships, naming the build that carries the target's.
With no --target, deploy places the release on every target the project's placement: names, sending it to each in the order it names them. Every name is resolved before anything is sent, so a name no target answers to refuses the whole placement; each target then takes the deploy described here under a line naming the target and the release, and once every release is sent the deploy follows every record at once, so the placement waits one bound however many targets it names. A target that refuses leaves the others going, a record the ground still holds when the bound passes is left to the ground's retries under a line naming its phase, and the closing line counts the targets that took it and names every other with the phase its record rests at. With no placement, deploy goes to the target space target ls marks, and its first line names that target before anything is sent.
The image is target-agnostic. Deploy resolves its signed system partition, then the target profile selects the native assembly. A CM5 gets its board kernel, initramfs, DTB and signed boot image; a machine SpaceOS boots under a VMM or UEFI firmware takes the whole image under that boot boundary. Board-specific inputs come from the connected target; --board, --signing-key, --dtb and --kernel override them for bring-up and recovery.
--target names a connected target, a target manifest, a directory holding targets/, or local for this machine. A release for this machine names its profile with the launcher that boots it appended, as in macos-vmm@sha256:DIGEST+vz: +vz is Apple's Virtualization framework and +qemu is QEMU. The profile and its transport carry every platform difference, so one command reaches the VM on this laptop, a board on a bench and a satellite in orbit. A target reached over a link takes its release through the ground its registration records, this machine's when it records none, and needs a transport connected with --endpoint and --cla-address; a target that is re-imaged instead takes it as a whole boot image.
--plan assembles and validates the target release, prints its signed release descriptor beside what the target HOLDS, and writes no target state, so the release can be archived and approved before it ships. It resolves the target through the same function the apply resolves it through, so a target one of them accepts the other accepts. What it holds is the runner the target last proved it came up on, read from the ground receiving its acknowledgements, and a target that cannot be asked is reported as unavailable.
A deploy is named by its target and version, as in run-space v2: every deploy to a target is its next version. space show TARGET VERSION reads back the deploy record those words name, and --json carries the record's id. Its phases are published, approved, queued, held, uplinking, installed, activating and activated, with rejected and failed as terminal outcomes; space status reports what this machine is running now.
Lifecycle
A deploy mints a durable record, and every verb below acts on one. space deploy ls lists the records this machine holds, newest first. space deploy approve TARGET VERSION --as NAME admits a published record to the uplink queue on the signed decision of its target's one required approver; above a quorum of one, space deploy approve TARGET VERSION --approval FILE --approval FILE presents one --approval FILE per decision, since the gate counts the decisions presented in one call. space deploy reject TARGET VERSION ends it before activation, and is terminal. space deploy hold TARGET VERSION takes an admitted record off the queue and space deploy release TARGET VERSION puts it back. space deploy rollback --target TARGET re-delivers what that target ran before, as its next version.
A target whose registration names this computer's one operator identity as its sole approver (space target show reads approves: you, alone) is approved in the same command, as a decision that identity signs; the screen carries no approval step. A target naming anybody else leaves the record at published, and the deploy ends there, exit 0, saying who it waits for and the command that person types.
space show TARGET VERSION names the decision a record is waiting for, so the screen an operator reads and the verb they type next are the same vocabulary.
| Subcommand | Description |
|---|---|
space deploy approve [OPTION]… [TARGET] [VERSION] | Admit a published deploy to the uplink queue. |
space deploy hold [--color=WHEN] [--reason=TEXT] [--socket=PATH] [OPTION]… TARGET [VERSION] | Inhibit a queued deploy so the uplink skips it. |
space deploy ls [--color=WHEN] [--ground=URL] [--socket=PATH] [OPTION]… | List the deploy records this machine holds. |
space deploy reject [--color=WHEN] [--reason=TEXT] [--socket=PATH] [OPTION]… TARGET [VERSION] | End a deploy before activation starts. |
space deploy release [--color=WHEN] [--reason=TEXT] [--socket=PATH] [OPTION]… TARGET [VERSION] | Put a held deploy back on the queue it was admitted to. |
space deploy rollback [--color=WHEN] [--socket=PATH] [--target=TARGET] [OPTION]… | Put back the release a target was running before this one. |
Run space deploy SUBCOMMAND --help=plain for a subcommand's options.
| Argument | Description |
|---|---|
IMAGE | Image to put on the target. With none, the image the project here built: build:'s output in the space.yaml that space build and space run read. The id space build prints, or the build's whole digest, is the build this machine's store holds under it. |
| Option | Description |
|---|---|
--board=BOARD | Assert the target profile's published platform board identifier. The resolved profile's boot mechanism selects the assembly. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--display=MODE (absent=auto) | Select display rendering. Defaults to auto, a live TTY display on terminals. tty forces it. raw disables cursor-control redraws and emits append-only output. |
--dtb=DTB | Pin this device-tree blob into the per-board boot image, under verified boot. Overrides the DTB connected for the target with space target connect. With neither, the board firmware supplies the stock device tree. |
--ground-online-key=KEY.pem | Imported public PEM for this target's ground Snapshot and Timestamp signer. When supplied, the publisher stores offline Root and Targets roles in its CAS repository and sends publisher-only frames to ground. |
--issuer=KEY.pem | The publisher's public key, as space keys public writes it, to pin for a published IMAGE, replacing whatever this target has recorded. A target with no publisher recorded resolves one from the signing identity this machine has selected and records it beside the target manifest, or in this configuration's keys directory when it declares none. This flag is needed only for a deliberate re-pin: the publisher rotates its key, or the image comes from somewhere the recorded key does not answer for. |
--kernel=KERNEL | Board kernel image (path or OCI ref) for the per-board boot image. Overrides the kernel connected for the target with space target connect. The image's own kernel is target-agnostic and is not used for the board image. |
--plan, --dry-run | Assemble the target release and print its signed descriptor beside what the target currently holds. Writes no target state and ships nothing. |
--signing-key=PEM | RSA-2048 PEM private key for the target board's native secure boot. It signs the per-board boot.img as boot.sig, the signature the Raspberry Pi firmware verifies. Overrides the key connected for the target with space target connect. |
--socket=PATH (absent=the socket this checkout or this machine names) | Unix domain socket path for the runner control plane. Defaults to SPACE_RUNNER_CONTROL when set, the same override every command that addresses a runner honors. |
--target=TARGET | Target to deploy to: a connected target name, a path to a target manifest, or a directory holding targets/. Defaults to every target the project's placement: names, one after another in the order it names them, else the one declared in targets/NAME.yaml, else the only one connected on this machine. With more than one of those two in scope the command fails and names them. |
space deploy also accepts the observability
options and the common options
listed below.
space status
Synopsis: space status [OPTION]… [NAME].
Shows the current SpaceOS runtime state, one row per service. IMAGE is the image a run launched and, where a machine reported mounting another, that one beside it; STATE and RELEASE are what a machine reported, and OBSERVED is when it reported it. Every digest is its twelve hex, and --json carries the whole. A value no machine reported prints unobserved with a marker, and the reasons print once under the table. It is the terminal view of the web dashboard's overview page.
A NAME argument narrows the report to one service, and the command exits non-zero if that service is not running. The command shows every service when NAME is absent. The runs this machine holds, going or ended, are space run ls and space run ls -a.
A project report includes one line per target named in its deploy records: PROJECT on TARGET: vN running since HH:MM once the target says version N runs, who the record waits for while it rests at published, and its phase and since when otherwise. The records are the ones space deploy ls lists, the project's own among them; space show ID reads one. A NAME that is one of those targets prints its line alone; one space target ls lists that no record from here names points at space target show NAME and exits 1.
--json prints the machine-readable form. It prints the whole runtime report when NAME is absent: service state plus telemetry, SBOM, signature, downlink and result state, as canonical JSON against Space_schema.Report. A script or an e2e harness reads that report as its oracle. A NAME argument narrows it to the service array for that service.
The report answers in two scopes and names each. runs and services are this MACHINE's, whichever project started them: runs come off a machine-wide registry, and the whole-runtime document is the machine's oracle. runs_here is the runs of the project this command was typed in, by the directory each run was built from, so a script tells its own from the machine's without comparing paths. It is absent where the command was typed in no project at all, which is also what says last_run names the machine's last run.
The control socket names the runner this reads. --socket names it on this command line: it is the term space up prints in its stop line and space down takes, so a runner started somewhere other than the default is read here too. --ground is read on two paths only. A NAME is looked up as an order against that ground's marketplace, and a finished order there also lands its result objects in the run's output: directory. --json with no NAME prints the whole-runtime report of the target that ground holds: the report the target's gateway last posted there, or a refusal naming what is missing. --runner NAME selects that target by name or id when the ground holds several. It is accepted only with --json and --ground, without a service name. With no --ground the command reads this machine alone.
The command starts nothing: no daemon, no image, no VM, and no runtime state is mutated. It says the runner is not connected and exits 0 on a machine with no local runner. The report form stamps "runner":{"connected":false}, runner.silence says why where a runner was there and said nothing, and runner.protocol_version is the control protocol the runner that answered speaks, absent where none did. SPACE_RUNNER_CONTROL overrides the local control endpoint.
| Argument | Description |
|---|---|
NAME | Show only the named service. Defaults to all. |
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--ground=URL | The ground station to act through, by the URL it answers on, which space login signs in to. |
--runner=NAME | The runner of the ground --ground names whose target to report, by its name or id. Needed only where the account has enrolled several. |
--socket=PATH (absent=the socket this checkout or this machine names) | Unix domain socket path for the runner control plane. Defaults to SPACE_RUNNER_CONTROL when set, the same override every command that addresses a runner honors. |
space status also accepts the observability
options and the common options
listed below.
space status exits with a status of its own as well as the shared ones.
| Status | Condition |
|---|---|
0 | on success. |
1 | when a NAME was given and no service has it. The command answers with an empty result. |
2 | when the NAME given is a deploy id, whose record space show ID reads. |
123 | on a refusal, printed on standard error with what to do about it. |
124 | on a command line this command could not read: an option, a value or an argument it does not have. |
125 | on a defect in this program, printed on standard error. |
space logs
Synopsis: space logs [OPTION]… [NAME].
Display SpaceOS service logs. A standalone space-logs collector is read directly when it is configured (SPACE_LOGS_CONTROL, then SPACE_LOGS_SOCKET) and reachable.
A run is addressed by the name and the id space run gave it, the way space run stop and space status address one, and never by a directory: two runs can be started from one, so a directory cannot say which. Naming one shows that run's output from wherever the command is typed, read out of the run's own directory.
A SERVICE name is read differently, because a space.yaml is what declares one. It is answered by the run the project this command was typed in started, so a second copy of that project elsewhere on the machine changes nothing here. Typed outside any project, or where the project it was typed in is running nothing, it is read across the machine and a name two runs answer to is refused with both.
With no NAME the run is the one this directory built. Where it built two the command refuses and names them, because the choice is the caller's. Where it has none, this project's own run answers where this directory has one and the local runner answers where it does not, which is what space status lists in each case, so the two commands describe one directory the same way. A run this directory is holding comes first, read out of the store that run writes as it happens; then the services the runner is supervising, over its control socket; then the last run to end here. Each is shown under a line naming what is being read: a run by the words space status names it in, the runner by name.
--target reads a deployed target instead, over the route space target apply recorded for it: NAME is then one of its services. The target answers the request the local runner answers, so --tail, --since and --follow mean what they mean here, and a follow is a request the target holds open until a line lands. What PID 1 and the runner themselves say is not in the target's record: those lines are on the serial console, which space target console streams. A target reached over a routable interface authenticates the query under the BPSec key space target apply provisioned; without that key here the target drops the query and the command reports no reply.
--run reads another run of this project's and never the runner. The command starts no collector and no runner, and a directory with neither one listening nor a run behind it says so and exits non-zero.
--memory answers a different question about the same run: what PID 1's own memory did across the session. Its VM reports three series on one pass: PID 1's resident set, the size of its OCaml major heap, and the words it has allocated. Each is summarised on its own line, so a resident set rising while the heap is flat is growth outside the OCaml heap. The VM samples them on a clock and the run keeps what its VM reported, so a run that has ended answers as fully as one that is going. A save that works and costs the VM its memory is visible there and nowhere else.
| Argument | Description |
|---|---|
NAME | The run to show, by the name or the id space run gave it, or by a service one of them is running. Omitted, the run in this directory. |
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
-f, --follow | Keep printing new lines as they arrive. |
--ground=URL | The ground station to act through, by the URL it answers on, which space login signs in to. |
--memory | Show what the run's VM measured about PID 1's own memory instead of its output: every reading with the instant it was taken, then one line per series carrying the first, the peak, the last and the net change. A run that recorded none is refused by name. |
-n N, --tail=N | Show the last N lines. |
--run=ID | Read the run ID instead of the last one to end here. A refusal lists the ids this project has. |
--since=TIME | Show entries since TIME (ISO 8601 timestamp or Unix epoch seconds). |
--socket=PATH (absent=the socket this checkout or this machine names) | Unix domain socket path for the runner control plane. Defaults to SPACE_RUNNER_CONTROL when set, the same override every command that addresses a runner honors. |
--target=TARGET | Target to act on: a connected target name, a path to a target manifest, or a directory holding targets/. Defaults to the target the project's placement: names, else the one declared in targets/NAME.yaml, else the only one connected on this machine. With more than one in scope the command fails and names them. |
space logs also accepts the observability options
and the common options listed below.
space show
Synopsis: space show [OPTION]… [ADDRESS] [VERSION].
space show reads a run's record. It opens with the fields space run ls -a prints for the run; then what the run has used, off the record its VM leaves under it: for the run and for each service, how long it has run, the memory it holds and the most it held, and the bytes it moved on the network and through the disk; then the six facts the last save left behind: the derivation it resolved and which of that key's inputs had moved, the layer it rebuilt and what that layer weighs against the image it lands in, who signed the generation, the diff it sent with the command that reads what went on the link, the release the VM reports holding beside the one this save sent, and each service the VM's runner lists with the image it runs and the release it mounts.
It closes on the run's certificate, the bindings the run's page on the dashboard prints: what ran, by the digest of each image; the release, by the build's identity and each file the build record stands by; what stood behind the VM's word on its boot; and the root the run checked its build under. A run that recorded no root has none, and the block says why.
--certificate FILE writes the same bindings as one file a third party checks
with the root's two public keys and no state of this product: a COSE_Sign (RFC
9052) signed with both halves of the root, its Ed25519 key and its ML-DSA-65
key (RFC 9964), whose payload is an in-toto statement (media type
application/vnd.in-toto+json). A verifier accepts it only when both signatures
verify, as a boot manifest under the same root is accepted. Its subjects are
the images that ran and the files the release stands by, each by its digest;
its predicate, of type https://os.space/spaceos/run-certificate/v1, is the
certificate member --json carries. It is signed on the machine that holds the
root, found by fingerprint among the identities space keys ls lists, and
refused anywhere else. space keys public --json carries the two public keys a
verifier needs.
space show DIFF reads back what one diff put on the link, every delivery of it. A diff is the release object as it rides: the signed envelope carrying exactly the images the VM did not already hold, named by its content address, diff:<hex>. Two saves that send one envelope are two deliveries of one diff. The screen opens with the key, what the VM held when the omission was chosen and the release sent; under it each delivery names the save and run that made it, the session with the manifest it froze before its first byte crossed, the bundles in order with their octets, the BPSec blocks, one bundle a viewer can read, and what the VM states it reassembled and activated.
space show TARGET VERSION reads a deploy back the same way, off the records the deploy left: the durable record the ground advanced, what each contact pass put on the link and the stages the transfer went through. It opens with the fields space deploy ls prints for the record, then the image it was minted for. The first four rows are the save's labels in the save's order, so an operator who has read one screen has read the other; the fifth is the transfer stage by stage in the words CCSDS 727.0-B-5 gives its PDUs (Metadata, File Data, EOF, NAK, Finished), each with the instant the ground recorded it, the time since the stage before and the party that reached it, problems first: a NAK, a Finished that did not deliver, a pass the link refused, the target's boot verdict refusing the trial after the staged acknowledgement, and the wait on that verdict named as the absence it is. The target's own word on the release sits above the rows as the status of the delivery: what the target signed and the key it verified under, or the sentence saying the target has not spoken. Under it are the record's transitions, each phase of the pipeline with the instant and detail of the transition into it where the record made one, and, for a record only an operator's decision moves, what that decision has to carry and the command that makes it. A deploy leaves no command trace, so the derivation and the layer rows read as the absence naming what does not record them. The bundles that crossed by identity, their BPSec blocks and one decoded field by field are the protocol's internals, printed as the frames row under -v and nowhere else.
It reads the run's own documents and computes nothing of its own, so every number here is the one the stage that did the work recorded: the use is the newest reading the VM's runner took of each service, summed for the run, and a counter the record does not carry has no row. A fact of a save this tree does not record yet is printed as an absence naming what does not record it: a missing row cannot be told from a save that never got that far.
ADDRESS is the name you gave space run --name, the id the run was filed under, or the diff's key, or the short id a screen printed for any of them; a deploy's target, followed by its VERSION; or a key --json carries. Without one this reads the run that ended last in this project.
--socket is refused. It is the term space up prints in its stop line and space status and space deploy ls take, and it addresses a runner; every address here is read off the records it left, a deploy record off the store SPACE_CONFIG locates, so no runner has to be up for this to answer. The refusal names that store.
Pass --json for the same rows as one machine-readable document, with the target's word as a member of its own: a heading is nothing a consumer can branch on. A delivery document also carries the durable record whole, as member record, and that document is what space deploy approve --record signs a decision from.
--sbom prints the software bill of materials of the build a project file configures, in place of the save this verb reads back. In a project that was built it is the bill space build signed and wrote beside the image, spaceos.spdx.json: the kernel, the runtime, each partition's init and each service image, each under the digest the image carries it by. It is shown only once its signature, spaceos.spdx.json.cose, verifies under the signing identity this machine selects, and a bill that does not verify is refused. --json prints that document byte for byte. The positional argument under this flag is the project file the bill is of, defaulting to space.yaml.
Before a build, the bill comes from the build config and describes what that config will produce: the digest-pinned kernel, each partition's init binary and the workspace opam closure the dune.lock names.
An OCI reference resolves to the SPDX document its attestation layers carry, and falls back to the config's own metadata when the image carries none. --cache selects the local OCI cache those attestations are read from, and --lock the workspace lock the closure comes from. --lock none omits the closure, and an installed binary with no checkout around it omits it too.
The default output is a text report on stdout. --json prints SPDX JSON, --format cyclonedx prints CycloneDX JSON, and --output writes the JSON to a file. The creation timestamp comes from SOURCE_DATE_EPOCH and defaults to the Unix epoch, so the same config produces the same document.
--osv scans the same opam closure against the OSV.dev vulnerability database and prints that report in place of the SBOM.
| Argument | Description |
|---|---|
ADDRESS | What to read back, from any directory: a run by its name, its id or the short id a screen printed; a deploy by its target and VERSION, as every screen names it, or by the record's id --json carries; an image by its digest or short id; or the runtime, engine, kernel or SDK by the digest space version names; or a diff by the key diff:<hex> a save printed or its short id. Without it, the run that ended last here. Under --sbom it is the project file the bill is of instead, defaulting to space.yaml. |
VERSION | The deploy's version, as in v2, after its target: the two words every screen names a deploy by. Without it the one word before is the record's id, which --json carries. |
| Option | Description |
|---|---|
--cache=DIR | OCI store the service images are read from. Defaults to the store space build files its images in. |
--certificate=FILE | Write the run's certificate to FILE: an in-toto statement in a COSE envelope signed by both halves of the root the run checked its build under. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--format=FORMAT (absent=spdx-json) | JSON SBOM format, spdx-json or cyclonedx (cyclonedx-json is an alias). Defaults to spdx-json. Any other format implies JSON output. |
--lock=DIR | Workspace dune.lock directory whose opam closure is enumerated into the SBOM. Defaults to the enclosing checkout's dune.lock; pass none to omit the closure. |
-o FILE, --output=FILE | Write the JSON SBOM to FILE. Defaults to stdout. |
--osv | Scan the enumerated opam closure against the OSV.dev vulnerability database and print the report instead of the SBOM. |
--project-root=DIR | The project root the build was run with (space build --project-root): the output directory the build wrote the image and its signed bill to is resolved under it. Defaults to the project file's directory. |
--sbom | Print the software bill of materials of the build the project file configures, in place of the save this verb reads back. Under this flag the positional is that project file and defaults to space.yaml. |
--socket=PATH (absent=the socket this checkout or this machine names) | Unix domain socket path for the runner control plane. Defaults to SPACE_RUNNER_CONTROL when set, the same override every command that addresses a runner honors. |
space show also accepts the observability options
and the common options listed below.
space version
Synopsis: space version [--arch=ARCH] [--color=WHEN] [--root=DIR] [OPTION]….
version prints one line: this binary, the runtime it boots and the engine it builds with, each by the first twelve hex digits of its digest. It is the first line of every build, run and deploy log, so a log a developer pastes names the same three artefacts this does.
-v prints the report: what this installation is and what its runtime holds, one row per producer. It produces no value of its own: each row is rendered from that row's existing producer, and the runtime section is the payload set's own bill of materials, read as an SPDX document from where the build wrote it. --json carries every row whichever screen was asked for.
space --version prints the first row alone, on one line, for a script. Both are the same function over the same input, so they cannot disagree.
source, commit and vendor are the build's own record of what it compiled. commit and vendor are the two source trees' revisions and whether either carried uncommitted content, and source is one digest over both. They are written down at build time and carried in the binary, so a copy answers with the sources it was made of. The answer is independent of the current checkout. A build that read no checkout at all records no revision and says so.
bootstrap-root is the root the runtime rows were read under, resolved once at the command's entry, and bootstrap-root-from the arm that named it: --root, SPACE_BOOTSTRAP_ROOT, the checkout the command runs in, or the bootstrap_root this machine's configuration records. Both read none where the command stands in no root.
runtime is read from the store this machine keeps, by the function space build reads, so the two cannot name different bundles. It prints the bundle's reference and digest when the store holds it, what is missing and the command that puts it there when it does not, and where to look when no release has published one. Reading it fetches nothing.
runtime-vendor names each vendored tree the root's runtime was minted from, as the mint recorded it: its revision, any uncommitted changes, and whether that revision is the one .mqmodule pins. It is the line the mint printed under source:.
engine is the builder engine every local service image is built inside, read from the same store and by the same selection space build resolves it through, so this row and that build's line cannot name different engines. It is the second artefact a build is made of, and a report that named only the runtime left an input of every build that no screen recorded. Reading it fetches nothing.
SPACE_MAINTAINER=1 adds the runtime bill, which is read out of a Space checkout and is about the tree a maintainer builds from. There is nothing in it for a reader who installed space to build their own project, so it is behind the same gate as the command that writes it.
The bill is read and never assembled. Where the build has written none, version names the path nothing wrote: a bill that lists nothing and a bill that was never written are different facts, and only one of them is about the runtime.
The member list names every member the payload set declares and carries the digest the bill records for each. Where the bill records no digest at all, version says that the document describes what the runtime was built from. The bill carries no digest for shipped bytes.
The crypto section says which AES-GCM kernel this executable links and whether a machine-checked proof covers it. It is probed and not tabulated: the verified kernel is Vale-verified x86-64 assembly, the probe attempts a key expansion through it, and the row reports what answered. On AArch64 there is no verified AES-GCM to link, because Vale has no AArch64 backend, so the row reads as a negative and that negative is what a document about this build may state.
It refuses in place, row by row, and exits 0 for the rows it did resolve. A path given with --root is the exception: a directory this process cannot use is a typo, and a typo is answered from the flag. That flag selects the checkout the bill is read from, so it is read under SPACE_MAINTAINER=1 and nowhere else.
| Option | Description |
|---|---|
--arch=ARCH | Target architecture or comma-separated architecture list. Supported values: arm64, amd64. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--root=DIR | Bootstrap artifact root, which must already exist. Defaults to SPACE_BOOTSTRAP_ROOT when that is set, else to .space-bootstrap in the checkout this command is run from, else to the bootstrap_root the machine's configuration records, which a completed bootstrap writes. |
space version also accepts the observability
options and the common options
listed below.
space up
Synopsis: space up [OPTION]….
Starts the local runner used by space client commands. By default it runs in the background and returns once its control socket is ready.
Use space status to inspect runtime state and space down to stop the local runner.
With --foreground, the runner stays attached to the current terminal and its lifetime is the command's.
The engine's launch-trust floor rests on this machine's hardware root, and the runner reads what the machine presents: sealed to a TPM it has, with a monotonic anchor, or a private file with neither on a machine with none. The screen says which. --tpm-device and --tpm-socket point at a specific root instead. If a named TPM root does not answer, space up refuses to start.
| Option | Description |
|---|---|
--allow-host (absent SPACE_ALLOW_HOST env) | Permit host isolation, for local development and tests. A host service runs as an uncontained native process. Admission and launch signing still apply. |
--allow-trusted-host-network (absent SPACE_ALLOW_TRUSTED_HOST_NETWORK env) | Permit a signed, trusted platform service to share the laptop network namespace. Off by default: apps requesting host_network are refused. This exposes host interfaces and loopback services; a service binding a port below 1024 also runs without a user namespace. |
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--dashboard-port=PORT (absent SPACE_DASHBOARD_PORT env) | Loopback HTTP port for the local dashboard. When none is named the runner takes 8080, or the first free port after it when that one is held. A port named is used exactly, and a refusal names what holds it. 0 starts the runner with no dashboard. Read from SPACE_DASHBOARD_PORT when set. |
--deploy-ack-port=PORT (absent=4556 or SPACE_DEPLOY_ACK_PORT env) | TCPCL port for signed deployment acknowledgements. Defaults to SPACE_DEPLOY_ACK_PORT when set, or 4556 otherwise. Set to 0 to disable the acknowledgement listener. |
--foreground | Keep the runner attached to the current process. |
--keys=PUBKEY.pem | Public key (PEM) a deployed image's signature must verify against before it is launched: the file space keys public writes, or a single Ed25519 or ML-DSA-65 key. Repeatable. With no key the anchor is the identity space keys use selected, and with neither space up refuses to start. |
--log-epoch=SECONDS (absent SPACE_LOG_EPOCH env) | Assign captured service lines consecutive timestamps beginning at this Unix time. Process scheduling continues to use the system clock. Defaults to SPACE_LOG_EPOCH when set; otherwise captured lines use their real event times. |
--peer-uid=UID (absent=501) | Only accept connections from this user id. Defaults to the daemon's own user. |
--socket=PATH (absent=the socket this checkout or this machine names) | Unix domain socket path for the runner control plane. Defaults to SPACE_RUNNER_CONTROL when set, the same override every command that addresses a runner honors. |
--tpm-device=DEV | Seal this command's secrets to TPM character device DEV. Mutually exclusive with --tpm-socket. Given neither, this machine's own hardware root is used when it presents one. Naming a device the machine does not have is a refusal, never a silent fall back to a plaintext secret. |
--tpm-socket=PATH | Seal this command's secrets to the TPM at Unix socket PATH, a swtpm simulator or a TPM this machine does not present at a device path. Mutually exclusive with --tpm-device. Naming an endpoint says it is there, so a socket that does not answer is a refusal and never a silent fall back to a plaintext secret. |
--vm-type=TYPE (absent BUILDR_VM env) | VM backend for VM launches, vz (Apple Virtualization.framework, macOS) or qemu. Defaults to vz where the framework is available, qemu otherwise. |
space up also accepts the observability options
and the common options listed below.
space down
Synopsis: space down [--color=WHEN] [--socket=PATH] [--timeout=SECONDS] [OPTION]….
Stops the local runner used by space client commands. It signals the runner control server recorded in the socket pid file and waits for it to exit.
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--socket=PATH (absent=the socket this checkout or this machine names) | Unix domain socket path for the runner control plane. Defaults to SPACE_RUNNER_CONTROL when set, the same override every command that addresses a runner honors. |
--timeout=SECONDS (absent=15.) | Seconds to wait for the runner to exit after SIGTERM. Defaults to 15, the runner's own bound plus five. |
space down also accepts the observability options
and the common options listed below.
space runner
Synopsis: space runner COMMAND ….
A runner is a machine this account has put into a hosted pool: join enrols this box and serves the gateway loop, status says where one machine has got to, revoke takes back what one machine speaks with, and ls reads back what the pool holds. An operator names one by id when they bind it behind a satellite; a buyer names none, because which machine takes an order is the scheduler's answer.
| Subcommand | Description |
|---|---|
space runner join [OPTION]… | Join a hosted runner pool and serve the gateway loop. |
space runner ls [--color=WHEN] [--ground=URL] [OPTION]… | List the machines this account has enrolled in the pool. |
space runner revoke [--color=WHEN] [--ground=URL] [OPTION]… RUNNER | Take back the credential a machine speaks with. |
space runner status [--color=WHEN] [--ground=URL] [--timeout=SECONDS] [OPTION]… [RUNNER] | Report where this box has got to in a hosted runner pool. |
Run space runner SUBCOMMAND --help=plain for a subcommand's options.
space runner also accepts the common options listed
below.
space image
Synopsis: space image [COMMAND] ….
The three verbs act on one store: the OCI store on this host, at SPACE_CACHE_HOME/oci when that variable is set, at XDG_CACHE_HOME/space/oci when that one is, and at ~/.cache/space/oci otherwise. It is the store space build writes its output into and the store space run NAME resolves a name against, so an image pulled here is the one a build and a run then find.
space image ls reads that store and writes nothing. space image pull writes into it. space image push reads it and writes to the registry it names. space image verify and space image fsck read it and write nothing either, and they are the noun's two questions about trust: whose signature a delivered image carries, and whether the objects this store holds are still the bytes their keys name.
space image rm drops a name this store holds. It removes references and reclaims nothing: a blob belongs to every manifest that names it, so what a removal made unreachable is space prune oci's sweep to report and to collect.
| Subcommand | Description |
|---|---|
space image fsck [--color=WHEN] [OPTION]… | Check every object this machine holds against its own key. |
space image ls [--all[=KIND]] [--color=WHEN] [--socket=PATH] [OPTION]… | List images in the image store. |
space image pull [OPTION]… IMAGE | Pull an image into the image store. |
space image push [OPTION]… IMAGE | Publish a built image to a registry. |
space image rm [--color=WHEN] [OPTION]… [IMAGE] | Retire an image reference from the image store. |
space image verify [--anchor=KEY.pem|NAME] [--color=WHEN] [OPTION]… IMAGE | Check a delivered image against the evidence it carries. |
Run space image SUBCOMMAND --help=plain for a subcommand's options.
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
space image also accepts the observability options
and the common options listed below.
space target
Synopsis: space target [COMMAND] ….
A target is a machine bound to an exact content-addressed target profile. The profile declares its platform board and boot mechanism, and is shared: every target on that board resolves the same document. The registry these verbs keep carries only what is this machine's own (keys, kernel and DTB overrides, transport), one entry per target name.
The registry is a record and not a connection. Recording a target, listing what is recorded and removing an entry reach no machine; space deploy is what sends a release to one.
| Subcommand | Description |
|---|---|
space target apply [OPTION]… [NAME] | Reconcile a target's provider resources and connect it. |
space target auth [COMMAND] … | Manage a target's provider credentials in this configuration. |
space target bundle [OPTION]… | Assemble and sign a daemon set for a hosted install. |
space target catalog [COMMAND] … | Manage the public target-profile catalog. |
space target connect [OPTION]… NAME | Connect or update an existing deploy target. |
space target console [--color=WHEN] [--duration=SECONDS] [OPTION]… [NAME] | Stream a provisioned target's serial console. |
space target install [OPTION]… | Install the SpaceOS daemon set onto this machine. |
space target inventory [COMMAND] … | Manage authenticated target inventory. |
space target ls [--color=WHEN] [OPTION]… | List the targets connected on this machine, marking the default. |
space target restart [--color=WHEN] [--duration=SECONDS] [--plan] [OPTION]… [NAME] | Power-cycle a provisioned target and watch it boot. |
space target rm [--color=WHEN] [--out=FILE] [--registration=FILE] [OPTION]… NAME | Remove a deploy target. |
space target secret [COMMAND] … | Inspect and rotate a target's managed secrets. |
space target show [--color=WHEN] [OPTION]… [NAME] | Show one deploy target: its record and its provider state. |
space target sign --as=NAME [--color=WHEN] [OPTION]… FILE | Sign a version of a target's registration. |
space target uninstall [OPTION]… | Remove the SpaceOS daemon set from this machine. |
space target update [OPTION]… | Replace the daemon set, reverting if it does not come up. |
space target verify [--color=WHEN] [--issuer=KEY.pem] [--prefix=DIR] [OPTION]… | Check the installed bundle against its signed manifest. |
Run space target SUBCOMMAND --help=plain for a subcommand's options.
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
space target also accepts the observability
options and the common options
listed below.
space keys
Synopsis: space keys [COMMAND] ….
A signing identity names the artifact provenance key space build signs with. space keys use selects the one development builds default to. A --release build requires an explicit --signing-key, or --signing-key-ed25519 and --signing-key-mldsa65, and publishes the rollback index that identity declares, which space keys advance moves.
space keys ls shows each identity's name and key fingerprint, marking the current default. The fingerprint is checked against the files it names before it is shown, so an identity whose key pair has been replaced reads moved, and one written in the format that named a single PEM reads retired one-file format. Pass --json for the machine-readable projection: the recorded and on-disk fingerprints, the key_state verdict, key paths, the declared rollback_index, and a current flag.
Operator keys authorize deploy decisions. space keys ca creates the operator authority every deploy approval roots in, and space keys issue issues an identity under it; space deploy approve --as is what signs a decision with one. A signing identity and an operator identity share this directory and nothing else.
| Subcommand | Description |
|---|---|
space keys advance [--color=WHEN] [--to=N] [OPTION]… NAME | Advance the rollback index a signing identity declares. |
space keys ca [--color=WHEN] [OPTION]… | Create or show the operator authority approvals root in. |
space keys certify [--color=WHEN] --out=FILE [OPTION]… FILE | Certify another operator's request under this authority. |
space keys generate [--color=WHEN] [--replace] [--rollback-index=N] [OPTION]… NAME | Generate a local release signing identity (Ed25519 + ML-DSA-65). |
space keys install [--color=WHEN] [OPTION]… FILE | File an operator certificate certified elsewhere. |
space keys issue [--color=WHEN] [OPTION]… NAME | Issue an operator identity under the operator authority. |
space keys ls [--color=WHEN] [OPTION]… | List the signing and operator identities this computer holds. |
space keys offer [OPTION]… NAME | Offer a worker on a marketplace under an operator identity. |
space keys public [--color=WHEN] [OPTION]… NAME | Print a signing identity's public key PEM. |
space keys rekey [OPTION]… | Wrap fresh SDLS session keys to a target's ML-KEM key. |
space keys request [--color=WHEN] --out=FILE [OPTION]… NAME | Ask another computer's authority to certify an identity. |
space keys trust [COMMAND] … | Manage the release trust this computer's server keeps. |
space keys use [--color=WHEN] [OPTION]… NAME | Select the default signing identity for development builds. |
Run space keys SUBCOMMAND --help=plain for a subcommand's options.
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
space keys also accepts the observability options
and the common options listed below.
space login
Synopsis: space login [OPTION]… URL.
Starts an OIDC browser or OAuth device-code login for the ground station at URL and stores the issued scoped token in the operating-system credential store, where this CLI's provider identities already live. The token is kept under the ground's origin (its scheme, host and port) and this command's configuration file, so every target connected through that ground and every command given --ground with it presents it.
The browser flow asks the terminal for nothing. It binds a listener on the loopback interface, and the browser delivers the result there (RFC 8252 section 7.3). --flow device prints a short code to approve from a browser elsewhere when this machine has no browser.
Login never asks for a Parsimoni password or a long-lived access key. Identity lives at the external provider. The CLI stores only the scoped token the OIDC flow returns.
The token carries no satellite command authority. Deploy and command objects crossing the ground-to-target boundary are signed objects, verified against the target's pinned policy.
| Argument | Description |
|---|---|
URL (required) | The ground station to sign in to, by the URL it answers on: the one space target show prints for a target, or the one a command is given with --ground. |
| Option | Description |
|---|---|
--color=WHEN (absent=auto) | Colorize the output. WHEN must be one of auto, always or never. |
--flow=FLOW (absent=browser) | OIDC login flow to start. Defaults to browser, the loopback redirect of RFC 8252 section 7.3: the browser completes the sign-in and delivers the result to this terminal, which asks for nothing. device uses an OAuth device-code flow, for a shell on a machine with no browser. |
--scope=SCOPE | OAuth scope for the sign-in. RFC 6749 section 3.3 spells it as space-delimited scope-tokens. publish:LISTING is authority to publish that listing and nothing else, which is the credential a build machine should hold. |
--token=TOKEN | Store this token directly and skip the interactive OAuth flow. It is verified against the ground before it is stored. |
space login also accepts the observability options
and the common options listed below.
Observability options
space init, space build, space run, space prune, space publish,
space deploy, space status, space logs, space show, space version,
space up, space down, space image, space target, space keys and
space login accept these options.
| Option | Description |
|---|---|
--json | Enable JSON mode. By default logs are emitted with json-logs; commands that produce their own JSON may pass a custom reporter to keep stdout reserved for command output. |
--log=SPEC (absent SPACE_LOG env) | Set log level and per-source overrides. Format: LEVEL[,SRC:LEVEL,...]. Examples: debug, info,tls.tracing:warning, conpool:debug. Source names match exactly or by dot-separated prefix. Levels: error, warning, info, debug. |
--log-tag=TAG | Add a base tag to JSON log output. Can be repeated. Format: KEY=VALUE. Tags are attached to every JSON log record. Example: --log-tag env=prod --log-tag region=us-east-1. |
--log-trace=FILE | Write protocol log records from sources whose name ends in .tracing to FILE. These records include verbose hexdumps and parser state logs. |
--no-color | Disable ANSI colour. |
-q, --quiet | Suppress non-error log records. This still allows explicit application output written by the command itself. |
-v, --verbose | Increase log verbosity. Use once for info, twice for debug, and three times to also enable log sources whose name ends in .tracing. |
Common options
Every command accepts these options.
| Option | Description |
|---|---|
--help[=FMT] (default=auto) | Show this help in format FMT. The value FMT must be one of auto, pager, groff or plain. This program writes its manual to standard output and never opens a pager, so auto is plain here whatever TERM is set to. --help=pager still pages. |
--version | Show version information. |
Exit status
Every command exits with one of these statuses, and a command that adds one of its own says so in its own section above.
| Status | Condition |
|---|---|
0 | on success. |
123 | on a refusal, printed on standard error with what to do about it. |
124 | on a command line this command could not read: an option, a value or an argument it does not have. |
125 | on a defect in this program, printed on standard error. |