Skip to content

Latest commit

 

History

History
253 lines (224 loc) · 75 KB

File metadata and controls

253 lines (224 loc) · 75 KB

CambiOS Implementation Status

Living doc. Single source of truth for "what is currently built." Auto-refreshed as feature work lands (see CLAUDE.md § Post-Change Review Protocol Step 8). Intent lives in CambiOS.md and the design docs; this file is for current state.

"Is X done yet?" — read here. "Should X be done a certain way?" — read the linked design doc.

At a glance

  • Tri-arch first-class: clean release build on x86_64, aarch64, riscv64gc; all three boot in QEMU to an cambios> shell prompt. make check-all is the permanent regression gate.
  • 561 host unit tests passing on x86_64-apple-darwin. Run make stats for current counts — numbers live in code, not prose.
  • Security model live end-to-end: cryptographic identity, signed-ELF verification, capability-gated IPC, content-addressed ObjectStore, audit ring, kernel identity gate, userspace recv_verified.
  • GUI stack live on x86_64 + aarch64: scanout-virtio-gpu drives QEMU virtio-gpu-pci; compositor composites; virtio-input forwards HID keyboard/pointer events into the focused window; first-party app pong (continuous-motion 1-player vs AI) runs as the default GUI boot module on x86_64, and worm renders on aarch64 via make run-aarch64-gui now that the kernel has an ECAM-based PCI enumerator. tree (Minesweeper) stays buildable for regression.
  • Persistent storage live: virtio-blk + disk-backed ObjectStore + arcobj shell CLI; objects survive reboot.
  • Bare metal: USB boot tooling complete, untested on hardware.
  • Formal verification: Kani proofs live on BuddyAllocator + ELF parser + FrameAllocator + CapabilityManager + UserSlice + DTB parser (1 + 7 + 10 + 12 + 12 + 5 = 47 harnesses across 6 proof crates; proof authoring fixed 6 overflow sites in src/loader/elf.rs, 2 in src/memory/frame_allocator.rs, and 2 in src/boot/riscv.rs). verification/CLAIMS.md tracks the gap between proven and aspirational claims.

Recent landings

