Skip to content
StatusQuickstartDocsArchitectureDesign Run locally
Docs / Internals & notes / Updates

SwiftOS Update And Rollback Guide

This guide explains how to update and roll back the current SwiftOS image. It is written for operators, release testers, validation owners, and support engineers who need a predictable way to move from one checked-in revision or artifact set to another.

SwiftOS does not yet provide an on-target graphical installer, online updater, or live package upgrade command. The supported update model starts from host-built immutable artifacts, then either boots those artifacts directly or stages them through the checked A/B validation paths. Keep the previous artifacts available for rollback.

Use this guide with:

Current Update Model

SwiftOS is built around immutable boot and software artifacts.

Artifact Update method Rollback method
build/kernel.elf Rebuild with make build Boot a previously saved kernel or return to an older commit
build/base.img Repack with make base-image Boot a previously saved base image or return to an older commit
build/virt.dtb Rebuild with make build/virt.dtb Boot the previous DTB used with the previous kernel
build/swift-os.img Rebuild with make disk Boot the previous disk image
Package payload image Rebuild with make package-fixture or package tooling Attach the previous payload image
Package store image Rebuild with make package-store-fixture or tools/pkgstore.swift Attach the previous store image
Model bundle files Rebuild with make model and make base-image Boot a previous base image or ship an older verified generation

The running guest has only RAM-backed writable storage under /tmp. An update does not preserve /tmp, and a reboot clears it.

Choose An Update Workflow

Start with the artifact that changed. SwiftOS deliberately keeps these paths explicit so an operator can prove exactly what was promoted and what rollback means.

Change Current workflow Use when Minimum proof Rollback evidence
Kernel, syscall, VFS, scheduler, or userland source Direct kernel plus base-image rebuild Fast product validation, syscall work, and most application-hosting changes make build base-image build/virt.dtb, then ./tests/boot_test.sh or the focused service test Previous kernel.elf, base.img, virt.dtb, and serial log
UEFI loader, GPT image, or firmware handoff UEFI disk image rebuild Validating the boot disk story rather than direct -kernel boot make disk base-image, then UEFI_BOOT=disk ./tests/uefi_boot_test.sh Previous swift-os.img, matching base.img, and UEFI boot log
Signed base-image slot behavior SWOSBOOT A/B update store Proving target-side staging, activation, confirmation, and rollback of base images ./tests/ab_stage_test.sh, ./tests/ab_activate_test.sh, ./tests/ab_confirm_test.sh Store state, slot manifest, boot-attempt log, and ./tests/ab_rollback_test.sh when rollback is in scope
Signed kernel slot behavior UEFI ESP kernel A/B slots Proving loader-selected kernel slots, confirmation, and attempt rollback ./tests/uefi_kstage_test.sh, ./tests/uefi_kactivate_test.sh, ./tests/uefi_kconfirm_test.sh ESP slot files, kernel-state, loader log, and ./tests/uefi_krollback_test.sh when rollback is in scope
Read-only application payload Package payload image Shipping a small staged binary without baking it into /bin make package-fixture, then make package-overlay-test Previous payload image and guest command output
Package-store activation Package store image Validating signed package-store boot activation and dependency metadata make package-store-fixture, then make package-store-test Previous package-store image and package activation log
AI model, tokenizer, or bundle manifest Model bundle rebuild plus base-image repack Updating local inference assets or signed generations make model, make base-image, then ./tests/llm_run_test.sh and ./tests/llm_serve_test.sh Previous /models generation, manifest, and llmd verification log
Documentation only Documentation gate Updating guides, examples, or references without changing artifacts git diff --check, then make docs-test Commit hash and docs-test output

Example: a change to /bin/llmd source and the default model bundle touches both userland and model assets. Use the direct artifact path, rebuild the model assets, then prove the serving API:

make build model base-image build/virt.dtb
./tests/llm_run_test.sh
./tests/llm_serve_test.sh

Record the serving evidence with the public HTTP endpoints:

curl -fsS http://127.0.0.1:8080/health
curl -fsS -X POST --data "Once upon a time" http://127.0.0.1:8080/completion
curl -fsS http://127.0.0.1:8080/metrics

What Is Not Implemented Yet

The current tree has a narrow signed SWOSBASE A/B update-store path with swos-update, swos-activate, swos-confirm, boot-attempt rollback, and host tools for generating the store and kernel boot manifest. It is still a controlled validation path, not a production updater.

