Skip to content

build.yaml reference

build.yaml declares a target-agnostic system composition: partitions, their init processes, services, storage, and service interfaces. space build assembles that composition, signs it, and writes artifacts under output. It also registers a local OCI index that space run and space deploy resolve later.

build.yaml holds no board configuration, and space build produces no per-board bootable image. space run assembles that image for local execution. space deploy assembles it for a connected target from the board kernel, device tree, and firmware in the target’s profile.

Example

schema: space.build
version: "1"
kernel: linuxkit/kernel:6.6.13
output: _build/spaceos
partitions:
payload:
init: pid1
disk: true
services:
- name: eo-classifier
image: ghcr.io/<your-org>/eo-classifier:latest
runtime: oci
isolation: runc
interfaces:
- type: ip
bridges: [payload-net]
healthcheck:
type: command
argv: [/usr/local/bin/healthcheck]
- name: sdls-downlink
image: ghcr.io/parsimoni-labs/sdls-downlink:latest
runtime: oci
isolation: runc
depends_on: [eo-classifier]
storage:
blocks: 256
params:
- id: 1
value: boot_count=0
- id: 2
value: mode=nominal

This example spells out several members that would otherwise default, so that one document shows them together. kernel is the clearest case: a composition boots the product’s own kernel unless it names another, and the line above is that override.

space sbom reads a composition without building it, so it is the shortest way to check that a build.yaml resolves. The header names the kernel each partition boots and the init the supervisor runs:

Terminal window
$ space sbom build.yaml | sed -n '1,4p'
SpaceOS Software Bill of Materials
kernel linuxkit/kernel:6.6.13
payload pid1

The package inventory follows, one row per OCI layer and OCaml package the composition carries. A member the schema does not declare is refused by space sbom, naming the member.

Fields

FieldTypeRequiredDescription
schemastringyesMust be space.build. The CLI rejects a wrong-schema document with a clear error.
versionstringyesManifest schema version. Currently "1".
kernelOCI ref or pathno (default: the product’s own kernel, pinned by digest)Partition kernel for local and dev-VM execution. Name one only to boot a kernel of your own. A deploy target’s board kernel is a target fact recorded with space target connect, not this field.
outputpathno (default: _build/spaceos)Directory where build artifacts are written.
artifactsmapnoImported app artifact manifests, each with a manifest path and optional digest. A service references one imported output with artifact: <name>#<output>.
target_profilecatalog name or NAME@sha256:DIGESTnoTarget profile this composition is assembled against. The build copies its network section into the launch plan. A digest pins the exact profile; a bare name resolves through the target catalog.
partitionsmapyesThe partitions this composition boots, keyed by name.
partitions.<name>.initstringno (default: pid1)Partition init / supervisor. It starts services, mediates frame-bus access, and reports lifecycle and health. Override only when a workload needs custom supervision.
partitions.<name>.kernelOCI ref or pathnoPer-partition kernel override. When unset, the partition inherits the top-level kernel.
partitions.<name>.diskbooleanno (default: enabled when the partition has services)Persistent block storage for this partition, backed by the storage section.
partitions.<name>.files[]listnoLocal files or directories embedded into the partition initramfs. Each entry has source and image-root path.
partitions.<name>.services[]listnoWorkloads managed by the partition supervisor.
storage.blocksintno (default: 256)Persistent storage, counted in 512-byte blocks. The partition is rounded up to a whole mebibyte and is never smaller than one, so the default asks for 128 KiB and gets 1 MiB.
storage.params[]listnoParameter store entries that persist across reboots.
storage.ab_slotsbooleanno (default: false)WIP It is meant to reserve storage for a second bootable system release, so an update can stage the inactive slot. The decoder accepts it and the composition records it; image assembly does not bake a second slot today, and on-node updates now trial a release through a content store.

Service entry

Each item under partitions.<name>.services[]:

FieldTypeRequiredDescription
namestringyesUnique identifier within the partition. Cross-referenced by run.yaml to attach per-service runtime configuration.
imageOCI ref or Dockerfile pathconditionalA prebuilt service image, or a local Dockerfile (or a directory holding one) that the build resolves under the project root and builds for you. Use this or artifact, not both.
artifact<manifest>#<output>conditionalReference to an imported app artifact output. Use this or image, not both.
runtimeoci, mirage, unikraft, host, or autorequired with imageService payload runtime. Artifact references derive this from the artifact manifest.
isolationauto, runc, spt, hvt, qemu, host, or firecrackerno (default: auto)Build-side isolation default; auto resolves from the runtime (runc for oci, host for host). run.yaml may choose a concrete placement.
cpuslist of intnoDefault CPU set. run.yaml placement can override.
memory_mbintno (default: 128)Default memory cap for the service.
interfaces[]listnoTyped service interfaces: type: ip with bridges; type: frame_bus with apids and send_to; type: host_network with bind_tcp; type: data_link with link, mbps and mode; type: supervision_page. See service interfaces.
bridgeslist of stringnoShorthand for interfaces: [{type: ip, bridges: [...]}]. A bridge is a named virtual network segment.
lifecycleprocess, bgprocess, scripted, or internalno (default: process)How the supervisor runs the service: long-running, self-daemonizing, one-shot, or a grouping node.
environmentmap of stringnoProcess environment for the service. Entries override the ones the OCI image declares in its own Config.Env.
configmap of stringnoRuntime configuration read by the supervisor, not passed to the process. The space. prefix belongs to the product: it carries the entrypoint, working directory, data paths, credential bindings, and settings the runner lowers into a launch.
depends_onlist of stringnoService names that must launch before this one. Launch-order only: this is not a health gate. Circular dependencies are rejected at build time.
healthcheckmapnoApp-declared liveness check the runner evaluates from outside the guest. Omit for services without a health requirement.

