Firmware Data Flow
AtomixOS keeps immutable firmware, provisioned runtime state, and update state in separate paths so A/B slot switches do not rewrite operator data.
Boot Flow
- U-Boot RAUC bootmeth selects the slot using
BOOT_ORDERandBOOT_x_LEFTfrom the SPI environment. boot.scrloads kernel, initrd, and DTB from the selected boot partition.boot.scrpassesroot=fstab,rauc.slot, andatomixos.lowerdevto Linux.- Initrd mounts the selected squashfs rootfs as
/run/rootfs-base. sysroot.mountassembles/as OverlayFS with squashfs lowerdir and tmpfs upper/work dirs.- Initrd
systemd-repartcreates missingboot-b,rootfs-b, and/datapartitions on a fresh flash.
Provisioning Flow
Provisioning imports exactly one operator configuration into /data/config/ from /boot/config.toml on fresh flash, a
USB seed, a supported seed bundle, or the LAN bootstrap console.
Persisted outputs are:
| Output | Path |
|---|---|
| Imported source config | /data/config/config.toml |
| Managed users | /data/config/users.json |
| User SSH keys | /data/config/ssh-authorized-keys/<user> |
| WAN inbound policy | /data/config/firewall-inbound.json |
| LAN runtime settings | /data/config/lan-settings.json |
| OS upgrade settings | /data/config/os-upgrade.json |
| Required health units | /data/config/health-required.json |
| Rendered Quadlets | /data/config/quadlet/*.container |
| Quadlet runtime metadata | /data/config/quadlet-runtime.json |
| Managed user tracking | /data/config/managed-users.json |
| Bundle payload files | /data/config/files/ |
first-boot.service fails before RAUC slot confirmation if Quadlet sync, LAN runtime apply, or provisioned firewall apply
fails.
Re-Apply Flow
Mutating bootstrap POST paths on an already-provisioned device require SSH signature authentication. The operator
requests a nonce via GET /api/nonce, then signs a request-bound message containing the nonce, target path, and
SHA-256 digest of the submitted config payload (ssh-keygen -Y sign -n atomixos-reapply). The request includes the
nonce and base64 signature in the X-AtomixOS-Nonce and X-AtomixOS-Signature headers. Nonces are single-use and
expire after 5 minutes (configurable via ATOMIXOS_NONCE_TTL).
Re-apply uses atomic candidate promotion:
- Validate and render candidate config in
/data/config-candidate/. - Rename active
/data/configto/data/config-rollback. - Rename candidate to
/data/config. - Run activation services synchronously (user apply, Quadlet sync, LAN apply, firewall).
- On success, clean up
/data/config-rollback. - On failure, restore
/data/config-rollbackto/data/configand re-activate.
POST /api/config is asynchronous for programmatic clients. It returns a typed response with job_id, state, and
job_url; the Location header points to the same job resource. The job records provisioning steps, service
deployment/status events, activation failures, final result, and rollback status.
First provisioning (no existing config.toml) remains unauthenticated and writes directly.
Managed Users Flow
atomixos-apply-users.service materializes managed users from /data/config/users.json on every boot and after
re-apply. It runs before sshd.service so accounts exist before SSH accepts connections. Admin users are added to the
wheel group. Users removed from the config are locked (expiredate=1, shell=/sbin/nologin). Protected image users
(root, appsvc) are never created or locked by this service.
Update Flow
os-upgrade.service reads /data/config/os-upgrade.json and skips polling cleanly when no provisioned update server is
configured. When polling is configured, it sends
the compact lowercase 12-hex eth0 MAC in X-Device-ID, compares available bundle metadata with the booted version,
downloads the bundle to /data, installs it with RAUC, and reboots into the newly selected slot.
os-verification.service commits a slot only after service, network, LAN, and required-unit checks remain healthy through
the sustained verification window.
Firewall and LAN Apply Flow
lan-gateway-apply.service consumes /data/config/lan-settings.json, writes the eth1 network drop-in, updates dnsmasq
and chrony runtime snippets, and restarts the affected services. provisioned-firewall-inbound.service consumes
/data/config/firewall-inbound.json and applies the requested WAN and LAN nftables rules for the configured scopes.
WAN remains deny-by-default unless explicitly opened. LAN is open by default, but an explicit lan scope replaces that
default-open rule with an allowlist of the configured ports merged with platform-required LAN ports.
Application Runtime Flow
Provisioned Quadlets are rendered under /data/config/quadlet/, mirrored into the active rootful or rootless systemd
Quadlet search path, and described by /data/config/quadlet-runtime.json. Rootless containers are constrained to pasta
networking with loopback publish rewrites; privileged rootful containers use host networking.