Current limitations:

  • No live in-place kernel or root filesystem replacement; updates stage an inactive slot and require reboot.
  • No persistent writable root filesystem.
  • No target-side package upgrade or rollback transaction.
  • No production image signing policy for the whole OS image.
  • Base-image rollback is implemented for the checked A/B update-store path.
  • Kernel-image staging, activation, health confirmation, boot-attempt counting, and rollback are implemented for the checked UEFI ESP slot path.

The current verified model-bundle flow is a narrow working example of signed manifest verification and generation fallback for AI assets. It is not a whole OS updater.

Stage Base Images In The A/B Update Store

Use this path when validating the checked SWOSBOOT base-image update flow. The store is a narrow, writable, two-slot virtio-blk disk; each slot holds a signed SWOSBASE image. The OS never trusts the store manifest as code authority: each slot image is still verified by its own Ed25519 signature and per-file hashes.

Build the store tooling and base image:

make updatestore base-image

For an end-to-end target-side update, attach a store disk and a read-only payload disk, then run the guest flow as root:

swos-update
swos-activate

After rebooting into the trial slot, confirm it healthy:

swos-confirm

Minimum verification:

./tests/ab_stage_test.sh
./tests/ab_activate_test.sh
./tests/ab_confirm_test.sh

Rollback is automatic for the checked base-image A/B path when a trial slot exhausts its boot attempts without confirmation. Verify that behavior with:

./tests/ab_rollback_test.sh

Use Update Store for the manifest format, slot state model, and trust boundary.

Stage Kernel Slots From The UEFI ESP

Use this path when validating the checked kernel-image A/B flow. The UEFI loader selects between signed ESP kernel slots using the signed kernel-boot manifest; the running OS can courier-copy already-signed artifacts but never signs new kernel manifests.

The current operator sequence is:

swos-kstage
swos-kactivate

Then reboot through the UEFI disk profile. On the next boot, the loader verifies the signed kernel manifest's slot hashes, reads the selected active slot from kernel-state, and checks the selected kernel image before handoff. The loader also persists a per-slot boot-attempt counter in kernel-state; an unconfirmed kernel slot rolls back after the checked attempt window. After a healthy trial boot, confirm that booted kernel slot:

swos-kconfirm

Minimum verification:

./tests/uefi_kernel_ab_test.sh
./tests/uefi_kstage_test.sh
./tests/uefi_kactivate_test.sh
./tests/uefi_kattempt_test.sh
./tests/uefi_kconfirm_test.sh
./tests/uefi_krollback_test.sh

Kernel-image A/B has end-to-end staging, boot-state activation, health confirmation, and attempt-based rollback for the checked ESP layout. A real new-kernel payload source is future work.

Release Identity

SwiftOS does not publish stable external version numbers yet. Identify a build by the git revision and the artifact set used to boot it.

Record the current revision:

git log -1 --oneline

Record the changed files for a candidate:

git diff --stat HEAD^ HEAD

For a support handoff, include:

  • The git commit or branch.
  • The exact boot profile: direct -kernel, UEFI disk, graphical smoke, or VirtualBox.
  • Whether build/base.img, build/swift-os.img, package images, or model files were rebuilt.
  • The verification commands run after the update.
  • The serial log from a successful or failed boot.

Preflight Checklist

Before replacing a working artifact set:

  1. Start from a clean worktree or record intentional local changes.
  2. Save the known-good artifacts if you need quick rollback.
  3. Rebuild only the artifacts affected by the change.
  4. Run the narrowest relevant test first.
  5. Run the broader gate before calling the update release-ready.

Check the worktree:

git status --short --branch

Save the current artifacts:

mkdir -p build/rollback
cp build/kernel.elf build/rollback/kernel.elf.prev
cp build/base.img build/rollback/base.img.prev
cp build/virt.dtb build/rollback/virt.dtb.prev
cp build/swift-os.img build/rollback/swift-os.img.prev

If one of those files does not exist yet, build it first or skip that copy.

Update Kernel Or Userland

Use this path when code under kernel/, userland/, base/, or shared build rules changes.

Build the kernel and base image:

make build base-image build/virt.dtb

Boot the direct serial profile:

make run

Minimum verification:

./tests/boot_test.sh

Use the direct profile for fast kernel, syscall, userland, VFS, account, and tmpfs iteration. It exercises the normal kernel and base image without the UEFI loader layer.

Update The UEFI Disk Image

Use this path when touching the loader, disk layout, firmware handoff, or the primary UEFI/GPT boot story.

Build the disk and base image:

make disk base-image

Boot the UEFI disk profile:

make disk-run

Minimum verification:

UEFI_BOOT=disk ./tests/uefi_boot_test.sh

For SMP-sensitive boot work, also run:

SMP_CPUS=4 UEFI_BOOT=disk ./tests/uefi_boot_test.sh

The UEFI disk image and base image are separate in the normal QEMU flow. If the loader starts but userland files are missing, verify that the updated build/base.img is also attached.

Update Base Image Contents

Use this path when changing files under base/, staged /bin programs, default web content, account seed files, or model files included in the base filesystem.

Repack the base image:

make base-image

Run a direct boot smoke:

./tests/boot_test.sh

For filesystem-only changes, also use the relevant command-level or VFS test when one exists. Examples:

./tests/vfs_disk_test.sh
./tests/console_login_test.sh
./tests/httpd_test.sh

Do not edit /etc, /bin, /www, or /models from inside the guest and expect those edits to persist. Those paths come from the read-only base image.

Update The Hosted Static Site (Reflash-Free)

Use this path to change the static site content that the box's nginx serves (e.g. swiftos.tech) on a running machine — no Rescue mode, no dd, no base-image reflash. This is content-only; it does not touch the kernel or base image (use the A/B paths above for those).

How it works: nginx's production docroot is /data/www/current on the persistent /data tier. /bin/swos-init runs /bin/swupdate seed at every boot to seed it from the baked default site (so a fresh box still serves something) and to recover any crash-interrupted update. A new site is published as a signed SWSITE bundle and applied with an atomic directory swap; the previous generation is kept in /data/www/prev for rollback.

