Skip to content
AtlasOSFramework API
Browse the docs

bootc

Typed views of bootc status --json for AtlasOS (booted, staged and rollback images, the update channel and available updates) and the channel tag rewrite.

The bootc module has types for the output of bootc status --json (bootc 1.16, API org.containers.bootc/v1) and the helpers that decide what is booted, staged, available and which channel the system follows.

Every field is lenient: unknown fields are ignored and optional ones default, so a newer bootc does not break the parser. The module does not run bootc; the caller (the root helper) does and passes the JSON in.

Example

use atlas_framework_system::bootc::{Channel, Status};

let json = std::fs::read_to_string("status.json")?; // the output of `bootc status --json`
let status = Status::from_json(&json)?;
if let Some(booted) = status.status.booted.as_ref() {
    println!("booted {}", booted.version().unwrap_or("unknown"));
}
if status.update_available() {
    println!("an update is available");
}
let next = status.booted_ref().map(|r| r.with_channel("testing"));
assert_eq!("stable".parse::<Channel>().unwrap(), Channel::Stable);

Status

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct Status

Field Type Description
api_version String apiVersion
kind String
spec Spec The desired state
status HostStatus The observed state
image_ref_heads Vec<String> Not in bootc's JSON (serde(skip)): the caller fills it from image_ref_heads. Without it latest_image falls back to the booted entry
bad_image_digests Vec<String> Not in bootc's JSON: the caller fills it from bad_image_digests
Method Description
fn from_json(json: &str) -> Result<Status, serde_json::Error> Parses the document
fn booted_ref(&self) -> Option<&ImageReference> The booted image's reference; falls back to spec.image
fn channel(&self) -> Option<Channel> The channel the system follows, from spec.image (a switch changes it at once, while the booted ref keeps the old channel until the restart)
fn has_staged(&self) -> bool A staged deployment exists
fn latest_image(&self) -> Option<&ImageStatus> The newest image of the tracked reference this system knows of: the cachedUpdate of the entry holding the image ref's commit, or that entry's own image when it has none. Without ref heads, or when no entry holds the commit, the booted entry's cachedUpdate
fn available_update(&self) -> Option<&ImageStatus> latest_image when it is neither the booted nor the staged image: an update found by upgrade --check that is not staged yet. An image older than the booted or staged one, or one with no digest, is not an update
fn update_available(&self) -> bool available_update() has one
fn is_downgrade(&self, candidate: &ImageStatus) -> bool candidate is older than the booted or staged image of the same reference (a tag moved back to an older build). Such an image is never offered or staged; Go Back and a channel switch are the ways to an older build
fn is_bad_image(&self, digest: &str) -> bool digest failed its boot health checks on this machine

Deployment types

All derive Debug, Clone, PartialEq, Serialize, Deserialize, Default with camelCase JSON names.

Type Fields
Spec image: Option<ImageReference> (the image the host tracks), boot_order: Option<String>
HostStatus staged, booted, rollback: Option<BootEntry>; rollback_queued: bool; host_type: Option<String> (JSON type: bootcHost on a bootc system, null on a plain container)
BootEntry image: Option<ImageStatus> (null for a deployment that is not a container image), cached_update: Option<ImageStatus> (an update bootc found with upgrade --check but has not downloaded), incompatible: bool, pinned: bool, store: Option<String>, ostree: Option<OstreeEntry>
OstreeEntry checksum: String, deploy_serial: u32, stateroot: String
ImageStatus image: ImageReference, architecture: Option<String>, version: Option<String>, timestamp: Option<String> (build time, RFC 3339), image_digest: String
ImageReference image: String (name with tag, such as ghcr.io/eternalcoder454/atlasos:stable), transport: Option<String> (registry, oci, containers-storage, ...; missing means registry), signature: Option<serde_json::Value>

Methods on these:

Method Description
BootEntry::version(&self) -> Option<&str> The image version label (org.opencontainers.image.version)
BootEntry::timestamp(&self) -> Option<&str> The image build time, RFC 3339
BootEntry::digest(&self) -> Option<&str> The image digest
ImageStatus::is_older_than(&self, other: &ImageStatus) -> bool Built before other: the version labels (when both are numeric, such as 44.20261008-2) and the build times (RFC 3339 UTC to the second) are compared, and one must say so with neither saying the opposite. Images that cannot be compared are not held back
ImageReference::same_image(&self, other: &ImageReference) -> bool Same image and transport (the signature policy is not compared)
ImageReference::transport_or_default(&self) -> &str The transport, registry when not given
ImageReference::tag(&self) -> Option<&str> The tag of image. A port in the host (host:5000/img) and a @digest are not tags
ImageReference::channel(&self) -> Option<Channel> The channel in the tag, if it is stable or testing
ImageReference::with_channel(&self, channel: &str) -> Result<ImageReference, RefError> The same reference with only the tag replaced by channel. Transport and name stay; a digest is dropped, since it would pin the old image. Errors with RefError for a bad channel, a transport outside TRANSPORTS, or an unusable name

Channel and errors

Item Description
enum Channel { Stable, Testing } An update channel. Only these two exist. Copy. as_str() gives stable or testing; implements Display and FromStr (exactly stable or testing, else RefError::BadChannel)
enum RefError { BadChannel(String), BadImage(String), BadTransport(String) } Why a reference or channel was refused. Implements Display and std::error::Error

Functions and constants

Name Signature or value Description
IMAGE_REFS_DIR "/ostree/repo/refs/heads/ostree/container/image" Where ostree-ext keeps one ref per container image reference. Readable without root
image_ref_heads pub fn image_ref_heads(dir: &Path) -> Vec<String> The commit checksums of the image refs under dir. Empty when they cannot be read
BAD_IMAGE_DIGESTS "/var/lib/atlasos/bad-image-digests" Digests of images that failed their boot health checks and were rolled back by greenboot, one per line, newest last (at most 20). Readable without root
bad_image_digests pub fn bad_image_digests(path: &Path) -> Vec<String> The digests in such a file. Empty when it cannot be read
TRANSPORTS &[&str] Transports a switch may use: registry, oci, oci-archive, containers-storage, docker-daemon
CONTAINERS_POLICY "/etc/containers/policy.json" The system's containers policy
policy_requires_signature pub fn policy_requires_signature(policy: &serde_json::Value, image: &str) -> bool True when the parsed containers policy demands a signature (signedBy or sigstoreSigned) for the registry image image under the most specific matching scope, and its default does not accept anything
version_cmp pub fn version_cmp(a: &str, b: &str) -> Option<std::cmp::Ordering> Compares numeric versions such as 44.20261008 or 44.20261008-2; None if either has a non-numeric part
utc_second pub fn utc_second(t: &str) -> Option<&str> An RFC 3339 time in UTC to the second (2026-10-02T18:54:39), which orders as text; None for any other form