Partition model
SpaceOS uses a partition to group services from one project. The partition
sets their memory limit, communication rules, and supervisor. Each service
still runs in its own virtual machine or container sandbox. Use the same
partition names in build.yaml and run.yaml.
Configure and inspect a partition
Use the same partition and service names in both manifests. In build.yaml,
declare the service and the interfaces it may use:
schema: space.buildversion: "1"kernel: linuxkit/kernel:6.6.13output: _build/spaceos
partitions: payload: services: - name: classifier image: ghcr.io/<your-org>/classifier:1 runtime: oci isolation: runc interfaces: - type: frame_bus apids: [0x120, 0x12F] send_to: [sdls-downlink] - name: sdls-downlink image: ghcr.io/parsimoni-labs/sdls-downlink:1 runtime: oci isolation: runc interfaces: - type: frame_bus apids: [0x130, 0x13F]send_to names peer services on the bus. ground is reserved and refused
there: frames reach ground through the relay below.
$ space sbom build.yaml | sed -n '1,4p'SpaceOS Software Bill of Materials
kernel linuxkit/kernel:6.6.13 payload pid1In run.yaml, assign resources and route the frames. The partition declares
no egress list of its own: relay is the one place the flow topology is
written, and a manifest that states both is refused.
schema: space.runversion: "1"image: _build/spaceos
partitions: payload: memory: 512 apids: [0x120, 0x12F] services: - name: classifier isolation: runc cpus: [0, 1]
relay: from: payload to: [ground]
ground: port: 8080 socket: /tmp/spaceos/ground.sockBuild the service image, then rehearse the placement before booting it. The
rehearsal names the payload partition, its 512 MB memory limit, the CPU set,
and the allowed APID range. A mismatched service name or an unavailable backend
fails the rehearsal:
$ chmod +x classifier.sh$ space build app.yaml -t classifier:devBuilding an app from app.yamlBuilt classifier:dev@sha256:e1c0f1a2d51660ac0aeed2d1fd028e656e1b8893d09bb25f96e39c0321003765 (arm64)$ space run classifier:dev --runtime run.yaml --dry-runLocal target: ...Boot image: not validated (not in the local store: linuxkit/kernel:6.6.13); build firstimage: classifier:devpartitions: payload 512 MB apids 0x120, 0x12f services: classifier runc cpus 0, 1relay: payload -> ground (sdls)ground: port 8080 socket /tmp/spaceos/ground.sock
1 partition, 512 MB totalDry run complete; no workload started.Boot image: not validated reports that only the service image is built; a boot
also needs the composition that carries the kernel and the partition init. The
boot and the report both use the local runner, so this sequence needs the local
context. Build it, boot it, then inspect the running partition:
space build . -t classifier:devspace run classifier:dev --runtime run.yamlspace status --jsonspace logs classifierPartitions
A partition is the unit that receives memory and communication rules. Each
service in it runs in its own sandbox. The service-level isolation field
picks one of three boundaries:
- A virtual machine. The service is a sealed guest with its own memory and
no general-purpose OS inside it.
hvtboots a unikernel on the CPU’s virtualization hardware through a Solo5 tender.qemuboots a Unikraft image on a host without that hardware.firecrackerboots a unikernel in a micro-VM with a reduced device surface. - A confined process. The service runs on the kernel of
dom0, the SpaceOS control domain, so a kernel
defect crosses this boundary where it does not cross a virtual machine’s.
runcruns an OCI container with a read-only root, its own user namespace, a default-deny seccomp profile, and cgroup limits.sptruns a Solo5 unikernel as a seccomp-confined process, for boards without hardware virtualization. - A host process.
hostruns the binary uncontained on a development machine, for test fixtures.
The hypervisor underneath a virtual machine belongs to the target, and you do
not name it in isolation. A unikernel built against the Solo5 host interface,
about ten calls wide, runs unmodified on any host that implements it, so a
change of hypervisor leaves the composition alone. KVM on a Linux host is the
substrate a target profile selects today. Xen arrives in the next release
[v1.1], and separation kernels are agreed direction [roadmap].
A multi-service partition boots its own init process, the supervisor, which is
pid1 by default. The supervisor starts the declared services in dependency
order and reports their lifecycle and health. Memory is assigned per partition
and CPUs per service in run.yaml;
build.yaml declares what the partition contains.
Services in one project may share communication rules while keeping separate sandboxes.
Service interfaces
A service declares typed interfaces at build time; the runner wires them at launch. There are five kinds:
- Frame bus. Carries fixed-size telemetry and command messages between
services and ground. Each message carries an
APID, the stream label used to sort
messages. The
send_tolist controls where a service may send it. - IP. Provides a standard IP network. The service binds ports on its declared bridges, and connections not listed in the signed release are rejected.
- Host network. Publishes named TCP ports from the node into the service. It is capability-gated, and a privileged port on a target that permits one changes the container’s own boundary.
- Data link. Carries large sensor products over a spacecraft serial fabric such as SpaceWire or SpaceFibre, or over a high-bandwidth virtual queue. The declaration names the link, and the signed release decides whether the service may attach to it.
- Supervision page. A small shared-memory page between the host and the
guest carrying heartbeat, mission time, health, and the safe-mode command
word.
space-runnerowns the host side and can put the service in safe mode.
Authorization and APID routing
Each signed release lists what every service may read, write, or open. An APID
is the 11-bit CCSDS number used to label a telemetry or command stream. The
number helps receivers sort messages. It does not grant permission to send
them. Declare the APID range and allowed destinations in run.yaml.
Isolation layers
Partition level. The CPU’s virtualization hardware isolates a virtual machine. A fault, a crash, or a radiation-induced bit flip (a single-event upset) in one partition cannot reach another through memory. Kernel namespaces, cgroups, and seccomp filtering isolate a confined process, a weaker boundary because the service shares the dom0 kernel. The selected execution boundary determines whether the service shares that kernel or runs in a separate virtual machine.
Service level. A multi-service partition’s supervisor keeps its services
in separate processes with declared dependencies, healthchecks, and restart
policy. Services communicate through their declared interfaces; there is no
shared memory between services. The frame bus is sized for telemetry and
command messages, so co-located services that need bulk exchange enable the
partition block device (disk: true).
Isolation backends
The service-level isolation field selects the concrete backend:
hvt: Solo5 on KVM. The unikernel path with hardware VM isolation.spt: Solo5 seccomp tender. The unikernel path where KVM is unavailable.runc: OCI container. Any Linux application, shared dom0 kernel.qemu: QEMU launcher for Unikraft images (x86_64 only).host: host-native process, for development and test fixtures.auto: let the runtime pick a compatible backend.firecracker: KVM-backed micro-VM with a reduced device surface, for unikernels. Linux micro-VM images follow [v1.1].
Flight-domain boundary
The spacecraft’s safety-critical flight software is a separate trust domain and never a SpaceOS partition. It handles attitude control, orbit propagation, and command and data handling, usually on a real-time operating system running a framework such as F-Prime. It boots on its own authority and runs its own fault protection, with a hardware watchdog behind it. SpaceOS observes it over a spacecraft bus and may relay commands that carry its authority. Boot, supervision, and recovery stay with the flight domain, as SpaceOS on the spacecraft describes.
A partition can be bus-facing, meaning it is trusted to exchange telemetry and commands with the flight domain over the frame bus. A bus-facing partition relays; it does not own the flight software.
Hardware acceleration
An accelerator broker opens the GPU, FPGA, or AI engine for a service. Before opening the device, the broker checks the service and device listed in the signed release. A service receives no device access by default.
- Linux services (
isolation: runc) reach accelerators through the device nodes the target profile grants (for example/dev/drifor a GPU). - Unikernel services use host-mediated delegation. The guest calls a narrow API and the host forwards it to the device, as Solo5 already does for virtual block and network devices.
- Direct passthrough, where the guest drives the device itself, requires a dedicated device and an IOMMU, the hardware that limits which memory a device may reach. Targets without both disable passthrough and keep the isolation boundary.
Each accelerator class has its own named delegation interface:
| Accelerator | Interface | Exercised on |
|---|---|---|
| GPU (NVIDIA) | vAccel | Jetson TX2i |
| FPGA | ukAccel | Xilinx Versal |
| AI engine | Vitis AI | VCK190 |
| Many-core | OpenCL [roadmap] | Kalray MPPA |
When services share a GPU, a broker multiplexes the device so that each service sees its own queue.
The target profile lists its accelerators. A service that needs one runs only on a target that lists the matching device.
Manifest references
The two manifests use the same partition names as keys:
build.yaml defines… | run.yaml defines… |
|---|---|
| Supervisor override (optional) | How much memory to allocate |
| What services to bundle | Which CPUs to assign per service |
| Service interfaces and dependencies | Per-service isolation backend |
| Per-service healthcheck probe | APID ownership and send_to flow policy |
| Whether to enable the partition disk | Relay shape and ground endpoint |
The same composition runs with different placements without rebuilding: 512 MB on a development laptop, or 128 MB on a constrained on-board computer. Board facts such as the kernel, device tree, firmware, and secure-boot keys belong to neither file. They are properties of a connected target.