Skip to content

Partition model

A payload computer runs work from more than one party: your classifier, a partner's compression service, the downlink handler. SpaceOS groups the services of one project into a partition, which sets their memory, communication rules, and supervisor, and is the unit that owns a machine of its own. One space.yaml names the partitions and what runs inside them.

space.yaml names each partition, the services inside it, and their interfaces, then assigns memory, CPUs, isolation, and frame routes. SpaceOS starts each service in its selected sandbox.

Configure and inspect a partition

A partition is a key under run.partitions. Under it you declare the services, the interfaces each one may use, the memory the partition gets, and the CPUs each service is pinned to:

schema: space.project
version: "1"
build:
output: _build/spaceos
run:
partitions:
payload:
memory: 512
apids: [0x120, 0x12F]
services:
- name: classifier
image: ghcr.io/<your-org>/classifier:1
runtime: oci
isolation: runc
cpus: [0, 1]
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]
relay:
from: payload
to: [ground]
ground:
port: 8080
socket: /tmp/spaceos/ground.sock

send_to names peer services on the bus. ground is reserved and refused there: frames reach ground through the relay. The partition declares no egress list of its own, because relay is the one place the flow topology is written.

Terminal window
$ space show --sbom | sed -n '1,4p'
No signed SBOM at _build/spaceos/spaceos.spdx.json yet; printing the SBOM computed from space.yaml
SpaceOS Software Bill of Materials
kernel ghcr.io/parsimoni-labs/uniboot-kernel/node:6.18.39-62332cf6bcc6@sha256:21e9b2669e2347dd0a9b7703a248bf191ef91e412ae6b720f3d3768f6cfbbd8f

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

Terminal window
$ chmod +x classifier.sh
$ space build app.yaml -t classifier:dev
Building an app from app.yaml
✓ classifier:dev built in ...
Run it here: space run classifier:dev
Terminal window
$ space run --dry-run
Local target: macos-vmm@sha256:4c549a833d4e9801bdc6f6e55485e64b23d5d33e0190ba713a20131f114a65da+vz
Boot image: not validated (not in the local store: ghcr.io/parsimoni-labs/uniboot-kernel/node:6.18.39-62332cf6bcc6@sha256:21e9b2669e2347dd0a9b7703a248bf191ef91e412ae6b720f3d3768f6cfbbd8f); build first
image: _build/spaceos
partitions:
payload 512 MB apids 0x120, 0x12f
services:
classifier runc cpus 0, 1
sdls-downlink runc
relay: payload -> ground (sdls)
ground: port 8080 socket /tmp/spaceos/ground.sock
1 partition, 512 MB total
Dry run complete; no workload started.

Boot image: not validated reports that only the service image is built; a boot also needs the system image that carries the kernel and the partition init. The boot and the report both use the local runner. Build it, boot it, then inspect the running partition:

Terminal window
space build . -t classifier:dev
space run classifier:dev
space status --json
space logs classifier

Partitions

A partition is the unit that receives memory and communication rules, and it is a tenant: its workloads run in a machine that belongs to it alone, so two partitions never share a kernel. Inside that machine, each service runs behind the backend its isolation field names:

  • A virtual machine of its own. hvt boots a unikernel on the CPU's virtualization hardware through a Solo5 tender. firecracker boots a virtual machine under Firecracker, and qemu boots a Unikraft image under the x86-64 emulator on any host.
  • A container. runc runs an OCI container in the partition's machine, with a read-only root, its own user namespace, a default-deny seccomp profile, and cgroup limits. seccomp is the Linux filter on a process's system calls, and a default-deny profile refuses every call its allow-list does not name. These controls hold the partition's services apart from each other; the machine holds the partition apart from other partitions.
  • A confined process. spt runs a Solo5 unikernel as a seccomp-confined process, for machines without hardware virtualization.
  • A host process. host runs the binary uncontained on a development machine, for test fixtures.

Isolation backends lists every backend, what the machine must provide for it, and which target profiles offer it.

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 runs unmodified on any host that implements it, so a change of hypervisor leaves the composition alone. That interface is thirteen calls, declared in Solo5's solo5.h: exit and abort, setting the thread-local storage base, two clocks (monotonic and wall), yield until a deadline or an I/O event, console write, and acquire, read, and write for one network device and one block device each. KVM on Linux is the hypervisor the target profiles select. Xen arrives in the next release [v1.1], and separation kernels are agreed direction [roadmap]: a mission that needs a verified boundary chooses it by the hypervisor its target profile names. Isolation backends describes each one.

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, beside the services themselves.

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_to list 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. The service binds those ports in the node's own network namespace, so they are reachable on the node's addresses and the container loses its network isolation; a service may declare it only under runc, and never beside an ip interface.
  • 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-runner owns 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 space.yaml.

Isolation layers

Partition level. Each partition runs in a machine of its own, isolated by the CPU's virtualization hardware. A fault, a crash, or a radiation-induced bit flip (a single-event upset) in one partition cannot reach another through memory, and a kernel defect reached from one partition's container stays inside that partition's machine. With several partitions on a node, the runner boots one Firecracker VM per partition to hold its containers. When a release names a single partition, the node itself is that partition's machine: no other partition shares it, and the container controls alone stand between its services and the control plane, as Tenant machines describes.

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

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

A GPU, an FPGA, or an AI engine on the payload computer is a shared device that no partition should reach by default. SpaceOS puts a broker in front of it: a workload asks for an accelerator by role, such as inference, and never names a device. The accelerator schema names two device kinds, GPU and FPGA. The broker speaks vAccel, and it opens a session only under a grant signed for that run on that target. vAccel is an open-source API through which a virtual machine or a container asks the host to run an accelerator operation, such as an inference, on the device; ukAccel is the same interface for Unikraft unikernels.

No shipped target profile declares an accelerator, so no workload reaches one on a supported target. The delivery paths below are agreed direction [roadmap]:

  • Linux services (isolation: runc) reach the device nodes the target profile grants, such as /dev/dri for 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. A target without both keeps passthrough off.

Each accelerator class has its own named delegation interface [roadmap]. The last column names the board where each interface ran in a bench demonstration; no shipped profile carries that board.

AcceleratorInterfaceBench demonstration
GPU (NVIDIA)vAccelJetson TX2i
FPGAukAccelXilinx Versal
AI engineVitis AIVCK190
Many-coreOpenCLKalray MPPA

When services share a GPU, the broker multiplexes the device so that each service sees its own queue [roadmap].

The target profile lists its accelerators, and a service that needs one runs only on a target that lists the matching device [roadmap]. Moving a service to an accelerated board keeps its name and its image: space deploy --plan checks the target's profile and names the accelerator granted to each service before it prepares the release [roadmap].

Partition and service declarations

One partition key carries both halves of the same statement:

The partition declares...Each service under it declares...
Supervisor override (optional)The image or artifact it is built from
How much memory the partition getsWhich CPUs it is pinned to
APID ownership and the bus endpoints it needsIts interfaces, dependencies and isolation backend
Whether the partition disk is enabledIts healthcheck probe

The relay shape and the ground endpoint sit beside partitions in the same run: section. The same project 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 no project. They are properties of a connected target.