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 /inferand 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:
space build -f Dockerfile -t ghcr.io/<your-org>/wildfire-segmenter:1space image push ghcr.io/<your-org>/wildfire-segmenter:1Step 2 names the same reference in build.yaml.
SpaceOS runs OCI images whoever built them, so an existing pipeline works here unchanged:
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:
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.buildversion: "1"kernel: linuxkit/kernel:6.6.13output: _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: /healthThe 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.runversion: "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.sockThe 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:
$ 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, 1Step 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:
space build . -t wildfire-segmenter:devspace run --name wildfire-dev wildfire-segmenter:dev --runtime run.yamlIn another terminal, watch the service:
space statusspace status --jsonspace logs wildfire-devFind the wildfire-segmenter IP address in the report, then send it an image:
curl --fail \ --data-binary @scene.tif \ http://<service-ip>:8080/inferThe service accepts the input and answers:
{"accepted":true}Open http://localhost:8080 to see the result message on APID 0x120.
On-board inference cuts the result down to a mask that fits in a contact window too small to carry the whole scene.
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:
- NVIDIA Jetson TX2i for CUDA and TensorRT.
- Xilinx VCK190 for Vitis AI and FPGA execution.
- Kalray Coolidge for many-core execution on its MPPA processor array.
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:
$ space keys generate releaseGenerated 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:...space build . \ -t ghcr.io/<your-org>/wildfire-composition:1 \ --release \ --signing-profile release \ --rollback-index 1space image push ghcr.io/<your-org>/wildfire-composition:1Check the image picked for the board, the CPU architecture, the isolation backend, and the accelerator given to the service:
space deploy ghcr.io/<your-org>/wildfire-composition:1 \ --target <target> \ --planDeploy the release after reviewing those values:
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.