lerux is a static Microkit system: protection domains, memory maps, and channels are fixed at image build time. Isolation is therefore a property of the composed SDF + typed IPC contracts, not of a dynamic process model.
This document is the Phase 60 threat model and trust map. It does not claim formal verification of lerux PDs (kernel seL4 is verified; userspace is not).
| Asset | Why it matters |
|---|---|
| Block device / on-disk FS | Integrity of configs, logs, app data |
| Network stack state | Availability and privacy of sockets |
Config keys under /config/ (incl. secret.*) |
Policy and credentials (path isolation today; no encryption at rest) |
| Service readiness | Shell and apps need FS/net/supervisor alive |
| Image integrity | What boots is what CI built (signing is host-side stretch) |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Platform (highest privilege for devices) β
β serial-driver, virtio-*-driver, genet, emmc2, RTC/timer PDs β
β Maps: MMIO, IRQs, DMA rings β
ββββββββββββββββββββββββββββββ²βββββββββββββββββββββββββββββββββββββ
β rings / device RPC (not app-facing)
ββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββ
β Trusted services β
β fatfs, serial virtualisers, network virtualisers, copier β
β Maps: shared queues; no app DMA share β
ββββββββββββββββββββββββββββββ²βββββββββββββββββββββββββββββββββββββ
β LionsOS queues (`fs_cmd_t`, rings)
ββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββ
β Untrusted / interactive apps β
β shell, sddf-agent, sddf-program, crash-demo β
β Maps: **no** virtio/net/blk/display DMA; channels only β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Rule (ADR-003): untrusted apps never map NIC or block DMA. On the device-framework images they speak LionsOS queues. The remaining virtio boards keep the driver DMA off the app.
Rule (ADR-009): untrusted apps never map display MMIO. The live browser shell paints through the graphics client queues. The desk-browser spec names request-server and display-server again; those protection domains are not in this tree.
| PD | Trust class | MMIO / IRQ | DMA | Clients may call | Must not map |
|---|---|---|---|---|---|
virtio-*-driver / genet / emmc2 |
platform | yes | driver (+ bounce per ADR-003) | service only | β |
serial-driver |
platform | UART | no | serial-virt (device-only mode) |
app channels |
serial-virt |
service | no | no | apps (serial RPC) | UART MMIO |
fs-server |
service | no | blk client DMA | shell, edit, backup, http-fs, config | NIC DMA |
net_virt_* / net_copy |
service | no | shared data regions | net_client / shell |
device MMIO |
lerux-tls (library) |
in the client | no | no | shell / agent fetch | NIC / blk DMA |
config-server / log-server |
service | no | no | shell, supervisor | device DMA |
supervisor |
control | no | no | shell (status/reboot/time) | device DMA |
| shell / apps | untrusted | no | none | each other only via typed RPC | any DMA / MMIO |
program (sddf-program) |
untrusted | no | none | filesystem and network queues | any DMA / MMIO (Milestone 14, ADR-012) |
debug-handler |
debug-only | no | no | hierarchy parent | production workstation default |
Channel numbers come from profile [[channel]] manifests; PPC callees outrank callers (qos.md, ADR-006).
Those boards are a phase record. Milestone 17 deleted display-server, request-server, browser-ui, web-content, and the postcard agent. The live browser image is qemu_virt_aarch64_browser_sddf: the shell owns smoltcp and lerux-tls, and paints through the graphics client queues. The desk-browser spec names the Ladybird-shaped set again.
The table below is the phase-72β80 trust map. Those protection domains are not in this tree.
| PD | Trust class | MMIO / IRQ | DMA | Clients may call | Must not map |
|---|---|---|---|---|---|
display-server |
service | fw_cfg + ramfb backing | no app DMA | display-demo, paint-demo, browser-ui |
NIC / blk DMA |
request-server |
service | no | no | web-content, agent, program-runtime |
NIC / blk / display MMIO (uses tls-proxy / net-server) |
browser-ui |
untrusted | no | none | web-content, display-server |
any DMA / MMIO |
web-content |
untrusted | no | bitmap MR only | request-server |
NIC / blk / display MMIO, FS |
agent |
untrusted | no | none | request-server, fs-server, web-content (Browse), shell grok |
NIC / display MMIO |
The live residual is that the browser shell owns smoltcp. A later smoke should crash the shell's worker and still complete a filesystem round-trip from another protection domain.
| Threat | Status | Mitigation |
|---|---|---|
| Untrusted app corrupts FS DMA / disk | Mitigated | Apps use fs_cmd_t only; only fatfs and blk_driver map the data region |
| Untrusted app sniffs NIC DMA | Mitigated (QEMU virtio-net all arches) | Unified-dma; apps have no DMA map (ADR-003, Phase 61) |
| App crash takes down services | Partial | Separate PDs; just test-debug crashes a child. Milestone 17 deleted the isolation board |
| Shell over-privileged surface | Partial | Profile tiers (admin / admin-core / appliance); lerux profile audit; shell still holds many ends on admin images |
| Secrets on disk writable by shell | Mitigated (ACL) | secret.* Set/Delete is supervisor-only (ConfigResponse::Denied for shell) |
| Secrets on disk readable by any FS client | Partial | Path prefix /config/secrets/; no encryption; FS RPC clients can still open paths |
| Compromised network client | Accepted residual | The shell owns smoltcp; the driver and virtualisers stay trusted |
Malicious loader.img |
Partial (host digest) | SHA-256 sidecar at image build; lerux deploy verifies by default; asymmetric / measured boot still open |
| Channel/QoS abuse (starve shell) | Mitigated (checks) | Fixed priorities + single-flight; lerux profile check-qos + workstation concurrent-boot smoke; MCS deferred |
| Supply-chain pin drift | Mitigated (process) | Pins in deps/versions.toml / Cargo.toml / package pins; runbook for bumps and CVE response |
just test-debug still nests crash-demo under debug-handler and logs the fault. Milestone 17 deleted qemu_virt_aarch64_isolation, so there is no follow-on filesystem round-trip after that crash.
Production images stay flat (no debug parent) per ADR-005. Isolation is a property of the protection-domain layout.
trust_class |
Profiles | Surface |
|---|---|---|
| admin | workstation, workstation-riscv, workstation-x86, workstation-rpi4 |
Shell + FS/net/config/supervisor + bulk apps (edit/chat/http-fs/backup) |
| admin-core | dev-workstation |
Shell + services only; install apps via lerux package |
| appliance | net-appliance, server |
Fixed PD set; no interactive admin shell |
| minimal | minimal, hardware-rpi4 |
Serial hello / bring-up |
| debug | isolation/debug boards (inferred) | Fault parent + crash child β not production default |
Declare with trust_class = "admin" (etc.) in support/profiles/*.toml. Host tooling:
lerux profile list # shows [trust_class] column
lerux profile audit # all profiles
lerux profile audit workstation # one profile: PD domains + high-risk edgesThe shell holds many RPC ends by design (REPL admin console). High-risk edges flagged by profile audit:
- shell β supervisor β reboot / status
- shell β config-server β policy R/W (see secrets ACL)
- shell β fs-server / net-server β full storage and network RPC
- shell β edit / chat / backup β app launch
Shrink the surface by choosing a lower tier profile, not by runtime capability dropping (Microkit is static). Prefer dev-workstation or net-appliance when bulk apps or a full REPL are unnecessary.
config-server allows Get/List of secrets from shell (operator inspection) but Set/Delete on secret.* is supervisor-only. Non-supervisor writers receive ConfigResponse::Denied. Ordinary keys (hostname, net.*, log.*) remain writable from shell.
Still open: encryption at rest; path-level FS ACL for apps that speak FsRequest directly (any FS client can open /config/secrets/ if it has FS rights β prefer config IPC).
Host-side SHA-256 digests for loader.img (not asymmetric signing, not measured boot):
| Step | Command / behavior |
|---|---|
| Write | Automatic after lerux image β build/<board>/loader.img.sha256 (sha256sum format) |
| Write (manual) | lerux digest --board <board> or lerux digest -p path/to/loader.img |
| Verify | lerux verify-image --board <board> |
| Deploy | lerux deploy β¦ verifies the sidecar by default; copies .sha256 to media; skip with --no-verify |
BOARD=rpi4b_4gb_workstation just image # writes loader.img + loader.img.sha256
lerux verify-image --board rpi4b_4gb_workstation
lerux deploy --board rpi4b_4gb_workstation --dest /media/$USER/boot
# sha256sum -c build/rpi4b_4gb_workstation/loader.img.sha256 # host cross-checkTrust model: the sidecar is only as strong as the host that wrote it (CI or developer machine). It catches accidental corruption and casual SD-card swap of loader.img without the matching digest. It does not replace secure boot, signed releases, or a hardware root of trust.
Phase 67: optional ed25519 signatures next to the digest (loader.img.sig). Smoke key: support/keys/smoke.ed25519 (not a production secret).
lerux keygen --out support/keys/smoke.ed25519
lerux sign --board qemu_virt_aarch64
lerux verify-image --board qemu_virt_aarch64 --key support/keys/smoke.ed25519.pub --require-sigMeasured boot / TPM / in-guest verification remain out of scope.
| Check | How |
|---|---|
| PPC priority rule | lerux profile check-qos (host; also in just check) |
| Service-class bands | Same command on admin/admin-core profiles (qos.md) |
| Concurrent boot under bulk apps | just test-workstation expects lerux-shell: qos ok after services/apps init |
MCS budgets and raising shell above bulk apps remain deferred (ADR-006). See qos.md.
- Reduce shell channel set further β launch apps without giving shell every service end (may need non-PPC notify; see ADR-006)
- Asymmetric image signing / measured boot β ed25519 is Phase 67; measured boot stays hardware-gated
lerux does not vendor seL4 / Microkit / rust-sel4 trees into git. Security and feature bumps are pin updates followed by rebuild and smoke. This section is the operational runbook.
| Component | Pin location | Current (see also plan version table) | When to bump |
|---|---|---|---|
| seL4 | deps/versions.toml β [repos].sel4.tag |
16.0.0 | Kernel CVE / release notes from seL4 Foundation |
| Microkit | deps/versions.toml β [repos].microkit.tag |
2.3.0 | Microkit release; often paired with seL4 |
| rust-sel4 (docs mirror) | deps/versions.toml β [rust].rust_sel4.tag |
v5.0.0 | Keep in sync with Cargo.toml |
| rust-sel4 (build) | root Cargo.toml workspace git tag = "β¦" for all sel4* deps |
v5.0.0 | rust-sel4 release / API fixes; must match versions.toml |
| Rust toolchain | rust-toolchain.toml |
nightly-2026-04-04 | rustc regressions, or when rust-sel4 requires a newer nightly |
| PD package ELFs | support/package-pins.toml |
per-board sha256 | After any PD/interface rebuild that ships CI artifacts |
| Host crates (clap, etc.) | Cargo.lock |
resolved | Dependabot / cargo update for host crates only; PD path is git-pinned |
Invariant: deps/versions.toml [rust].rust_sel4.tag and every sel4* git dep tag in root Cargo.toml must be the same string. lerux fetch clones seL4/Microkit from versions.toml; Cargo resolves rust-sel4 from Cargo.toml.
| Signal | Action |
|---|---|
| seL4 Foundation security advisory / kernel CVE in pinned tag | P0 β bump seL4 (+ Microkit if required), rebuild SDK, full smoke, redeploy images |
| Microkit bug that breaks isolation or loader integrity | P0 β bump Microkit, rebuild SDK, full smoke |
| rust-sel4 fix for driver/IPC safety used by lerux PDs | P1 β bump tag in Cargo.toml + versions.toml, cargo update -p sel4β¦, check-pd + smoke subset |
| Host-only crate CVE (clap, serde on host tools) | P2 β cargo update host path; just check; no SDK rebuild |
| PD package pin drift (sha256 mismatch in CI) | P2 β lerux package upgrade --all after green build; commit package-pins.toml |
| Nightly rustc break on pinned channel | P1 β move rust-toolchain.toml only after just check + just check-pd |
Document the CVE / advisory ID in the commit message and, if user-facing, a short note in docs/plan.md version table when seL4/Microkit major pins move.
Work on a branch. Prefer one logical bump per PR (e.g. βseL4 16.0.0 β 16.0.1β or βrust-sel4 v5.0.0 β v5.0.1β).
# seL4 and/or Microkit
$EDITOR deps/versions.toml
# rust-sel4: edit root Cargo.toml (all sel4* git tags) AND deps/versions.toml [rust].rust_sel4
# Then refresh the lockfile for git deps:
cargo update -p sel4
# (or cargo update for a broader refresh; review Cargo.lock diff)# Drop stale checkouts when tags change (fetch re-clones/updates deps/workspace/)
rm -rf deps/workspace/seL4 deps/workspace/microkit # only if tags moved
just fetch
# Rebuild SDK for boards you care about (CI builds a matrix; local minimum:)
MICROKIT_BOARDS=qemu_virt_aarch64,x86_64_generic,qemu_virt_riscv64,rpi4b_4gb just build-sdk
# Or prebuilt fallback when the pin matches a published SDK:
# just fetch-sdkIf only rust-sel4 moved (kernel/Microkit tags unchanged), SDK rebuild is usually not required; still run just check-pd so PD crates recompile against new bindings.
just check # host fmt + clippy (lerux-cli, interface-types)
just check-pd # cross-arch PD clippy (needs SDK)
just test-isolation # Phase 60 isolation smoke (fast security-relevant)
just test-workstation
# Prefer full matrix before merge when seL4/Microkit moved:
# just test-allCI also runs check β sdk β check-pd + smoke matrix (ci.md).
lerux package build edit --board qemu_virt_aarch64_workstation
lerux package upgrade --all --board qemu_virt_aarch64_workstation
# review support/package-pins.toml; commit with the bump- Commit pin files +
Cargo.lock+ any necessary PD/API fixups. - Push and wait for green CI (smoke matrix is the acceptance bar for kernel/Microkit bumps).
- Rebuild and redeploy field images (
just image/lerux deploy) for boards in use; oldloader.imgfiles are not hot-patched. - Optional lab: RPi4
just test-hwafter deploy (boards.md).
Copy into the PR or issue body:
## Security pin update
- **Advisory / CVE / issue:** β¦
- **Component:** seL4 | Microkit | rust-sel4 | host crate | package pin
- **Old pin β new pin:** β¦
- **Affects deployed images?** yes/no (if yes: boards β¦)
- **SDK rebuild required?** yes/no
- **Verification:**
- [ ] `just check`
- [ ] `just check-pd`
- [ ] smoke: isolation / workstation / test-all (link CI)
- [ ] package-pins updated (if applicable)
- [ ] field images rebuilt / operators notified
- **Residual risk:** β¦- Do not commit trees under
deps/workspace/ordeps/microkit-sdk/(gitignored). - Do not bump seL4 without checking Microkit compatibility notes for that release.
- Do not leave
versions.tomlrust-sel4 tag different fromCargo.toml. - Do not treat
lerux packagepins as a substitute for rebuilding from source in CI β they record artifact digests, they do not load arbitrary remote ELFs at runtime.
- Smoke matrix and caches:
ci.md - RPi4 deploy after image rebuild:
boards.md - Package pin CLI:
packages.md
- ADR-003 β net trust map
- ADR-005 β fault parent (not production default)
- ADR-006 β priorities / PPC
- debug.md β
test-debugand GDB - plan-arch.md β Phase 60 checklist
- net-topology.md β NIC channel map
- ci.md β CI gates after pin bumps