Service interfaces

interfaces[] is a list of tagged objects. The type field selects the arm, and there are five:

TypeFieldsGrants
ipbridgesAddresses on each named bridge, and connections to the peers the signed release lists.
frame_busapids, send_toA tenant socket on the frame bus, the APID range the service owns, and the peer services it may send frames to. ground is reserved and refused here: ground is reached through relay in run.yaml.
host_networkbind_tcpTCP ports published from the node into this service. It changes the container’s network isolation, so a service may declare it once, only under runc, and never alongside an ip arm.
data_linklink, mbps, modeAttachment to one named high-throughput link, admitted against the links the signed release grants. mbps is the reserved rate; mode is stream or rmap. All three are required.
supervision_pagenoneThe shared page carrying heartbeat, mission time, health, and the safe-mode word.

bridges at the top of a service entry is shorthand for a single ip arm.

Healthcheck

healthcheck is a tagged object. The type field selects the probe:

TypeRequired fieldsDescription
tcpportHealthy when the guest accepts a TCP connection on the port.
httpport, optional path (default /)Healthy when an HTTP GET returns a 2xx response.
commandargvHealthy when the command exits successfully. argv must not be empty.

All probe types also accept interval (default 10 s), timeout (5 s), retries (3), and start_period (0 s).

Parameter entry

Each item under storage.params[]:

FieldTypeRequiredDescription
idintyesInteger identifier. Must be unique within storage.params.
valuestringyesString in key=value format. The supervisor reads this as the wire-format representation; parsing is the workload’s responsibility.

Partition supervisor

Each partition runs a single supervisor process as PID 1 (the init field, pid1 by default; you do not need to declare it). Its responsibilities:

  • Start the partition’s services in order, honoring depends_on.
  • Mediate each service’s access to the frame bus.
  • Surface the declared per-service healthchecks.
  • Own the guest half of the supervision page.

Set init to a custom binary only when a workload brings its own supervision. Custom supervisors must implement the partition supervisor contract.

Build output

space build writes the composition artifacts and an SBOM to the output directory, then registers the composition in the local OCI store under the tag passed with -t:

_build/spaceos/
kernel/ # resolved partition kernels
initramfs/ # partition initramfs images
disks/ # rootfs / storage artifacts
spaceos.spdx.json # software bill of materials (SPDX)

Use space sbom <build.yaml> to inspect or write SBOM data. Every build also appends one provenance row to the host build record, giving the tag, image digest, source, completion time, status, and signing fingerprint; a failed build is recorded there too.

Service image sources

Every service is an OCI image. There are two ways to produce one:

  • An app manifest. Write an app.yaml (kind: mirage, unikraft, or host) and run space build app.yaml. The build is pinned and runs in the disposable builder VM. It writes the OCI artifact and an artifact manifest that build.yaml imports with artifact:. The build needs no local toolchain and no Docker daemon.
  • Any OCI image. A service image: is a plain OCI reference. An image built and pushed with standard tools (docker build, buildah) works. A unikernel binary packaged as FROM scratch plus the binary is a valid unikernel service image.

While assembling the system image, space build detects each service’s runtime, records it in the image annotations, and signs the composition.

Multi-architecture images

A service image may be a standard OCI image index carrying more than one architecture. Build one with standard tooling:

Terminal window
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/<your-org>/cloud-mask:latest \
--push \
.

One space build assembles a composition for one architecture. Run it once per architecture you intend to ship, and publish each result under its own tag.

Notes

  • Partition names (e.g., bus-relay, payload) are free-form identifiers. They must match between build.yaml and run.yaml.
  • OCI images are pulled once and cached locally. When you need a fresh artifact, use an immutable digest or publish a new tag, then rebuild the composition.
  • The kernel field accepts two formats: an OCI image reference (pulled from a registry) or a local file path. Values starting with ./ or / are always local paths; other values containing / or : are OCI references; a bare filename is a local path.