Boot, updates, and rollback
SpaceOS verifies each release before activation and retains a known-good state for rollback. Workload rollout and system updates use separate mechanisms.
Deploy and watch a release
Inspect the complete release before sending it:
space deploy payload:release --target sat-42 --planThe plan names the composition digest, target profile, boot files, signing identities, security versions, and expected image digest. Deploy the release and save the returned ID:
space deploy payload:release --target sat-42space status <deploy-id>The status checklist advances from published to activated. The node signs
the installed and activated acknowledgements.
Boot verification
Boot checks authority and content. Signature verification identifies who authorized the content. Digest verification checks that the authorized bytes arrived unchanged. On the Raspberry Pi CM5 reference target the chain is:
| Stage | Verification |
|---|---|
| Board ROM and firmware | Native board secure boot verifies the customer-signed boot image. |
| Boot release manifest | An Ed25519 signature binds the kernel, runner, applications, and rollback version together. |
| Root activation | dm-verity verifies every filesystem block on read, against a root hash pinned on the signed kernel command line. |
space-init handoff | The measured launch plan is checked against what is actually on disk, and boot stops on any mismatch. |
| Workload admission | TUF authorizes signed workload digests, with no network needed. |
Each layer verifies its own object, and a pass at one layer does not satisfy another. The native board signature authorizes the firmware boot stage; it says nothing about which workload may launch. On the development VM the chain starts at the host hypervisor. Its root of trust is the development signing identity pinned on first use. The dm-verity and TUF layers are the same.
Measured boot evidence
The signature and content checks above rest on keys and digests, so they hold on every target and need no security chip. Measured boot needs one. A target that carries a TPM measures the release it booted into platform configuration register 11. A target without one says so on the console at boot, and its chain is the signature and verity layers alone.
The TCG PC Client profile reserves register 11 for the operating system, so the measurement leaves the firmware and bootloader registers alone and the system is measuring itself. The value says which release the running system reported, and the TPM holding it is one that system chose to talk to. The boot prints the release it measured, and anyone who holds that release can recompute the value register 11 must then hold. The measurement does not chain to firmware, so it does not separate a node that booted the measured release from one whose firmware was replaced and then measured the same value into register 11.
Read the target profile to know which you have. It names the device classes the board carries and the security claims the profile makes, so the strength of a target’s boot chain is a fact you look up. Attestation over measured-boot evidence is planned for the next release [v1.1].
Update mechanisms
SpaceOS uses different mechanisms for workloads and for the boot-critical system:
| Mechanism | Changes | Activation |
|---|---|---|
| Workload blue/green | Independently deployable services and their policy | Old and new run side by side; promote after health probes |
| System release | Kernel, initial RAM filesystem, control plane, verity root | Append to the content store; trial reboot; confirm after boot health |
A workload update leaves the node running, while a system release enters the content store and starts through a trial reboot.
The two mechanisms also map onto the two integration models. Blue/green workload rollout and signed admission apply everywhere, including daemon installs on a vendor Linux. So does the runtime’s own signed self-update. System releases and the anti-rollback floor apply to system-image targets, where SpaceOS owns the boot path.
Workload blue/green rollout
A workload generation rolls out without a reboot:
- Preflight. SpaceOS checks the candidate image digest, signature, permitted resources, security version, and available memory. If the old and new copies cannot run together, the update stops and the old copy keeps running.
- Launch green. Candidates start in dependency order while the old (blue) instances remain authoritative.
- Probe. Each service must pass its declared healthcheck within its deadline.
- Promote. The first healthy probe promotes the new generation. The node persists the commit, and the blue instances stop after that.
A candidate that fails before promotion is stopped and blue stays authoritative. A multi-service generation reports either a committed result or a rolled-back result.
Promotion becomes an atomic route flip, with a health acknowledgement signed over the workload’s own probes, in the next release [v1.1].
System release rollout
System images live in a content store, one partition that holds any number of dm-verity filesystem images indexed by their root hash. There are no fixed A/B partitions. An update appends the new image and shares unchanged content with the old. A small, tamper-evident boot record then holds three pointers: the confirmed release, the release on trial, and a last known good to fall back to. The boot record selects releases by content identity. Retention policy and free space set how many rollback targets stay available.
A boot-critical change is staged into the store, verified, and read back. It is then armed as the trial release, with a power-loss-safe record, before the node reboots. The trial boot must pass native secure boot, the release manifest, dm-verity activation, state recovery, and the configured probes. Only then does the confirmed pointer move. A failure, or exhausted boot attempts, falls back automatically to the last healthy release.
A trial release gets three boot attempts. Each attempt is bounded by the payload watchdog, which resets a node that stops answering within 60 seconds of the last refresh, so an attempt that hangs cannot hold the trial open. The worst case from activation to running the last healthy release again is three attempts of boot time plus that timeout. Size your contact window against that figure before you activate a release you cannot supervise.
Every phase has a durable recovery rule for power loss. The transfer resumes after a loss during receive. The confirmed release remains untouched if power fails during staging. Confirmation retries idempotently after a loss. The confirmed release stays bootable and verified throughout the update.
Anti-rollback floor
The security version number (SVN) is separate from the release generation. It
changes only when policy bars older security states, so a routine rebuild
leaves it alone. The operator publishes it with space build --rollback-index N,
and a --release build requires 1 or more: a release left at the development
index would never outrank its predecessor, and that is refused where you can
see it.
After a system release confirms, the node ratchets its floor. Any release whose SVN is below the floor is invalid, even with an otherwise valid older signature. Workloads updated on their own carry per-component SVNs in an authenticated rollback map, so each ratchet applies only to the workload that was updated.
The floor is anchored to the strongest monotonic state its target has, and each target profile publishes which that is:
| Anchor | Where the floor lives | Profile claim |
|---|---|---|
| Software | A MAC-authenticated record on the node’s own writable storage, sealed under a boot state key whose source the target profile names: the SoC’s one-time-programmable memory on cm5-dev, the provider’s virtual machine monitor on scaleway, the tender on macos-vmm. | software-rollback |
| Hardware | A monotonic counter in a discrete TPM 2.0 NV index, which only ever counts up and survives a wiped disk. | tpm2-rollback |
The floor belongs to the system-image model. space target apply mints the boot
state key for a node whose boot disk it assembles, prints the path it generated,
and binds the key into the disk, and a node SpaceOS boots takes the source from
its signed kernel command line. A daemon install has no boot disk of ours and no
whole-system release to floor, which is the same boundary the
integration models draw.
The node refuses a key file it cannot account for. The path has to be a regular
file, owned by the user that wrote it, at mode 0600. The mode is compared for
equality over every bit, so a file granting group or other access is refused.
The path is checked without following a symlink, so a symlink left in its place
is not the floor. A file failing any of those is refused and never repaired: a
chmod closes the mode and leaves the access the permissive one already
granted, and a chown would adopt a file this node did not write. A node with
no key configured brings up no service and prints no boot state key configured; refusing service bring-up.
Of the five target profiles shipped today, cm5 claims tpm2-rollback and the
other four claim software-rollback. The claim sits on the profile because the
same board carries either floor: cm5-dev and cm5 both name the Raspberry Pi
CM5, and only cm5 requires the discrete TPM the counter lives in. An attacker
who can rewrite the software floor’s own storage and holds its key can move that
floor. That attacker cannot move a TPM counter.
Routine rollback stays possible within that boundary. space rollback
re-delivers the target’s prior activated release generation as a fresh deploy,
through the same approve, uplink, and activate pipeline, and prints a new deploy
id. The node admits it only while its SVN still satisfies the committed floor.
Rollback is a delivery-phase operation, so it runs through the selected
context’s ground queue and refuses by name on the local context.
space rollback --target sat-42space status <rollback-deploy-id>Deployment states
A deploy is a durable record that survives restarts, missed contacts, and
redelivery. The states, in order, are published, approved, queued,
uplinking, installed, and activated. rejected and failed are terminal
outcomes. These are the deploy pipeline’s own states. space status reports a
separate runtime control state (running, backoff, exited, failed) once
a deploy is activated.
Two signed acknowledgements from the node mark the transitions. A staged acknowledgement (bytes written, verified, read back, not booted) moves the deploy to installed. A healthy acknowledgement (probes passed, floor ratcheted) moves it to activated. Ground-side delivery evidence records delivery and nothing more.
space status <deploy-id> renders the pipeline as a checklist, one row per
stage with its evidence. After activation the workload instance appears under
space status with its runtime state.