Skip to content
Steps 1 to 4 run the service as a runc workload and their compose transcripts are executed on every build of this site; step 5 selects an accelerator that no shipped target profile declares.

Run AI inference

Run an existing inference service as an OCI image on SpaceOS and route its result to ground. The manifests below hold for wildfire segmentation and for any other compiled model.

Prerequisites

  • The Space CLI on your computer.
  • An inference service that builds from a Dockerfile. It accepts POST /infer and returns {"accepted":true}, then writes each result to the SpaceOS frame bus.
  • A registry that can store the service image.

The service uses two SpaceOS data paths. A data link is a separately granted high-throughput path for sensor data, named in the service’s interfaces and admitted against the links the signed release grants it. The frame bus is the CCSDS path that carries telemetry and command messages between partitions and ground.

Step 1: Build the service image

space build reads the service’s Dockerfile, builds it in the builder VM, and signs the ARM64 image it leaves in the local store. space image push publishes that image to the registry its reference names:

Terminal window
space build -f Dockerfile -t ghcr.io/<your-org>/wildfire-segmenter:1
space image push ghcr.io/<your-org>/wildfire-segmenter:1

Step 2 names the same reference in build.yaml.

SpaceOS runs OCI images whoever built them, so an existing pipeline works here unchanged:

Terminal window
docker buildx build \
--platform linux/arm64 \
-t ghcr.io/<your-org>/wildfire-segmenter:1 \
--push \
.

Use a multi-architecture image when you also want to run the service on an x86-64 development computer:

Terminal window
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/<your-org>/wildfire-segmenter:1 \
--push \
.

Step 2: Add the service to a composition

Create build.yaml:

schema: space.build
version: "1"
kernel: linuxkit/kernel:6.6.13
output: _build/spaceos
partitions:
payload:
init: pid1
disk: true
services:
- name: wildfire-segmenter
image: ghcr.io/<your-org>/wildfire-segmenter:1
runtime: oci
isolation: runc
interfaces:
- type: ip
bridges: [payload-net]
- type: frame_bus
apids: [0x120, 0x12F]
send_to: [sdls-downlink]
- type: data_link
link: sensor-input
mbps: 200
mode: stream
healthcheck:
type: http
port: 8080
path: /health

The data link carries source imagery into the service. The frame bus carries small results and status messages. Keep large image products on the data link or in content-addressed storage.

Step 3: Set memory and CPU placement

Create run.yaml:

schema: space.run
version: "1"
image: _build/spaceos
partitions:
payload:
memory: 2048
apids: [0x120, 0x12F]
services:
- name: wildfire-segmenter
isolation: runc
cpus: [0, 1]
relay:
from: payload
to: [ground]
ground:
port: 8080
socket: /tmp/spaceos/ground.sock

The service name must match build.yaml. The APID range labels the partition’s streams, and the send_to list on the service’s frame_bus interface in build.yaml limits which destinations it may send to. Resolve both files before building:

Terminal window
$ space sbom build.yaml | sed -n '1,4p'
SpaceOS Software Bill of Materials
kernel linuxkit/kernel:6.6.13
payload pid1
$ space run _build/spaceos --runtime run.yaml --dry-run | sed -n '4,7p'
partitions:
payload 2048 MB apids 0x120, 0x12f
services:
wildfire-segmenter runc cpus 0, 1

Step 4: Run inference locally

This step needs the local context, because space status --json reads the runner on your computer.

Build the composition and start it under the local runner:

Terminal window
space build . -t wildfire-segmenter:dev
space run --name wildfire-dev wildfire-segmenter:dev --runtime run.yaml

In another terminal, watch the service:

Terminal window
space status
space status --json
space logs wildfire-dev

Find the wildfire-segmenter IP address in the report, then send it an image:

Terminal window
curl --fail \
--data-binary @scene.tif \
http://<service-ip>:8080/infer

The service accepts the input and answers:

{"accepted":true}

Open http://localhost:8080 to see the result message on APID 0x120.

Infrared satellite scene of a wildfire, with active fronts visible as bright lines against burned ground. Binary mask marking the active fire fronts found in the scene.
The scene submitted to the service, and the mask it returns. The mask is a few kilobytes; the scene is tens of megabytes.

On-board inference cuts the result down to a mask that fits in a contact window too small to carry the whole scene.

A sensor or local test client sends an image to the inference service. The service runs the compiled model and returns a small result through the SpaceOS frame bus.

Step 5: Select an accelerator target

Keep the service name and OCI image unchanged. Pick a board whose profile lists the accelerator your image needs. Space CLI checks that profile before it prepares the release.

The boards intended to carry this, each in bring-up:

Build and publish the release composition. --signing-profile names a release identity that already exists, so create one on the bench first if space keys ls shows none:

Terminal window
$ space keys generate release
Generated signing identity release
ed25519-key: /tmp/space-docs/config/keys/release.ed25519.pem
mldsa65-key: /tmp/space-docs/config/keys/release.mldsa65.pem
fingerprint: sha256:...
Terminal window
space build . \
-t ghcr.io/<your-org>/wildfire-composition:1 \
--release \
--signing-profile release \
--rollback-index 1
space image push ghcr.io/<your-org>/wildfire-composition:1

Check the image picked for the board, the CPU architecture, the isolation backend, and the accelerator given to the service:

Terminal window
space deploy ghcr.io/<your-org>/wildfire-composition:1 \
--target <target> \
--plan

Deploy the release after reviewing those values:

Terminal window
space deploy ghcr.io/<your-org>/wildfire-composition:1 --target <target>
space status <deploy-id>

Step 6: Return only the useful result

Write the compact result to the frame bus, such as a mask or a detection list. Keep the original sensor product on board unless the mission asks for it. Fewer bytes then cross the space link, and the ground system acts on the result sooner.