Security model: the trigger is gated by the operator SSH key (the bounded-exec sshd allowlist permits /bin/*); the content is gated by an Ed25519 signature on the bundle, verified against the public key baked at /etc/swupdate/site-root.pub. There is no scp and no writable root. TLS supplies confidentiality only (the server certificate is not yet verified) — the signature is the authenticity anchor, so a man-in-the-middle serving a different bundle fails verification.

Build and sign a bundle on the host (see HOST_TOOL_REFERENCE.md):

make sitepack
build/sitepack create ./my-site build/my-site.swsite --seed models/dev-site-signing.seed

Publish build/my-site.swsite to an HTTPS URL, then apply it over the operator's SSH key:

ssh root@swiftos.tech /bin/swupdate site https://downloads.example/my-site.swsite

On success the next HTTP request returns the new content immediately (static files; no nginx reload needed). A tampered or unsigned bundle is refused and the live docroot is left untouched. Offline, a bundle already on the box can be applied with swupdate apply-local <path>.

Bundles are capped at 64 entries (files + dirs): datafs holds 256 inodes total and current+next+prev is ~3× the site.

Validation:

make site-seed-test     # boot seed + crash-recovery of an interrupted swap
make site-bundle-test   # signed apply, tamper rejection, reboot persistence
make site-update-test   # full operator path: swupdate site <url> over SSH

QEMU does not exercise every hardware path, so also validate swupdate site on the real target after a content change.

Update Package Payloads

Use this path when validating package content without baking it into /bin.

Build the sample package fixture:

make package-fixture

Run the payload-overlay acceptance test:

make package-overlay-test

Or boot manually with the package payload image attached, then run:

/usr/bin/pkghello

Expected output:

pkghello: hello from package overlay

Current package payload images are read-only and attached at boot. They are not a production online update mechanism.

Update Package Store Images

Use this path when validating package-store boot activation.

Build the package store fixture:

make package-store-fixture

Run the package-store acceptance test:

make package-store-test

Rollback is host-driven today: attach the previous package-store image on the next boot. Target-side active-generation updates, garbage collection, and rollback commands are roadmap work.

Update AI Model Bundles

Use this path when model source files, tokenizer files, serving manifests, or verified bundle generations change.

Rebuild model artifacts and the base image:

make model
make base-image

Run the local inference smoke:

./tests/llm_run_test.sh

Run the serving smoke:

./tests/llm_serve_test.sh

The default serving image intentionally includes a bad newest generation and a valid older generation. A healthy /bin/llmd boot should reject the bad generation and serve from the newest verified generation.

Expected serial evidence includes:

llmd: generation 2 rejected (model size/sha256 mismatch)
llmd: bundle stories15M generation 1 verified (ed25519+sha256)
llmd: serving on 8080

For the full AI runbook, see AI Hosting Guide.

Roll Back Quickly

There are two practical rollback paths today.

Roll Back To Previous Artifacts

If you saved artifacts under build/rollback, boot the previous kernel, DTB, base image, or disk image by copying them back or by pointing QEMU directly at the saved files.

For a direct boot rollback:

cp build/rollback/kernel.elf.prev build/kernel.elf
cp build/rollback/base.img.prev build/base.img
cp build/rollback/virt.dtb.prev build/virt.dtb
make run

For a UEFI disk rollback:

cp build/rollback/swift-os.img.prev build/swift-os.img
cp build/rollback/base.img.prev build/base.img
make disk-run

After rollback, run the same smoke that proved the original artifact set:

./tests/boot_test.sh

Roll Back To A Previous Commit

Use normal git history to rebuild an older artifact set. Prefer a separate worktree for release comparison so the current worktree stays intact.

Example:

git worktree add ../swift-os-rollback <known-good-commit>
cd ../swift-os-rollback
make build base-image build/virt.dtb
./tests/boot_test.sh

Do not use destructive git commands in a shared worktree unless everyone using that worktree has agreed.

Validation Matrix

Run the narrowest proof that matches the update, then broaden if the change touches shared code.

Updated area Minimum proof Broader proof
Kernel core, syscall, process, VFS ./tests/boot_test.sh make test
Base image contents make base-image, ./tests/boot_test.sh Relevant command or service test
UEFI loader or disk image UEFI_BOOT=disk ./tests/uefi_boot_test.sh SMP UEFI smoke plus make test
Framebuffer or input ./tests/fb_vi_test.sh UEFI disk smoke
Networking ./tests/httpd_test.sh, ./tests/tcp_echo_test.sh, or ./tests/udp_echo_test.sh Full networking smoke set
Package payload make package-overlay-test Package store test
Package store make package-store-test Overlay test plus boot smoke
Base-image A/B update store ./tests/ab_stage_test.sh, ./tests/ab_activate_test.sh, ./tests/ab_confirm_test.sh ./tests/ab_rollback_test.sh, ./tests/ab_flush_test.sh
Kernel-image A/B ESP slots ./tests/uefi_kernel_ab_test.sh, ./tests/uefi_kstage_test.sh, ./tests/uefi_kactivate_test.sh, ./tests/uefi_kconfirm_test.sh ./tests/uefi_kattempt_test.sh, ./tests/uefi_krollback_test.sh, UEFI_BOOT=disk ./tests/uefi_boot_test.sh, make test
AI model bundle ./tests/llm_run_test.sh ./tests/llm_serve_test.sh
Documentation only make docs-test, git diff --check make build, ./tests/boot_test.sh

Common networking proofs:

./tests/httpd_test.sh
./tests/tcp_echo_test.sh
./tests/udp_echo_test.sh
./tests/dns_test.sh
./tests/tls_test.sh
./tests/ipv6_smoke_test.sh

Release Candidate Checklist

Before handing a candidate to another operator:

  1. Record git log -1 --oneline.
  2. Build the intended boot artifacts.
  3. Boot the intended profile from a clean terminal.
  4. Log in with the expected account.
  5. Run one command that proves filesystem access, such as cat /etc/motd.
  6. Run one process or observability command, such as ps or top -b -n 1.
  7. If networking is in scope, run the service or socket test that matches it.
  8. If packages are in scope, boot with the intended package image attached.
  9. If AI serving is in scope, prove /health, /metrics, and one /completion request.
  10. Save the serial log and any host-side command output.

Troubleshooting Updates

Symptom Likely cause Fix
New command is missing build/base.img was not rebuilt or not attached Run make base-image and boot with the updated image
UEFI boot starts old code build/swift-os.img was not rebuilt Run make disk
Direct boot starts old code build/kernel.elf was not rebuilt Run make build
Package binary disappears after reboot Package image was not attached again Reattach payload or package-store image
/tmp contents disappear Expected tmpfs behavior Store only runtime scratch in /tmp
llmd rejects newest model generation Manifest or payload mismatch Check model size, SHA-256, and generation layout
Network service cannot bind Missing virtio-net profile, missing capNet, or port conflict Boot with the networking QEMU command and run one service per port

For deeper diagnosis, see Troubleshooting.

Future Direction

The intended production direction is signed immutable image updates with A/B slots and automatic rollback after failed health checks. That model fits the current two-tier filesystem and deterministic boot design, but it is not the current implementation.

Until that work lands, treat every SwiftOS update as an explicit artifact promotion:

  1. Build immutable artifacts on the host.
  2. Boot the candidate.
  3. Verify health with tests and serial evidence.
  4. Keep the previous artifact set available.
  5. Promote only after the candidate passes the relevant gate.

Edit this page on GitHub