Skip to content

Platform architecture

Payload software written on a laptop has to reach a computer in orbit that you talk to a few minutes at a time, over a link that loses packets and drops for hours. The Parsimoni Platform splits that path into three places, each with one job: your machine builds and signs a release, a ground holds it and carries it across the link, and the node checks it and runs it.

Build on your machine

space build reads space.yaml and builds each service from its Dockerfile, or from an app.yaml for a MirageOS or Unikraft unikernel. The build runs inside a disposable builder VM, so your machine needs no toolchain and no Docker daemon.

Terminal window
space build
space show --sbom

The result is a composition: an OCI image index holding every partition and service of the project, with an SBOM and a provenance statement attached. An OCI image index is a JSON document that lists several image manifests by their SHA-256 digests. The SBOM (software bill of materials) is an SPDX document that lists every component the build used. The provenance statement records which inputs and which build steps produced the composition. Both are stored as blobs in the same layout, and the index names them by digest, so changing one byte of either changes the digests the signature covers.

A composition is target-independent, so the same composition runs on the development VM, a board, and a cloud instance. The build signs it under your identity, which is a pair of keys: an Ed25519 key and an ML-DSA-65 key. ML-DSA-65 is the lattice-based signature scheme of NIST FIPS 204. It is designed to resist a quantum computer, which would break Ed25519. A node accepts a release only when both signatures verify, so a release stays authentic as long as either scheme holds. The build then records the composition in the local image store. Signing and verification covers the identities, and space image push copies a composition to an OCI registry.

Run on your machine

space run boots the composition in a VM on your machine: Apple's Virtualization framework on macOS, QEMU on Linux. The VM is a complete SpaceOS node, the development VM, and it boots the same system image a board boots.

Terminal window
space run
space status
space logs

space status lists what each service reports, and space logs prints what they wrote. space run also serves a local dashboard, on port 8080 by default, which shows the same report and the frames each service sends toward the ground; space status is its terminal view. space show reads back what the last save rebuilt, signed, and sent to the VM.

Deliver through a ground

A ground is the control-plane endpoint that holds deploy records and carries releases to nodes. Each target records its ground: this machine's local ground for the development VM and bench work, or a hosted ground such as run.space.

Terminal window
space deploy --target sat-42 --plan
space deploy --target sat-42
space show sat-42 v1

space deploy assembles the composition for the target's profile, which supplies the board kernel, device tree, firmware, and boot signer. It signs the release and writes a durable deploy record, and the ground moves that record from published to activated. A contact window is the few minutes during which a satellite is in radio range of a ground station; the next one may be hours away.

The release crosses a space link as a CFDP file transfer over BPv7 bundles with BPSec integrity, and SDLS authenticates each link frame. CFDP (CCSDS 727.0-B-5) is the space file transfer protocol. BPv7 (RFC 9171) is the Bundle Protocol, which stores a bundle at each hop until the next hop is reachable. BPSec (RFC 9172) adds an integrity block to each bundle, and SDLS (CCSDS 355.0-B-2) is the link-layer security protocol.

SDLS appends to each frame a sequence number and a 16-byte MAC computed over the frame's header and data under a key the two ends share, and the receiver drops a frame whose MAC fails or whose sequence number it has already seen. Ground relay gives the frame's bytes.

A release waits in the ground's store between contact windows and resumes where it stopped. The ground sends it as a CFDP Class 2 transfer, the acknowledged mode: each File Data PDU carries its byte offset, and the receiver answers with NAKs that list the missing byte ranges. At the end of a contact the ground freezes the transfer and records how far it has gone in its durable store. The next contact thaws it, and the ground sends the rest of the file and the NAKed ranges only. No byte that arrived is sent twice, so the contact time a release needs does not depend on how many windows it is split across. For a release of S bytes over an uplink of R bit/s it is at least 8S / R, plus the headers of each frame and bundle. A 40 MB release over a 1 Mbit/s uplink needs at least 320 seconds of contact, a little over five minutes, which fits in one ten-minute pass. Boot, updates, and rollback follows a deploy through its states.

What runs on the node

A node boots a Linux kernel and a small control plane of statically linked OCaml services. The control plane is the set of SpaceOS services that admit, start, and supervise workloads; the workloads are everything else.

The launch plan that space-init checks is the list of services the node starts, in dependency order. Each entry names the service's image by its SHA-256 digest, the size of its blob, the dm-verity root hash of its file system, its isolation backend, its CPUs and memory, its health check, and its signed capability. The plan is one component of the signed release manifest, which names it by digest. space-init verifies the manifest against the release anchors baked into its own initramfs, reads the plan from the node's content store, and refuses it when its bytes do not hash to the digest the manifest names. The plan is measured in that sense: the node records which software it selected, by digest, before any of it starts.

Each control-plane service owns one job:

ServiceJob
space-initPID 1. Checks the measured launch plan against what is on disk, mounts the verified root, starts the other services, and arms the watchdog.
space-runnerAdmits signed workloads, starts each one behind its isolation backend, and supervises it.
space-deployReceives releases from the ground, stages them, and signs the acknowledgements.
space-netRoutes the frame bus between services and the ground link, and carries the node's network.
space-secretHolds the node's key material. Other services hold key identifiers only.
space-logs, space-statsCollect each workload's logs and metrics for the ground.

SpaceOS owns the whole node on a system-image target: the signed boot image, the verified root, and everything above them. The same services run as a signed bundle on a Linux the vendor owns in a daemon install.

Tenants and workloads

A tenant is one project's partition. Each tenant's workloads run in a machine that belongs to that tenant alone, so tenants never share a kernel. A unikernel runs in a virtual machine of its own under a Solo5 tender or Firecracker. A Solo5 tender is the small host program that loads a unikernel and serves its host calls. Firecracker is the minimal KVM virtual machine monitor from AWS. A Linux service runs as a runc container inside its tenant's machine, which is either the node itself when the release names a single tenant, or a Firecracker VM the runner raises for that tenant. Isolation backends lists every backend and what the machine must provide for it, and Partition model explains how a project declares its partitions and services.

Library operating systems

A unikernel links one application with the operating-system libraries it needs and nothing else. The result is a single-purpose machine image of a few megabytes, with no shell and no general-purpose kernel inside it. SpaceOS builds MirageOS and Unikraft unikernels from an app.yaml and runs them behind the hvt, spt, firecracker, or qemu backends. Build a unikernel walks through one.