Chronological, newest first. ~3 week window — older items rotate out; git log has the full history.

  • 2026-05-25 — USB host stack Stream B substage B-vii: CCID class driver split into user/ccid/ boot module on endpoint 33, with a new cambios-ccid-proto wire-format crate at the workspace root following the cambios-fde-proto precedent. user/usb-host no longer knows anything about CCID semantics — it exposes a BULK_OUT / BULK_IN IPC ABI on endpoint 31 ([opcode:1][slot_id:1][...payload] request, [status:1][...result] reply; STATUS_OK/INVALID_OPCODE/TRUNCATED/PAYLOAD_TOO_BIG/DEVICE_NOT_READY/XHCI_ERROR codes; MAX_BULK_PAYLOAD=192 inline-data cap). The retired inline run_ccid_get_slot_status_smoke test in usb-host's main is replaced by the real consumer in user/ccid. cambios-ccid-proto carries encode_get_slot_status + decode_slot_status + IccPresence/CommandStatus enums + message_type constants for the broader CCID 1.1 § 6.1+§6.2 message families that future substages will fill in; 7 host tests cover canonical round-trips, the actual QEMU-observed RDR_to_PC_SlotStatus byte pattern, and rejection of truncated/mistyped buffers. End state under make run-quiet-dev-piv: usb-host completes enumeration through Configure Endpoint, registers ep 31, becomes ready; ccid registers ep 33, issues PC_to_RDR_GetSlotStatus via usb-host's IPC, and logs the parsed response — bSlot=1, bSeq=0, bStatus=0x02, ICC presence = absent. MAX_BOOT_MODULES + MAX_MODULES bumped 16 → 32 to accommodate the new boot module (current load is 19; the 16 cap was silently dropping shell + audit-tail + worm before B-vii, found via [boot::limine] WARNING: dropped 3 boot modules diagnostic). New code uses durable anchors only — no B-vii prefixes in comments, doc-comments describe what the code does. B-vii is the last QEMU-only substage: B-viii (PIV applet APDUs over CCID) needs a real smart card or a chardev-backed CCID emulation.
  • 2026-05-25 — USB host stack Stream B substage B-vi.c: first bulk transfer end-to-end on the configured CCID endpoints. New BulkDir enum + XhciController::bulk_transfer(dir, paddr, len) -> Result<u32, _> issues a single Normal TRB (xHCI 1.2 § 6.4.1.1, type 1) on the chosen direction's transfer ring, rings the per-DCI doorbell, polls for the matching Transfer Event, returns actual bytes moved (= requested - residual). wait_for_transfer_event extended to accept both SUCCESS and SHORT_PACKET (CCID 1.1 § 6.2.2 bulk IN responses are typically short — the device returns only the bytes it has, not the full requested length). New completion_code::SHORT_PACKET = 13 constant. End state under make run-quiet-dev-piv: PC_to_RDR_GetSlotStatus (10 bytes) goes out on bulk OUT (DCI 6 = ep 3 OUT), RDR_to_PC_SlotStatus (10 bytes) comes back on bulk IN (DCI 5 = ep 2 IN), parsed bMessageType=0x81 bSlot=0 bSeq=0 bStatus=0x02 (ICC absent — QEMU usb-ccid has no chardev backend, expected). Bug found + fixed during bring-up: configure_endpoint's EP context offset calculation was dci * ctx_size, off-by-one — the Input Context layout per xHCI 1.2 § 6.2.5 places EP DCI=N at offset (N+1) * ctx_size because the Input Control Context takes the first ctx_size slot. Fix changed the offset to (dci + 1) * ctx_size. Symptom was usb_xhci_ep_kick slotid 1, epid 6 followed by no usb_xhci_fetch_trb — the controller saw the doorbell, looked at the EP context's TR Dequeue Pointer, found 0 (because the IC context for the kicked DCI was untouched — driver had populated the DCI-1 slot instead), and silently stayed idle. Found via diagnostic dumps of the Device Context EP6 ctx (all zeros except EP State = Running) + comparison to the Input Context layout in the spec. The corresponding offset in address_device was correct from B-iv because EP0/DCI=1 happened to line up under the wrong formula (1*32 == (1+0)*32 is coincidentally the same as 2*32 // ICC + Slot). Polling-based completion retained; IRQ delivery skipped from B-vi scope per the substage plan agreed before B-vi.a (polling is acceptable for low-rate CCID APDU traffic). New code uses durable anchors only — no B-vi.c prefixes; the closing cleanup sweep of earlier substages' B-iii/B-iv/B-v labels stays queued for after B-ix.
  • 2026-05-24 — USB host stack Stream B substage B-vi.b: SET_CONFIGURATION + Configure Endpoint + bulk transfer rings. New XhciController::control_transfer_no_data(setup) helper carries the 2-TRB Setup/Status sequence for control transfers without a Data Stage (xHCI 1.2 § 6.4.1.2 with TRT=0; Status Stage direction is IN per USB 2.0 § 8.5.3). set_configuration(value) wraps the helper with the standard SET_CONFIGURATION request (USB 2.0 § 9.4.7, bmRequestType=0x00, bRequest=0x09, wValue=config_value). New XhciController::configure_endpoint(&CcidEndpoints) -> Result<u8, XhciError> allocates per-endpoint TransferRings, rebuilds the Input Context (Add Context flags = A0 | A_in | A_out; Slot Context re-populated with Context Entries = max DCI; bulk IN endpoint context with EP Type=6 + speed-correct MPS + TR Dequeue + Avg TRB Length=1024; bulk OUT context with EP Type=2 mirrored), submits the Configure Endpoint Command (TRB Type 12 per xHCI 1.2 § 4.6.6, DC=0), and reads back the Device Context's Slot Context DWord 3 to confirm the Addressed → Configured transition. New CcidBulkRings struct on XhciController retains DCIs + MPSes + transfer rings for B-vi.c's first bulk transfer. New fields port_idx/port_speed on XhciController (populated by address_device) so the Slot Context re-fill in Configure Endpoint preserves current state without needing to read the Device Context back. DCI mapping (xHCI 1.2 § 4.5.1) is 2 * ep_num + dir_bit: QEMU usb-ccid's ep 2 IN → DCI 5; ep 3 OUT → DCI 6; Context Entries field = 6. End state under make run-quiet-dev-piv: B-vi.a sequence still passes; SET_CONFIGURATION OK reaches the device; Configure Endpoint OK; slot state = 3 confirms the slot moved to Configured. New code uses durable anchors only — no B-vi.b references in comments per Convention 10; the closing cleanup sweep over earlier substages' B-iii/B-iv/B-v labels still queued for after B-ix.
  • 2026-05-24 — USB host stack Stream B substage B-vi.a: GET_DESCRIPTOR(Configuration) + descriptor parser + Evaluate Context for EP0 MPS reconciliation. EP0 control-transfer code factored — the 3-TRB Setup/Data/Status enqueue + doorbell + wait pattern lifts into a single control_transfer_in(setup, buf_paddr, length) helper on XhciController; get_descriptor_device reduces to a thin wrapper, new get_descriptor_configuration(&mut [u8]) issues the two-pass USB 2.0 § 9.4.3 read (9 bytes to learn wTotalLength, then full blob) into a caller-owned buffer. New MAX_CONFIG_DESCRIPTOR_SIZE = 512 SCAFFOLDING bound + ASSUMPTIONS row. New user/usb-host/src/descriptors.rs module — pure functions (no syscalls, no MMIO) walking the variable-length config blob: parse_configuration for the top-level descriptor + body slice, find_ccid_interface for the CCID-class (0x0B) interface together with its bulk IN + bulk OUT endpoint pair. Skips class-specific descriptors (CCID functional type 0x21, etc.) via bLength. New XhciController::evaluate_ep0_context(new_mps: u16) reuses the Input Context page from address_device, sets Add Context flags = A1 only (EP0), populates the EP0 endpoint context with the device-reported MPS, submits an Evaluate Context Command (TRB Type 13 per xHCI 1.2 § 4.6.7); the controller writes through to the Device Context's EP0 entry so subsequent transfers use the corrected MPS without rebuilding the slot. End state under make run-quiet-dev-piv: B-v sequence still passes; EP0 MPS reconciled 8 → 64 ("Evaluate Context OK"); GET_DESCRIPTOR(Configuration) returns wTotalLength=93 bytes; CCID interface located: bConfigurationValue=1, bInterfaceNumber=0, bulk IN @ address 0x82 (MPS=64), bulk OUT @ address 0x03 (MPS=64). B-vi.b consumes these to build the Configure Endpoint Input Context.
  • 2026-05-24 — USB host stack Stream B substage B-v: GET_DESCRIPTOR(Device) via EP0 control transfer. TransferRing gains an enqueue method symmetric with CommandRing::enqueue (two-step write: body without cycle → compiler_fence(Release) → control DWord with cycle as commit; returns slot paddr for completion matching; same Link-wrap-unimplemented stance with Convention 9 trigger pointed at B-vi runtime-driven transfer issuance). New TRB types in xhci.rs: SETUP_STAGE=2, DATA_STAGE=3, STATUS_STAGE=4, TRANSFER_EVENT=32. New XhciController::get_descriptor_device() -> Result<[u8; 18], XhciError> walks the standard 3-TRB control-transfer sequence on the slot's EP0 transfer ring (xHCI 1.2 § 4.11.2 + § 6.4.1.2): Setup Stage TRB carries the 8-byte SETUP packet [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00] via IDT=1 + TRT=3 (IN Data Stage); Data Stage TRB points at a freshly allocated DMA page with DIR=1 + length=18; Status Stage TRB has DIR=0 (OUT, opposite of IN data) + IOC=1, so the controller emits exactly one Transfer Event when the full sequence completes (event's TRB pointer matches the Status TRB paddr). All three TRBs enqueued, device doorbell rung (target=slot_id, value=1=EP0/DCI 1), wait_for_transfer_event(status_paddr) polls the event ring for the matching Transfer Event and returns 18 bytes from the buffer. New XhciError variants: EpNotReady / TransferRingFull / TransferTimeout / TransferFailed(u8). End state under make run-quiet-dev-piv: B-iv sequence still passes; B-v then logs the parsed descriptor — bLength=18 bDescriptorType=1 bcdUSB=0x0110 bDeviceClass=0 bMaxPacketSize0=64 idVendor=0x08E6 idProduct=0x4433 bNumConfigurations=1. Notable finding: QEMU's usb-ccid reports bMaxPacketSize0=64 despite running at Full Speed (PORTSC speed=1), while B-iv's address_device set EP0 MPS=8 (the spec-correct FS default). QEMU's xHCI emulation accepted the transfer regardless; real hardware would require an Evaluate Context command to update the EP0 endpoint context's Max Packet Size before subsequent transfers. The Convention 9 trigger named at log_device_descriptor in main.rs fires here — Evaluate Context implementation becomes load-bearing in B-vi alongside Configure Endpoint (same Input-Control-Context shape, with the Add Context flags addressing the relevant endpoint contexts). DMA buffer for the descriptor response is per-transfer alloc (alloc_zeroed_page); Convention 9 trigger at the alloc site points at B-vi for a buffer pool when bulk endpoints land.
  • 2026-05-23 — USB host stack Stream B substage B-iv: Address Device + Input Context + Device Context + EP0 transfer ring. New TransferRing type in ring.rs (structurally identical to CommandRing: 1 page, Link TRB at slot 255 toggling cycle, no enqueue path yet — B-v adds it; Convention 9 trigger named at the type: shared TrbRing refactor lands when a second transfer ring shows up in B-vi). New XhciController::address_device(port_idx, speed) -> Result<slot_state, XhciError> allocates a fresh Input Context page (Input Control Context with A0|A1 add-flags, Slot Context carrying Route=0 + Speed + Context Entries=1 + Root Hub Port Number, EP0 Endpoint Context carrying CErr=3 + EP Type=Control + speed-default Max Packet Size + TR Dequeue|DCS=1 + Avg TRB Length=8), allocates a fresh Device Context page (controller-written), allocates the EP0 TransferRing, installs Device Context paddr into DCBAA[slot_id], issues the Address Device Command (TRB Type 11 with BSR=0 — controller drives SET_ADDRESS on the bus), and reads back the Slot Context's Slot State [31:27] from the Device Context for logging. Context layout selects 32B vs 64B per-context spacing from cap.csz. New XhciError::SlotNotEnabled covers the misordering case. New SCAFFOLDING bound TRANSFER_RING_TRBS = 256 + ASSUMPTIONS row. XhciController grows input_context_paddr/vaddr, device_context_paddr/vaddr, ep0_transfer_ring: Option<TransferRing> fields (single-slot today; Convention 9 trigger: "Revisit when multi-device discovery lands (B-vi) — these belong in a slot-indexed table"). End state under make run-quiet-dev-piv: B-iii sequence still passes (port 5 detected, reset OK, NoOp OK, Enable Slot OK; slot_id = 1); B-iv then runs Address Device and logs Address Device OK; slot state = 2 (2 = Addressed per xHCI 1.2 § 6.2.2 Slot State enum). No actual EP0 transfers yet — that's B-v's GET_DESCRIPTOR. No IRQ delivery yet — that's B-vi.
  • 2026-05-22 — USB host stack Stream B substage B-iii: port enumeration + command/event flow + first commands. XhciController grows PORTSC accessors with RW1C-safe writes, port walk + port reset (preserve CCS/PED/PP, set PR=1, wait for PRC, RW1C-clear PRC), doorbell-array MMIO access, submit_command/wait_for_command_completion flow that enqueues a TRB via CommandRing::enqueue, rings HC doorbell 0, polls the event ring (via EventRing::poll_next) for a matching Command Completion Event, and updates ERDP on consumption (with EHB RW1C clear). noop_command() and enable_slot() helpers built on top. End state under make run-quiet-dev-piv: usb-ccid attached to qemu-xhci is detected on port 5 (Full Speed), port reset succeeds with PED=1, NoOp Command returns completion code 1 (Success), Enable Slot Command returns Slot ID = 1. XhciController.slot_id: Option<u8> threads the slot forward to B-iv's Address Device. Two non-obvious bugs found + fixed during bring-up: (1) all 64-bit MMIO writes (CRCR / DCBAAP / ERSTBA / ERDP) must be LO-first / HI-second under QEMU's xhci — the HI-first order silently failed because QEMU latches the dequeue/pointer on the HI write using the captured LO; (2) CommandRing::enqueue writes the TRB in two steps (body with cycle cleared → compiler_fence(Release) → control dword with cycle bit set) — a single 16-byte write_volatile<Trb> may be split by the compiler such that the controller reads a torn TRB with cycle=1 but stale body, asserting USBSTS.HCE. Kernel-side companion change: src/pci/mod.rs scan() now writes the PCI command register (I/O + Memory + Bus Master enable) for every discovered device after BAR decode — defensive against firmwares that don't, though SeaBIOS already does for the device classes shipped today. CommandRing::enqueue returns None on Link-TRB-slot collision; full Link-wrap (rewrite Link's cycle, toggle producer cycle, reset producer) is deferred — Convention 9 trigger named at the call site: "when port-status-change-event handler auto-issues commands without explicit driver code at the call site."
  • 2026-05-21 — USB host stack Stream B substage B-ii: operational bring-up. XhciController::bring_up runs the xHCI 1.2 § 4.2 sequence end-to-end — HCRESET (halt + reset + wait for CNR clear), CONFIG.MaxSlotsEn = 8, DCBAA allocation (1 page, zero-init), command ring (256 TRBs with Link TRB at slot 255 toggling cycle on wrap), event ring (256 TRBs) + ERST (single-segment table), interrupter 0's ERSTSZ / ERSTBA / ERDP installed, USBCMD.RUN, then poll USBSTS.HCH until cleared. End state verified under make run-quiet-dev-piv: USBSTS post-RUN = 0x00000000 (HCH=0 running, CNR=0 ready, no error bits). New user/usb-host/src/ring.rs module houses Trb / CommandRing / EventRing / Erst; new XhciController struct in xhci.rs owns the MMIO bases + DMA-backed rings; new XhciError enum (HaltTimeout / ResetTimeout / NotReadyTimeout / RunTimeout / DmaAllocFailed) covers the bring-up failure modes. Four new SCAFFOLDING bounds tagged + ASSUMPTIONS rows: COMMAND_RING_TRBS=256, EVENT_RING_TRBS=256, MAX_SLOTS_ENABLED=8, MAX_POLL_ITERATIONS=1000. CRCR / DCBAAP / ERSTBA / ERDP all written high-dword-first then low-dword per xHCI 1.2 § 4.6.1.2. No port enumeration, no command issuance, no IRQ — those land in B-iii / B-iv. Open: the default make run path (degraded-mode FDE) stalls between fde-mount's exit and virtio-net's module_ready in HEAD; pre-existing regression independent of B-ii — run-quiet-dev-piv (FDE-enabled e2e target) is the verification path that reaches the shell.
  • 2026-05-20 — YubiKey Stream A substage A-v.e: tools/format-volume/ host tool ships. Sibling to tools/sign-elf/ and tools/gen-dev-piv-keys/ — host-side, workspace-excluded, own .cargo/config.toml targeting aarch64-apple-darwin. Reads the 168-byte DPIV bundle (slot-9C Ed25519 sign key + slot-9D X25519 public key + bootstrap AID), generates a fresh ephemeral X25519 keypair, computes the X25519 ECDH shared secret, derives a ChaCha20-Poly1305 symmetric key via blake3::derive_key(WRAP_KDF_CONTEXT, &shared), AEAD-encrypts a 32-byte AES-256 master key (random by default; --master-key-hex override for reproducible runs), builds a 1-slot YubiKey-live volume header per ADR-032 § 4 (432 bytes total: 112 fixed prefix + 256 slot + 64 signature), signs the [0..header_length-64] prefix with the slot-9C Ed25519 key, zero-pads to 16 KiB to match the LBA 0..3 reserved extent, writes to the target raw disk image file. After writing, the tool self-verifies by round-tripping the bytes through the same cambios_fde_proto::parse + parse_slot_table + find_first_live_yubikey calls fde-mount uses at boot — catches format-drift bugs at format time rather than first-boot. Stream A's unlock side is now structurally complete: the runtime make run flow under --features dev-piv can make gen-dev-piv-keys + make format-volume + tools/format-volume/.../format-volume disk.img + boot, and fde-mount should walk health probe → PIN → read_volume_header → verify_volume_header → parse_slot_table → piv_decrypt → blake3 derive_key → ChaCha20-Poly1305 decrypt → stub install successfully. Out of scope for A-v.e: real YubiKey-backed format (uses the DPIV bundle for now; the production format mode talks to a real YubiKey via PC/SC, comes alongside stream B), Argon2id recovery slot provisioning (deferred to a future recovery-design ADR), multi-slot enrollment UX. Three new host-side deps for format-volume: chacha20poly1305, ed25519-compact (with x25519 feature), blake3. All match what fde-mount + gen-dev-piv-keys already use.
  • 2026-05-19 — YubiKey Stream A substage A-v.a: fde-mount boot module ships. New user/fde-mount/ userspace orchestrator walks the full FDE unlock flow per ADR-032 § Architecture lines 154-167: registers reply endpoint 32, probes piv_health (bails to "NoPiv" in default builds), verifies the dev PIN 123456, calls the new SYS_READ_VOLUME_HEADER to copy LBA 0..=3 (16 KiB) into a local buffer, calls SYS_VERIFY_VOLUME_HEADER (A-iv), walks the slot table via cambios_fde_proto::parse_slot_table + find_first_live_yubikey (A-v.b), extracts the 80-byte envelope per ADR-032 § 4 (locked at the prior ADR-032 commit), calls piv_decrypt(KeyManagement, ephemeral_pk) for the X25519 ECDH shared secret, derives the ChaCha20-Poly1305 symmetric key via blake3::derive_key(WRAP_KDF_CONTEXT, &shared), decrypts the envelope ciphertext in-place to recover the 32-byte AES-256 FDE master key, and stub-installs (A-v.d will replace the stub with SYS_INSTALL_MASTER_KEY). All key material (shared / symm_key / plaintext) zeroized via the zeroize crate before _start exits. New SyscallNumber::ReadVolumeHeader = 74 in cambios-abi (all seven syscall-workflow touchpoints updated); kernel handler uses the existing VirtioBlkDevice kernel-cmd path to read 4 × 4 KiB blocks; bootstrap-Principal-only authority (same gating as ClaimBootstrapKey / BindPrincipal); 16 KiB kernel-side stack buffer. New cambios_libsys::read_volume_header(&mut buf) -> i64 thin wrapper. New WRAP_ENV_* envelope constants in cambios-fde-proto (offsets, length, WRAP_KDF_CONTEXT, WRAP_NONCE, FDE_MASTER_KEY_LEN); 5 new tests verify offset contiguity, fit in SLOT_WRAPPED_KEY_MAX, AEAD overhead math, KDF context non-empty, nonce shape. limine.conf + limine-aarch64.conf add fde-mount.elf after virtio-blk. Makefile gets fde-mount / fde-mount-aarch64 / fde-mount-riscv64 targets + iso recipe inclusion + ELF signing. Three new userspace deps for fde-mount: chacha20poly1305 = "0.10" (no_std), blake3 = "1.8" (pure) already kernel-side, zeroize = "1.7" (no_std). Out of scope for A-v.a: SYS_INSTALL_MASTER_KEY (A-v.d), EncryptedBlockDevice<B> XTS-AES wrapper (A-v.c), tools/format-volume host tool (A-v.e), recovery boot path. All three arches build clean (make check-all). Default make iso succeeds with fde-mount included; runtime behavior in default builds = "NoPiv" log + clean exit.
  • 2026-05-18 — YubiKey Stream A substage A-v.b.1: shared-crate extraction. New cambios-fde-proto crate at workspace root (MPL-2.0, no_std, no dependencies — mirrors the cambios-abi precedent for shared-format crates). All volume-header byte layout, fixed-prefix parser, slot table parser, slot enums, and SlotEntry struct migrate from src/fs/crypto/header.rs into the shared crate; 21 parse-only tests move with them. Kernel src/fs/crypto/header.rs becomes a thin shim that re-exports the proto types and keeps only verify_header (which depends on crate::crypto::verify — kernel-only). Userspace shell fde-test switches from duplicated FDE_OFF_* constants to the proto crate's named imports. Setup for A-v.a's fde-mount boot module + A-v.e's format-volume host tool: both will consume cambios_fde_proto::{parse, parse_slot_table, find_first_live_yubikey} directly without duplicating byte layout. 853 kernel host tests pass (was 874; -21 moved to proto crate where they pass standalone). All lints clean; baseline regenerated to drop the stale src/fs/crypto/header.rs:47-58 entries now living in the proto crate (which is outside src/ and not scanned).
  • 2026-05-18 — YubiKey Stream A substage A-v.b: multi-slot volume header parser. Extends src/fs/crypto/header.rs with SlotType enum (Empty/YubiKey/Argon2idPassphrase per ADR-032 § 4 slot table), SlotClass enum (Live/Recovery per § 5), and SlotEntry { slot_type, slot_class, wrapped_key_len, slot_principal, wrapped_key } carrying the parsed slot at full 256-byte fidelity. New parse_slot_table(bytes, slot_count) -> [Option<SlotEntry>; MAX_VOLUME_SLOTS] walks the slot region after verify_header succeeds — pure function, validates slot_type/slot_class byte ranges + wrapped_key_len <= SLOT_WRAPPED_KEY_MAX=220, returns three new HeaderError variants for the structural failures. Helper find_first_live_yubikey(&slots) returns the first SlotEntry matching (slot_type=YubiKey, slot_class=Live) — the unlock target A-v.a's fde-mount will walk to. 15 new host tests cover all-None zero-slot, single live-YubiKey round-trip, 3-slot mixed (live YK + recovery Argon2id + empty), slot-count overflow / truncated bytes / bad slot_type / bad slot_class / oversize wrapped_key_len rejection, max wrapped_key_len acceptance, find-helper's recovery+Argon2id-skip behavior + first-of-multiple selection + empty-table handling, plus enum byte round-trips. Dispatcher's handle_verify_volume_header updated to map the three new HeaderError variants to InvalidArg (structural failures). 874/874 kernel host tests pass (was 859; +15). Out of scope for A-v.b: A-v.a fde-mount orchestrator that consumes this (next), A-v.c EncryptedBlockDevice (XTS-AES wrapper), A-v.d SYS_INSTALL_MASTER_KEY, A-v.e format-volume tool.
  • 2026-05-18 — YubiKey Stream A substage A-v.0: zero-on-free in FrameAllocator. New zero_frame_range(start_idx, count) helper writes zeros through the HHDM map for each freed frame; wired into both free and free_contiguous after the bitmap-clear succeeds. Closes a class of "freed-frame remnant" attacks system-wide; load-bearing for A-v.a's fde-mount orchestrator which will briefly hold an AES-256 FDE master key in a stack buffer between piv_decrypt and SYS_INSTALL_MASTER_KEY. Helper is cfg-split (real write under #[cfg(not(test))], no-op in host tests since the simulated phys addresses have no backing memory). Single unsafe block with SAFETY comment per Convention 1. Early-boot guard short-circuits when crate::hhdm_offset() == 0. +2 wiring tests; 859/859 host tests pass.
  • 2026-05-18 — YubiKey Stream A substage A-iv: first consumer round-trip. New src/fs/crypto/{mod.rs,header.rs} (kernel) carries VolumeHeader::parse + verify_header per ADR-032 § 4 — pure functions that check magic / header_length bounds / slot_count ≤ MAX_VOLUME_SLOTS=16 / volume_uuid == bootstrap_pubkey (AID-equals-pubkey per ADR-025) / ed25519 signature over bytes[0..header_length-64]. Wires through crate::crypto::verify so no direct ed25519-compact use in fs/. New SyscallNumber::VerifyVolumeHeader = 73 in cambios-abi (identity-required; requires_identity() + identity test arrays updated; from_u64 mapped; all_syscall_numbers_covered range extended to 0..=73). Kernel dispatcher arm + handle_verify_volume_header copy the user buffer through UserReadSlice (HEADER_MAX_LEN = 4 KiB stack-allocated), load the baked bootstrap pubkey via BOOTSTRAP_PRINCIPAL.load().current_key_bytes(), route through verify_header, and map HeaderError variants to InvalidArg (structural) or PermissionDenied (AID / signature mismatch). New cambios_libsys::verify_volume_header(&[u8]) -> i64 thin wrapper. Shell gains an fde-test command that probes piv_health, verifies the dev PIN 123456, reads the slot-9C pubkey, builds a HEADER_MIN_LEN=176 byte 0-slot header with volume_uuid = pubkey, signs the prefix via piv_sign(slot=Signature), and hands the result to sys::verify_volume_header. Under --features dev-piv on both kernel and key-store-service, this round-trips end-to-end (SwPivBackend slot-9C key matches kernel-baked bootstrap pubkey by construction); default builds exit early at the NotPresent health probe. Host test coverage: 14 new tests in fs::crypto::header (minimal-header round-trip, tampered content / signature / pubkey rejection, AID-vs-signing-key mismatch, length-bound violations, padded-1-slot header round-trip); 857/857 kernel host tests pass (was 843). Out of scope for A-iv: kernel-as-IPC-client architecture (kernel still reads BOOTSTRAP_PRINCIPAL.load() for verify, doesn't yet IPC to key-store-service — that lands at A-v with the FDE mount path + EncryptedBlockDevice + piv_decrypt-driven master-key unwrap), real volume-on-disk format/mount, multi-slot table parsing. No new lock, no new capability, no new unsafe. SYS_VERIFY_VOLUME_HEADER survives A-v as the diagnostic / recovery-shell primitive (does not get torn down).
  • 2026-05-18 — YubiKey Stream A substage A-iii: IPC dispatch wires cambios-key-store-service for the seven new CMD_PIV_* commands (3-9). New user/key-store-service/src/piv/dispatch.rs carries the pure dispatch_piv_command<B: PivBackend>(backend, request, response) -> usize function — decodes per the libsys codec from A-i, calls into the backend, encodes the matching response, maps backend PivError to wire PivStatus bytes. src/piv/inert.rs adds the always-NotPresent stand-in selected in default builds; src/piv/mod.rs introduces a pub type ActiveBackend cfg-alias (Sw under dev-piv, Inert otherwise) so main.rs picks one at compile time without Box<dyn>. src/main.rs instantiates ActiveBackend once at startup and adds a single arm in the service loop that routes any CMD_PIV_* opcode through dispatch; legacy CMD_SIGN/CMD_GET_PUBKEY paths unchanged. user/libsys/src/keystore.rs gains seven IPC wrappers — piv_health, piv_verify_pin, piv_list_slots, piv_get_pubkey, piv_sign, piv_decrypt, piv_attest — each taking a reply_endpoint: u32 the caller must have pre-registered via SYS_REGISTER_ENDPOINT; they compose the codec, call sys::write to KS endpoint 17, block on sys::recv_msg, strip the 36-byte IPC envelope, and return the typed result. Audit-emit on key-use is deferred per Convention 9 with Revisit when: userspace audit-emit syscall lands OR audit-tail subscribes to key-store events at both server (dispatch sign/decrypt arms) and client (libsys keystore module) sites — ADR-007's audit-emit primitives are kernel-side today, so the userspace-emit story is its own design problem. Identity gating relies on recv_verified per ADR-026 (anonymous senders dropped before reaching dispatch; no Principal-value branching inside the kernel ring). Test coverage: 10 InertPivBackend dispatch tests in default builds, +6 SwPivBackend-through-dispatch tests under --features dev-piv (sign+verify, decrypt ECDH round-trip, PIN lockout surfacing health=NotReady, sign-before-PIN→AuthRequired); 39/39 pass with dev-piv, 10/10 default. Out of scope for A-iii: the first consumer (A-iv FDE volume header sign+verify), CcidPivBackend (stream B). No new syscall, no new capability, no new lock, no new unsafe.
  • 2026-05-18 — YubiKey Stream A substage A-ii: software-emulated PIV backend lands behind a new dev-piv cfg flag spanning the kernel and cambios-key-store-service. New host tool tools/gen-dev-piv-keys/ derives slot-9C (Ed25519, signing) + slot-9D (X25519, key management) keypairs from a persistent per-developer seed at tools/gen-dev-piv-keys/.dev-seed.bin (gitignored), writes dev_bootstrap_pubkey.bin (40-byte CKEY v1, format-compatible with sign-elf --export-pubkey) at the workspace root and user/key-store-service/dev_piv_secret.bin (168-byte DPIV v1 bundle) inside the service crate, both gitignored. The kernel's bootstrap_identity_init baked-in pubkey switches between the committed real-YubiKey bootstrap_pubkey.bin (default) and the dev pubkey (under --features dev-piv) via cfg-gated include_bytes!. user/key-store-service gains src/lib.rs + src/piv/{mod.rs,sw.rs} exposing the PivBackend trait and SwPivBackend implementation that answers PivBackend::{health,verify_pin,list_slots,get_pubkey,sign,decrypt,attest} against the DPIV bundle's keys; decrypt performs X25519 ECDH and returns the 32-byte shared secret per real PIV slot-9D semantics. PIN policy mirrors PIV defaults (PIN = 123456, 3-failure lockout, verified state persists for backend lifetime). 23 host tests cover bundle parsing, PIN lockout transitions, sign-verifies-under-advertised-pubkey, and decrypt-roundtrip-against-independent-ECDH-sender; all pass on cargo test --features dev-piv --target x86_64-apple-darwin. Out of scope for A-ii: IPC dispatch arms for the new CMD_PIV_* codec (substage A-iii), kernel-side consumer integration (A-iv FDE volume header sign+verify). Default no-feature builds untouched; production path continues using the real YubiKey bootstrap pubkey. make gen-dev-piv-keys is the explicit prereq before any --features dev-piv build.
  • 2026-05-18 — USB host stack Stream B substage B-i: user/usb-host/ userspace driver lands as a boot module on endpoint 31. Discovers an xHCI controller via the SYS_DEVICE_INFO descriptor (filters class=0x0C, subclass=0x03, prog_if=0x30 — last byte newly exposed in the existing 108-byte descriptor's reserved pad slot at offset 9, not an ABI break), maps the controller's MMIO BAR via SYS_MAP_MMIO, parses the xHCI 1.2 § 5.3 capability register block (CAPLENGTH, HCIVERSION, HCSPARAMS1, HCCPARAMS1, DBOFF, RTSOFF), and logs the dump. Idles cleanly on hosts with no xHCI (aarch64/riscv64 without -device qemu-xhci). No port enumeration, no transfers, no IRQ — those land in B-ii / B-iv. make run / run-aarch64 / run-riscv64 now attach -device qemu-xhci,id=xhci0 -device usb-ccid,bus=xhci0.0 so the real CCID transport path is wired from B-i forward (CCID chardev backend wires in at B-v). All three arches build clean (make check-all). Foundation for stream A's PivBackend::CcidPiv backend swap at B-vii.
  • 2026-05-16ADR-029 step 6 backend half (3-commit 6-i/ii/iii chain). Per-process FdTable on ProcessDescriptor with MAX_FDS_PER_PROCESS = 64 SCAFFOLDING + monotonic generation counter for stale-fd detection; FileMetadata struct added to cambios-abi for SYS_STAT return shape. PosixFsBackend<B> gains create_inode / open_inode / stat_inode / acl_grant / acl_revoke / acl_list methods, all owner-or-ACL-row authorization per ADR-029 § Decision 3, all journal-flush-before-header-write per § Decision 5 (idempotent on replay). Tier3-at-1-TiB binding tests updated for the ~3.6 KiB SLOT_OVERHEAD growth from FdTable per ADR-008 § Post-Change Review (binding flipped MaxSlots → BudgetCeiling). Step-6 scope is backend-only: dispatcher arms still route SYS_FILE_OPEN..SYS_ACL_LIST through handle_posix_stub → Enosys; the runtime POSIX_STORE singleton is deferred to step 7 because the 16 MiB journal exceeds the 4 MiB kernel heap, and step 6 has no userspace consumer to motivate routing it through the frame allocator or amending the ADR. Step 6 handler logic is exercised by host tests with MemBlockDevice; 833 host tests pass.
  • 2026-05-16ADR-010 § Divergence 3 landed: CambiObject v2 records adopt the shared bitmap + journal (4-commit 5D chain). FORMAT_VERSION bumps to 2; the superblock gains bitmap_region_lba / journal_region_lba / data_region_lba / journal_capacity_bytes / last_checkpoint_offset declaring the v2 disk regions. New ARCOREC_MAGIC_V2 = "ARCOREC2" distinguishes v2 records from v1; the 192-byte header-resident extent array at offset 568..760 carries up to 16 (start_lba, block_count) extents pointing into the shared data region (single-extent allocation in v1; multi-extent fallback documented as Revisit when:). DiskObjectStore gains in-memory bitmap: BlockBitmap + journal: Journal struct fields; mount runs journal-replay over a fresh all-free bitmap and cross-checks against the on-disk bitmap region (defense-in-depth, mirroring PosixFsBackend 5C-ii). put writes v2 records (journal BitmapMutation::Set → content blocks → header → write-through bitmap region); get magic-dispatches on ARCOREC1 vs ARCOREC2 so v1 records remain readable forever; delete journals BitmapMutation::Clear for v2 records before zeroing the slot header. v1 disks (FORMAT_VERSION=1 superblock) are rejected at mount via SuperblockState::UnknownVersion. Crash-safety gap (out of scope): the current write order leaves a window where a crash between header write and journal record could orphan data blocks; full atomicity per Divergence 3's "atomic with the CambiObject record's header commit" wording requires mount-side slot-scan augmenting the bitmap reconstruction OR a journal record carrying enough info to commit the header on replay — Revisit when: production crash testing surfaces it.
  • 2026-05-15ADR-029 § Divergence 2 landed: JOURNAL_LOCK(13) promoted to top-level lock-hierarchy position alongside BLOCK_BITMAP_LOCK(12) (4-commit 5C chain). PosixFsBackend gains in-memory bitmap and journal struct fields; mount reorders to journal-replay → defense-in-depth bitmap cross-check → inode scan; new allocate_block / free_block primitives journal an ExtentUpdate record per ADR-029 § Decision 5 with BitmapMutation::Set/Clear. src/fs/journal.rs adds the Journal struct with single-wrap-around circular-log semantics (KIND_PAD = 0xFF records fill the tail when records won't fit; wrap-stop at last_checkpoint_offset prevents re-applying pre-checkpoint stale records). POSIX_STORE(11) reserved in the hierarchy doc; concrete Spinlock<Option<PosixFsBackend<...>>> static declaration deferred to ADR-029 step 6+ when the kernel-singleton wire-up picks a concrete BlockDevice. Cross-backend acquisition pattern documented: POSIX-only / CambiObject-only / CAMBIO-cross-backend each follow strictly-downward chains rooted at their top-level lock. The shared journal record format makes the two backends' allocation transactions compatible from the moment JOURNAL_LOCK is reachable from both sides.
  • 2026-05-13ADR-028ADR-029ADR-030 ABI surface reserved in cambios-abi. SYS_CAMBIO=50, SYS_REGALO=51, SYS_STREAM=52, SYS_FILE_OPEN..SYS_ACL_LIST=53..72. New type surface: ObjectHandle / FileDescriptor / StreamEndpoint / Rights / PosixInode / Extent / AclEntry / FrozenInodeView / InodeId / StreamCapShape. ChannelRecord gains stream_cap_shape + stream_state fields per ADR-030.
  • 2026-05-07ADR-027 rendering-limb cluster wiring landed end-to-end (Option A — minimum viable migration). user/compositor calls sys::cluster_create at startup with a 2-member manifest (compositor + scanout, both bootstrap Principal); cluster_id is threaded through an extended RegisterCompositor wire format ([tag:4][cluster_id:8] = 12 bytes); both scanout drivers (scanout-virtio-gpu and scanout-limine) call sys::cluster_join on receipt. New libsys wrappers (cluster_create / _join / _revoke / _info) + ClusterMember wire-format struct + CLUSTER_POLICY_* / CLUSTER_ROLE_* constants. Boots end-to-end on x86_64 (make run-quiet reaches cambios> shell). Out of scope (substrate-deferred): virtio-input membership (no inbound endpoint), cluster_policy::caps_for_role population, SYS_CHANNEL_CREATE cluster_id arg + auto-attach, stripping the pairwise handshake's cap-token-passing role. Triggers for each named in code (Convention 9) and in the original ADR-027 § Migration Path. Cluster value-add today is structural (membership tracking + exit-path auto-revoke fan-out); functional cap-promotion lands when the substrate does.
  • 2026-05-07ADR-027 cluster syscall handlers landed end-to-end. SYS_CLUSTER_CREATE / _JOIN / _REVOKE / _INFO (44–47) wire to the ClusterManager skeleton at lock position 5 (per ADR-027 § Architecture); CapabilityKind::CreateCluster / ClusterRevoke added. Authority: cluster-create needs CreateCluster cap or bootstrap Principal; cluster-revoke needs creator OR ClusterRevoke cap OR bootstrap. New cluster_policy.rs module: caps_for_role is a v1 stub returning empty (real cap shapes land with the rendering-limb migration that walks the existing pairwise handshakes); on_member_depart is populated — RenderingLimb's policy is "any departure tears down the limb", per ADR-027 § Migration Path step 7. handle_exit extended with cluster-departure cleanup driving the same teardown sequence as SYS_CLUSTER_REVOKE. Path A (advisory quiesce) per ADR-027 Divergence 2 — cluster revoke wraps N atomic per-channel revokes inheriting tombstone-on-revoke from ADR-007 Divergence 7. Bootstrap CreateCluster grant to init + rendering-limb migration pending.
  • 2026-05-06ADR-027 ClusterManager skeleton at src/ipc/cluster.rs: pure bookkeeping + state machine (Forming → Active → Revoking → Revoked; member Expected → Joined → Departed), slot+generation table, 34 host tests covering create/join/auto-promote/depart/attach-channel/begin-revoke/complete-revoke/slot-reuse. Three SCAFFOLDING bounds (MAX_CLUSTERS / MAX_CLUSTER_MEMBERS / MAX_CLUSTER_CHANNELS = 32 each per ADR-027 § Architecture v1-endgame math) tagged + rowed in docs/ASSUMPTIONS.md. No syscalls, no scheduler hooks, no integration — cluster ABI reservation, lock-hierarchy renumber, handlers, and rendering-limb migration follow per ADR-027 § Migration Path steps 4–7.
  • 2026-05-04verification/CLAIMS.md: the honest gap-map between "what runs" (this file) and "what's proven." 14 rows spanning all four Status values (Proven / Tested / Asserted / Aspirational) and all three Layers (1=behavior, 2=guarantee, 3=meaning). Strict schema requires Bound: and Gap: on every row; vacuous rows fail review. Layer-3 meaning-claims (Zero-trust, AI-watches-not-decides) are explicitly Aspirational by their nature and the doc says so. C-11 (IPC identity transcription invariant) and C-12 (ObjectStore ownership) carry the planned Kani→Verus pivot triggers.
  • 2026-05-04verification/dtb-proofs/: 5 Kani harnesses on the DTB parser at src/boot/riscv.rs (header validation, byte-extraction safety, wire-format contracts, bounded callbacks). Slice-shim refactor extracts pure walk_dtb_slice(&[u8]) and FdtHeader::read_slice(&[u8]) as the verifier entry points; the unsafe walk_dtb(dtb_phys) becomes a 4-line pointer-to-slice bridge. Real Kani finding fixed: be_u32_at / be_u64_at claimed "return 0 on overrun" but actually panicked with arithmetic overflow when the symbolic offset reached usize::MAX - 8; both helpers now use checked_add. ADR-013 Decision 2's "no panics" claim now has provable backing for the byte-extraction layer. End-to-end walker proof (P-DTB-6) deliberately deferred — its CBMC budget at the natural bound exceeds one CPU-hour; needs walker restructuring or the planned Kani→Verus pivot, not unwind-tuning.
  • 2026-05-03 — Multi-Principal vault Phase 1C: new userspace service extending key-store (endpoint 17) with kernel consultation at process spawn. Plurality lives in userspace; kernel keeps Principal singular and atomic. AI sandbox is per-Principal. Implements the design from the identity.md Phase 1C rewrite + ADR-026.
  • 2026-05-03ADR-027: service clusters as identity-bound channel meshes. Designed only — kernel-ABI cluster handle (cluster_create / cluster_join / cluster_revoke) and lock-hierarchy placement spelled out, no implementation yet. First cluster target: rendering limb (compositor + scanout + virtio-input + libgui clients). Closes the compositor-#PF-on-clean-client-exit class (cluster-shaped revoke vs per-channel teardown).
  • 2026-05-02ADR-026: "transcribe-not-interpret" identity invariant. Codifies the rule the kernel was already implicitly following: read a Principal AID from ProcessCapabilities.principal, copy it onto outgoing IPC sender_principal / authored CambiObject creator / audit events; never branch on the AID value to make a policy decision. Capability-shape duality (kernel (endpoint, rights) table entry vs rich external envelope) made explicit. AI-watches-flags-sandboxes-but-does-not-write-policy added as load-bearing principle. ADR-007 gains a divergence appendix tying the audit-cap migration to this framing. identity.md rewritten around the multi-Principal vault model at the same time.
  • 2026-05-01ADR-025: Principal-as-AID + crypto-agility plumbing. Principal is now a 32-byte AID (Authentication Identifier) — currently algo=Ed25519, hash=blake3(pubkey) but the algo/hash fields are first-class so post-quantum migration is a code-only change, not a wire-format change. 256-byte IPC envelopes include 32-byte AID + algo + hash byte; PQ audit shows the format is forward-compatible to Dilithium without growth.
  • 2026-05-01user/network polish: virtio-net registers reply-endpoint per ADR-027 framing precursor + wall-clock deadlines on retry/timeout + virtio-net modern PCI header parsing.
  • 2026-04-27ADR-022 wall-clock landed: SyscallNumber::SetWallclock / GetWallclock (39 / 40), SetWallclock capability gate, src/time/wallclock.rs (4 unit tests). Identity-aware shell prompt (did:key:z6Mk…@user>); GUI cmd_play flow; terminal-window app's manual clock command for offline demos. The ADR-022 implementation reservation memory pin from 2026-04-22 is resolved.
  • 2026-04-27user/terminal-window/ (new first-party app): boot splash matching coherentforge.com palette → recede-to-watermark transition → identity-aware shell with ANSI SGR per-cell color in the GUI Grid renderer. Compositor gains z-stack semantics: layered windows + per-pixel alpha; alpha-preserving Surface::blend_pixel + scale_brightness. Compositor forwards Pointer events to the focused window (was keyboard-only).
  • 2026-04-26 — Syscall ABI surface factored out to a new top-level cambios-abi/ crate (ADR-024; no_std, MPL-2.0, zero deps beyond core). Owns the canonical SyscallNumber / SyscallError / SyscallArgs definitions. Kernel re-exports them via pub use cambios_abi::* so existing use crate::syscalls::SyscallNumber call sites compile unchanged. libsys drops its 37 private const SYS_*: u64; policy-service drops 38 private const SYS_*: u32 plus the 9 dead-code warnings they were emitting on every make iso. policy-service's profile arrays move from &[u32] to &[SyscallNumber] — type-safe at every call site. Single source of truth across kernel + userspace; the "must match src/syscalls/mod.rs" comment that flagged the old hand-mirror is gone. 4-commit chain on syscall-abi-crate branch (crate creation → kernel re-export → libsys migration → policy-service migration); each independently passes make check-all.
  • 2026-04-26 — Audit consumer capability (ADR-023). CapabilityKind::AuditConsumer replaces the bootstrap-Principal-only check on SYS_AUDIT_ATTACH (ADR-007 § "Audit channel boot sequence"); SYS_GET_PROCESS_PRINCIPAL = 42 resolves a subject_pid to its bound 32-byte Principal so audit consumers can render did:key:z6Mk… without widening the 64-byte event format. Recent-exits ring on ProcessTable (SCAFFOLDING, 64 entries) handles principal-after-exit lookups. New signed user/audit-tail/ boot module holds the cap, attaches to the ring, resolves principals via the new syscall, and prints one-line summaries to serial. Tri-arch built; 554 host tests pass.
  • 2026-04-21 — Kani proofs for FrameAllocator (verification/frame-proofs/): 9 harnesses on src/memory/frame_allocator.rs covering allocate, free, allocate_contiguous, free_contiguous, and add_region overflow. Proof authoring found 2 integer-overflow sites that panic in debug / wrap in release on malformed bootloader memory-map entries (add_region line 138, reserve_region line 158); both fixed with saturating_add. reserve_region's fully-symbolic overflow proof blows CBMC's memory budget; covered instead by a mechanical-copy of the add_region fix + a unit-test regression gate (test_reserve_region_wrap_boundary_no_panic).
  • 2026-04-21 — Kani proofs for CapabilityManager (verification/capability-proofs/): 12 harnesses on src/ipc/capability.rs. Tier A (7) covers ProcessCapabilities — empty-table denial, grant/verify composition, revoke effectiveness, absent-endpoint rejection, rights upgrade, count bound ≤ 32, capacity-full rejection. Tier B (5) covers CapabilityManager on a 3-slot Box::leak'd manager — stale-generation rejection (ADR-008 slot-reuse defence), delegate-without-delegate-right denied, no-rights-escalation, revoke_all_for_process clears every endpoint cap + all 5 system-cap flags, non-bootstrap revoke returns AccessDenied with no state change. Kernel source unchanged; proof crate includes src/ipc/capability.rs verbatim via #[path] and stubs crate::ipc::{ProcessId, EndpointId, CapabilityRights, Principal} locally, drops the audit emit via the existing #[cfg(not(any(test, fuzzing)))] gate flipped by build.rs --cfg fuzzing. ADR-000 § Divergence now cites these proofs as the formal backing for the capability-soundness claim.
  • 2026-04-21did:key encoder for Principals (identity.md Phase 4 pull-forward): 32-byte Ed25519 pubkey ↔ did:key:z6Mk… via multicodec 0xed + base58btc, implemented in user/libsys/ (no new deps, no_std). Shell gains a did-key command (self / encode-hex / decode-did:key). Cross-verified against the RFC 8032 Test 1 vector. x/a/r.
  • 2026-04-22 — Kernel ECAM PCI enumerator → aarch64 GUI parity. src/pci/mod.rs grew a mod ecam + mod config shim: x86_64 continues to use mechanism-1 port I/O (CF8/CFC), aarch64 + riscv64 route through ECAM MMIO at ECAM_VIRT + (bus << 20) + (dev << 15) + (func << 12) + off. scan, decode_bars, and walk_virtio_modern_caps are now arch-agnostic. pci::init_ecam(phys_base, size) maps the window into TTBR1 via memory::paging::map_range on aarch64 (kernel frame allocator; bus 0 only, 1 MiB = 2 intermediate tables) and is a sanity check + VA publish on riscv64 (boot-HHDM gigapages already cover [0, 4 GiB)). handle_device_info is no longer x86-gated. Under make run-aarch64-gui (new target) the kernel discovers virtio-gpu-pci + virtio-keyboard-pci over ECAM, scanout-virtio-gpu + virtio-input bind, the compositor handshake completes, and worm renders frames — the verified failure mode before this landed was scanout-virtio-gpu hitting SYS_DEVICE_INFO = Enosys and falling into passive idle, which stalled the compositor's blocking recv_verified(WelcomeCompositor).
  • 2026-04-22 — Pong v0 (first-party app): user/pong/ — third game in the HN-launch playable arc. Continuous-motion physics (8-bit subpixel integer fixed-point, AABB collision, classic-Pong spin, speed-capped AI), Tree-world palette (logs + acorn + grass). Replaces worm as default GUI boot module; worm + tree stay buildable for regression via make worm / make tree (and each -aarch64 / -riscv64 variant). Second consumer of libgui::FrameClock (extracted in the prior commit).
  • 2026-04-22libgui::FrameClock: fixed-interval tick gate for self-driven apps. Pure-logic, host-testable; 7 unit tests covering boundary, refire, large-gap-no-catchup, backward-time, seed reset, zero-interval, step_ticks getter. Extraction trigger per Development Convention 9 (second consumer = pong).
  • 2026-04-22 — Worm v0 (first-party app): user/worm/ — classic snake on a 20×15 dirt grid with Tree-palette continuity (shared biosphere narrative). 200 ms tick, ring-buffer body + collision bitmap, bounded iteration. Replaces tree as default GUI boot module; tree retained for regression.
  • 2026-04-21 — Tree v0 (first-party app): user/tree/ — 9×9 Minesweeper homage on libgui + virtio-input, replaces hello-window as default GUI boot module; hello-window retained as protocol regression test.
  • 2026-04-21 — Input-1 (ADR-012): user/libinput-proto 96-byte wire format; user/virtio-input driver (modern virtio-pci, device class probed via VIRTIO_INPUT_CFG_EV_BITS, evdev→HID translation); compositor forwards events to focused window on COMPOSITOR_INPUT_ENDPOINT = 30; libgui Client::poll_event drains on a tagged libgui-proto message. make run-gui captures Cocoa keyboard/mouse → serial log end-to-end. x86_64 only.
  • 2026-04-21user/libgui v0: Client::open, Surface primitives (fill_rect, draw_line Bresenham, draw_text_builtin 8×8 ASCII font, blit_bitmap with optional chroma-key), TileGrid. hello-window ported to the library; Tree v0 consumes it.
  • 2026-04-21 — Licensing: AGPLv3-or-later on kernel + services + apps, MPL-2.0 on user/libsys/. SPDX headers across every source + config file. LICENSE files at both scope levels. Repo now public at github.com/coherentforge/CambiOS.
  • 2026-04-21 — Docs pivot: ADR-016 rewritten as "Windows Compatibility via Bounded Static Shims" (AI translator pipeline withdrawn); ADR-017 slot reused for "User-Directed Cloud Inference" (generative-not-extractive principle operationalized).
  • 2026-04-20 — Phase Scanout-4.b: scanout-virtio-gpu is the default scanout driver. Modern virtio-pci transport via kernel-parsed VirtioModernCaps + SYS_VIRTIO_MODERN_CAPS = 38. Five 2D ops (CREATE_2D / ATTACH_BACKING / SET_SCANOUT / TRANSFER_TO_HOST_2D / RESOURCE_FLUSH). make run-gui shows a visible green window.
  • 2026-04-19 → 04-21ADR-020 Phase A → B.1-B.5 → C landed: typed UserReadSlice / UserWriteSlice at the syscall boundary; every handler migrated; raw u64 user pointers are type-system unreachable in production code. Phase D marked deferred with revisit triggers.
  • 2026-04-19 → 04-20ADR-021 Phase A → B.3 landed: BootError propagation from Limine adapter → APIC → timer / PLIC init. Phase C lint (make check-boot-panics) enforces no new panic sites in the curated boot-path file set.
  • 2026-04-19 — RISC-V Phase R-6: riscv64 boots to cambios> shell prompt with 5 signed boot modules via -initrd (CAMBINIT archive). Third architecture at service-level parity.

Subsystem status

Archs column: x = x86_64, a = AArch64, r = riscv64. x/a/r means first-class parity. Subsystems still gapped on one or two arches show what runs today; see known issues for what's missing.

Subsystem Status Archs Code Design
Microkernel core Done x/a/r src/microkernel/, src/memory/ CambiOS.md
Per-CPU SMP scheduler Done x/a/r src/scheduler/ SCHEDULER.md, ADR-001
Voluntary + preemptive context switch Done x/a/r src/arch/*/mod.rs ADR-001
IPC control path (256-byte messages) Done x/a/r src/ipc/ ADR-000, ADR-002, ADR-005
IPC bulk path (shared-memory channels) Done (Phase 3.2d) x/a/r src/ipc/channel.rs ADR-005
Capability revocation Done (Phase 3.1, bootstrap-authority only; grantor + revoke-right paths deferred to post-v1) x/a/r src/ipc/capability.rs ADR-007
Boot-time-sized kernel object tables Done (Phase 3.2a-d) x/a/r src/config/tier.rs, src/memory/object_table.rs, src/process.rs, build.rs ADR-008
Deployment tiers Designed, policy-only (single binary; tier selects TableSizingPolicy at install) ADR-009, GOVERNANCE.md
Audit infrastructure Done (Phase 3.3) x/a/r src/audit/ ADR-007
Audit consumer capability + audit-tail Done (bootstrap-only check replaced) x/a/r user/audit-tail/, src/ipc/capability.rs (AuditConsumer), src/syscalls/dispatcher.rs (handle_get_process_principal), src/process.rs (RecentExitsRing) ADR-023
Policy service (syscall allowlisting) Done (Phase 3.4b, per-process allowlists) x/a/r user/policy-service/ ADR-006
Cryptographic identity Done (Phase 1C, hardware-backed + load-bearing; Principal as 32-byte AID with algo/hash agility per ADR-025; transcribe-not-interpret invariant per ADR-026) x/a/r src/ipc/, src/syscalls/, user/libsys/, bootstrap_pubkey.bin identity.md, ADR-003, ADR-025, ADR-026
Multi-Principal vault service Done (Phase 1C; userspace plurality, kernel-side Principal stays singular/atomic; AI sandbox is per-Principal) x/a/r user/key-store-service/ (vault extends ep 17) identity.md Phase 1C, ADR-026
did:key encoding (Principal ↔ did:key:z6Mk…) Done (encoder + decoder; not full DID resolution) x/a/r user/libsys/src/lib.rs (did_key_encode/did_key_decode), user/shell/src/main.rs (did-key cmd) identity.md Phase 4
Signed ELF loading Done (ARCSIG trailer, Ed25519) x/a/r src/loader/ ADR-004
Content-addressed ObjectStore (RAM) Done (Phase 1C, fallback) x/a/r src/fs/ram.rs ADR-003
Persistent ObjectStore (disk) Done (Phase 4b) x (via virtio-blk) src/fs/disk.rs, src/fs/block.rs, src/fs/lazy_disk.rs, src/fs/virtio_blk_device.rs ADR-010
BlockDevice abstraction Done (Phase 4a.i) x/a/r src/fs/block.rs ADR-010
POSIX file storage backend Substrate done (5A–5D) + step-6 handler half done (create_inode / open_inode / stat_inode / acl_grant / acl_revoke / acl_list); runtime POSIX_STORE singleton + dispatcher wire-up + libsys shim deferred to step 7. Per-process FdTable lives on ProcessDescriptor. SYS_FILE_OPEN..SYS_ACL_LIST still return Enosys until step 7. x/a/r (host-testable; no userspace consumer yet) src/fs/posix/mod.rs, src/fs/bitmap.rs, src/fs/journal.rs, src/process.rs (FdTable), cambios-abi/src/lib.rs (FileMetadata) ADR-029
FS service Done (endpoint 16, ObjectStore gateway) x/a/r user/fs-service/
Key-store service Done (endpoint 17; CMD_PIV_* live via dispatch_piv_command; default builds answer NotPresent via InertPivBackend; SwPivBackend selected under --features dev-piv; first consumer = shell fde-test round-trip through SYS_VERIFY_VOLUME_HEADER) x/a/r user/key-store-service/ ADR-032
Virtio-blk driver Done (Phase 4b, dual-endpoint user/kernel) x/a/r user/virtio-blk/ ADR-010
Virtio-net driver Done (Phase 4c, modern virtio-pci on x86_64; legacy MMIO on aarch64+riscv64; live in boot, drives udp-stack NTP demo) x/a/r user/virtio-net/
Intel I219-LM driver Scaffolded, untested on hardware x user/i219-net/
UDP/IP stack (ARP/IPv4/UDP + NTP demo) Done x/a user/udp-stack/
Shell (arcobj CLI incl.) Done x/a/r user/shell/
Spawn / WaitTask syscalls Done x/a/r src/syscalls/dispatcher.rs
Wall-clock subsystem (SetWallclock / GetWallclock syscalls + SetWallclock cap) Done (Phase 4d) x/a/r src/time/wallclock.rs, src/syscalls/dispatcher.rs (handle_set_wallclock / handle_get_wallclock) ADR-022
Service clusters End-to-end wiring live: kernel ABI (SYS_CLUSTER_* 44–47), cluster_policy (caps stub + RenderingLimb→revoke departure), exit-path auto-revoke, CreateCluster boot grant, libsys cluster_* wrappers, compositor calls cluster_create at startup, scanout drivers (virtio-gpu + limine) call cluster_join on RegisterCompositor receipt. Cluster_id threaded through extended RegisterCompositor wire format. Cap-table population + virtio-input membership + handshake-strip pending substrate (SYS_CHANNEL_CREATE cluster_id arg + virtio-input inbound endpoint). x/a src/ipc/cluster*.rs, src/syscalls/dispatcher.rs, user/libsys/, user/libscanout/, user/compositor/, user/scanout-virtio-gpu/, user/scanout-limine/ ADR-027
PCI bus discovery Done (x86_64 = port I/O mechanism 1; aarch64 + riscv64 = ECAM via init_ecam; riscv64 also synthesizes virtio-mmio via register_virtio_mmio) x/a/r src/pci/
Device syscalls (MapMmio/AllocDma/DeviceInfo/PortIo/VirtioModernCaps) Done x/a/r src/syscalls/dispatcher.rs
Bootloader abstraction (BootInfo + src/boot/) Done (Phase GUI-0) x/a/r src/boot/ ADR-011
SYS_MAP_FRAMEBUFFER + graphics capabilities Done (Phase GUI-0) x/a src/syscalls/dispatcher.rs, src/ipc/capability.rs ADR-011
Compositor (incl. Input-1 event routing) Done (Scanout-2/3 + Input-1) x/a user/compositor/ ADR-014
scanout-virtio-gpu driver (default) Done (Scanout-4.b; aarch64 enabled by kernel ECAM 2026-04-22) x/a user/scanout-virtio-gpu/ ADR-014
scanout-limine driver (fallback, not in default boot) Done x/a user/scanout-limine/ ADR-014
pong v0 (first-party app, default GUI boot module) Done x user/pong/ ADR-011, ADR-012, ADR-014
terminal-window v0 (boot splash → recede-to-watermark → identity-aware shell with ANSI SGR per-cell color) Done x user/terminal-window/ ADR-011, ADR-022
worm v0 (first-party app, buildable; default GUI on aarch64 run-aarch64-gui + regression on x86_64) Done x/a user/worm/ ADR-011, ADR-012
tree v0 (first-party app, buildable; retained for regression) Done x user/tree/ ADR-011, ADR-012, ADR-014
hello-window Done, buildable; retained as protocol regression (replaced by treewormpong as default GUI boot module) x user/hello-window/ ADR-011, ADR-014
libgui::FrameClock Done x/a/r (host-testable) user/libgui/src/frame_clock.rs ADR-011
libgui v0 (GUI client library) Done x/a/r (buildable; GUI only runs where scanout driver exists) user/libgui/ ADR-011
libinput-proto (wire format) Done (Input-0) x/a/r user/libinput-proto/ ADR-012
virtio-input driver Done (Input-1) x/a user/virtio-input/ ADR-012
USB host stack (xHCI) In progress (Stream B B-vii) — CCID layer split out. usb-host now stays controller-only (enumeration, addressing, control + bulk transfers) and exposes a BULK_OUT / BULK_IN IPC ABI on endpoint 31. New user/ccid/ boot module on endpoint 33 frames PC_to_RDR_* / RDR_to_PC_* messages via the new cambios-ccid-proto wire-format crate. Boot smoke: ccid drives PC_to_RDR_GetSlotStatus → RDR_to_PC_SlotStatus through usb-host's IPC; QEMU usb-ccid reports ICC absent (no chardev backend). Next: B-viii PIV applet APDUs (needs a real smart card or chardev-backed CCID emulation; QEMU-only validation ends at B-vii). x/a/r (buildable; runtime detection requires -device qemu-xhci) user/usb-host/, user/ccid/, cambios-ccid-proto/, src/pci/mod.rs (prog_if + post-scan device enable)
APIC + I/O APIC + SMP IPI Done x src/arch/x86_64/
GIC v3 + ARM Generic Timer Done a src/arch/aarch64/
PLIC + SBI timer + SBI IPI Done r src/arch/riscv64/ ADR-013
TLB shootdown Done (x86 vector-IPI / ARM TLBI broadcast / RISC-V SBI IPI) x/a/r src/arch/*/tlb.rs
Process lifecycle cleanup Done (Phase 3.2d.ii; kernel stack free deferred) x/a/r src/syscalls/dispatcher.rs, src/process.rs
USB boot tooling Done (make img-usb + make usb DEVICE=...) x Makefile
Formal verification (Kani) Started 2026-04-16. Live across 6 proof crates, 47 passing harnesses: BuddyAllocator::free reserved-prefix (1); ELF header parser (7; fixed 6 integer-overflow sites); FrameAllocator (10; fixed 2 overflow sites with saturating_add; reserve_region overflow covered by unit tests where CBMC's budget blew); CapabilityManager (12; Tier A ProcessCapabilities invariants + Tier B cross-process on a 3-slot manager); UserSlice validators (12; ADR-020 UserReadSlice/UserWriteSlice exhaustive over (addr, len) × CR3 state); DTB parser (5; fixed 2 attempt to add with overflow sites in be_u32_at/be_u64_at with checked_add). Walker proof (P-DTB-6) and ELF verify_binary cross-cutting proof both deferred — CBMC budget intractable; targeted by the planned Kani→Verus pivot. Compositor protocol parser deferred until scanout settles past Scanout-4.c. The gap-map between proven and aspirational claims is verification/CLAIMS.md. verification/{buddy,elf,frame,capability,userslice,dtb}-proofs/ ADR-000 § Divergence, verification/CLAIMS.md
AArch64 SMP timer on AP Gap: PPI 30 not firing on second CPU under QEMU virt. Single-CPU works. a
DHCP client Paused (pre-work in udp-stack; waiting on channel architecture consumer) partial in user/udp-stack/
DNS / TCP / Yggdrasil mesh / TLS / VFS / USB HID / DID resolution / identity revocation Planned identity.md, various ADRs
AI pre-exec analysis / behavioral anomaly detection / Win32 compat Planned (post-v1) CambiOS.md, ADR-016, ADR-017

Roadmap

Identity / storage phases

Source: identity.md, FS-and-ID-design-plan.md.

Phase Goal Status
0 Identity primitives in kernel + RAM ObjectStore (stamps identity on every IPC; every object has author + owner) Done
1 Real cryptography: Blake3, Ed25519, signed ELF, key-store service Done
1B YubiKey-derived bootstrap pubkey compiled into kernel Done
1C Key-store degraded mode + signed ObjectStore puts + identity gate (no unsigned fallback) Done
2A First user-space hardware driver (virtio-net) Done
2B First user-space network service (UDP/IP + NTP demo) Done
3 Architecture substrate: revocation (3.1), CreateProcess cap (3.2b), ProcessId generation counters (3.2c), channels (3.2d), audit (3.3), policy service (3.4b) Done
4 Persistent storage: virtio-blk + disk ObjectStore + arcobj CLI Done
5 Identity-routed Yggdrasil networking Planned
6 Biometric commitment + key recovery Planned (post-v1)
7 SSB bridge Planned (post-v1)

v1 target

Interactive, network-capable, identity-rooted OS running on real hardware with persistent storage. Items are dependency-ordered. Blocker = bare-metal hardware bring-up.

# Item Status
1 Shell Done
2 USB boot tooling Done (untested on target hardware)
3 Intel I219-LM NIC driver Scaffolded (untested on target hardware)
4 DHCP client Paused
5 DNS resolver Planned
6 TCP stack Planned
7 Virtio-blk driver Done
8 Persistent ObjectStore Done
9 arcobj CLI Done
10 Yggdrasil peer service Planned

RISC-V arch port

Parity-target with x86_64 / AArch64. All phases landed as of 2026-04-19. Source: ADR-013.

Phase Goal Status
R-0 Build infra + tri-arch gate Done (2026-04-15)
R-1 First serial output, kmain_riscv64 banner Done (2026-04-16)
R-2 Sv48 higher-half, DTB parser, frame allocator + heap Done (2026-04-16)
R-3 Trap vector, SBI timer, PLIC, context switch, 100 Hz preemption Done (2026-04-18)
R-4 U-mode transition via sscratch/tp swap, ELF EM_RISCV Done (2026-04-18)
R-5 SMP (SBI HSM), cross-hart TLB shootdown (SBI IPI) Done (2026-04-18/19)
R-6 Service parity: virtio-mmio transport, -initrd signed modules, 5 boot services Done (2026-04-19)

Test coverage

Total: 561 on x86_64-apple-darwin. Run RUST_MIN_STACK=8388608 cargo test --lib --target x86_64-apple-darwin, or make stats for the current number.

Major categories (approximate; breakdown drifts faster than the total):

Area Tests
Scheduler 35
Capability manager 40
IPC (interceptor, sender_principal, sync channel) 17
Channel manager 29
Process lifecycle cleanup 3
ELF verifier (incl. signed binary) 14
ObjectStore types + crypto 21
RamObjectStore 12
BlockDevice abstraction 11
DiskObjectStore (incl. reboot preservation) 30
Memory subsystem (buddy, frame, heap, paging, contiguous) ~37
Tier configuration 16
Kernel object table region 5
Audit (staging + events + ring/drain) 44
Syscall dispatcher (Cuts 1/2/3a) 40
Syscalls user_slice (ADR-020 Phase A) 26
Boot adapter (BootInfo + initrd parser) 8
PCI virtio-modern caps 11
AArch64 portable logic 12
Timer, ProcessTable, VMA tracker, syscall args, other ~127

User-space crates have their own host tests: libgui v0 ships 26 (drawing primitives + TileGrid + font coverage); libinput-proto ships 8 (wire format, round-trips, signature preservation); libgui-proto ships 13 (including input_event_roundtrip). Run cargo test --lib --target x86_64-apple-darwin from each crate directory.

Known issues (active)

  • AArch64 SMP timer on AP: PPI 30 not firing on second CPU under QEMU virt. Single-CPU works fully. Likely QEMU config or missing GIC redistributor step on the AP path. Investigation pending.
  • AArch64 device IRQ routing: GIC enable_spi / set_spi_trigger exist but are not called from the boot path or handle_wait_irq. No device IRQs on aarch64 today. Revisit when: first aarch64 path needs device IRQs (likely a polling→IRQ-driven transition in virtio-blk, or resolution of the AP-timer gap enabling PL011 RX to drive a consumer).
  • ELF loader overlapping-segment permissions: If two PT_LOAD segments share a page with different permissions, the first segment's permissions win. Worked around in user-space linker scripts via ALIGN(4096) before .data.
  • Kernel stack not freed on process exit: handle_exit now performs full lifecycle cleanup (Phase 3.2d.ii), but the 32 KiB kernel stack per task remains a bounded leak — can't free the stack you're running on. Requires scheduler-level deferred-dealloc. Bounded by num_slots × 32 KiB (~6.4 MiB worst case).
  • Clippy warnings (~125): ~67 multiple_unsafe_ops_per_block in arch code, ~25 missing // SAFETY:, ~12 static_mut_refs (Rust 2024 migration — IDT/GDT/TSS patterns need UnsafeCell or addr_of!), ~20 new_without_default. Dedicated pass scheduled before static_mut deprecation becomes a hard error.
  • Pre-existing driver warnings in user/i219-net/: dead_code / unused_imports from scaffolded state. Not correctness issues; clean up on next real-hardware bring-up.
  • Virtio-net TX on QEMU TCG: QEMU defers virtio TX to its event loop, which runs during guest hlt. The UDP stack's ARP retry/timeout logic doesn't yet exploit this fully.

Cross-references