Current Status
capOS boots on x86_64 QEMU and on a real Google Compute Engine instance. It
starts a standalone init from a Cap’n Proto boot manifest and runs a native
shell plus a resident service over typed capabilities. No capOS endpoint is
publicly exposed, and no path in the tree carries production authority.
A Validation: line names the gates that prove the claims above it. Those
gates run locally: a candidate’s full gate set has to pass before it can
integrate, and agents and the operator run focused gates on demand. What no
gate establishes is currency. Nothing re-runs the whole set on a schedule, and
each run’s output lives in its task worktree and is deleted with it, so a
Validation: line means “this passed when the work was accepted”, not “this
passes today”. Re-run the gate before relying on a claim.
Default Boot
make run builds the ISO from the default init-owned system.cue and boots it
in QEMU. On that boot:
- The kernel validates the kernel-owned boot boundary and boot-launches the
standalone
initprocess only. The service graph, login, session, and broker flow all live in userspace. initstarts exactly two services from the manifest graph: the residentchat-serverendpoint service and the foregroundcapos-shell.- The shell mints its own anonymous
UserSessionon boot.loginprompts forusername>then hiddenpassword>, validates the selected bootstrap account throughCredentialStoreandSessionManager, and upgrades the session in place to a broker-issued operator bundle.setupcan create a volatile local operator credential and then follows the same upgrade path. - The adventure, paperclips, and chat-client demos are manifest binaries,
not started services. The MOTD carries the exact
run/spawnlines an operator uses to launch them afterlogin. make runforwards127.0.0.1:2327to guest port 2327 and127.0.0.1:8080to guest port 8080. Neither the remote-session CapSet gateway nor the Web UI is a default service, so those forwards reach nothing on a stock boot. Both binaries stay in the manifest so an operator overlay can rewire them onto the Phase C userspace network stack.
Validation: make test-default-boot, which also asserts that the two
retired-listener services are never spawned.
Session expiry
Default password-authenticated local operator sessions have a bounded absolute
expiry derived from the configured profile lifetime – 15 minutes by default –
in addition to logout, terminal/connection/process-tree close, or administrator
revocation. SessionLifetimeConfig::UNBOUNDED_MS (u64::MAX) is the reserved
explicit opt-in for a non-expiring profile. Absolute expiry uses saturating
addition, so a sufficiently large finite configured lifetime also reaches
u64::MAX and behaves as non-expiring; such a value is not a safe way to
express bounded policy.
Local password login is username-aware on the ordinary foreground shell path. Durable multi-account credential storage remains future work: the seeded accounts are manifest-sourced.
Implemented
Boot and kernel baseline
- Limine boots the x86_64 kernel in QEMU. The kernel initializes dual UART output, GDT, IDT, LAPIC, syscall MSRs, memory management, page tables, heap allocation, and the global capability registry. The legacy PIC/PIT path remains as a fallback when LAPIC timer setup or PIT-based calibration is unavailable.
- The kernel creates its own page tables with per-section permissions and keeps the higher-half direct map for physical memory access. SMEP/SMAP are enabled when the QEMU CPU advertises support.
- User page-table map, unmap, and protect operations route through a TLB shootdown helper keyed by address-space CPU residency. Remote targets get pending full-TLB flush generations plus vector-49 IPIs, and the sender waits for observed target completion after ring dispatch releases address-space, cap-table, and scratch locks. Deferred queue slots are reserved before page-table mutation, and drains flush the current CPU before waiting.
- AP cpu=1 can own scheduler and user execution under
-smp 2. APs register theirPerCpurecords, program LAPIC timers from the BSP calibration, update APTSS.RSP0during context switches, and enter the scheduler from the AP idle loop. One scheduler owner is kept: while AP cpu=1 is online with a programmed timer, the BSP stays in kernel idle so the process-wide capability ring is not executed concurrently. - Independent CPU-bound worker processes scale across two vCPUs.
make test-smp-process-scalebuilds its own ISO, runs repeated-smp 1,-smp 2, and best-effort 4-vCPU cases, stores raw serial logs undertarget/smp-process-scale/<timestamp>/, and enforces a 1.6x median speedup threshold when KVM-backed evidence is available. Measured 1-to-4 scaling is weaker than 1-to-2, so only the 1-to-2 gate is claimed.
Validation: cargo build --features qemu, make run-smoke,
make test-smp-process-scale.
Process and userspace runtime
- Processes have isolated address spaces, one or more internal
Threadrecords with per-thread kernel stacks and saved CPU context, CapSet bootstrap pages, capability rings, and local capability tables. - ELF loading supports static
no_stduserspace binaries and TLS setup.targets/x86_64-unknown-capos.jsondefines the capOS userspace target for bootedinit,demos,shell, andcapos-rtbuilds; the kernel default target remainsx86_64-unknown-none. capos-rtowns the userspace entry path, allocator initialization, ring-client access, typed clients, result-cap parsing, and owned-handle release. It is the only source owner for the userspace_start, panic, global allocator, raw syscall, andcapos_rt_mainhandoff surfaces; a source check guards that split.ProcessRingReactorgives process-scoped multithreaded calls one runtime-ring CQ drainer. Generation-checkeduser_dataslots route completions to callers, while each caller blocks on ParkSpace through its own thread ring and never consumes the runtime CQ. A same-process ring kick makes acap_enter-blocked drainer retry newly published SQ work.- In-process threading is implemented through
ThreadSpawner/ThreadHandlecaps andThreadControl.exitThread: create, join, detach, self-join rejection, exit-code observation, and last-thread process exit. PrivateParkSpacewait/wake carries timeout, wake, and reserved-waiter completion semantics.SharedParkSpacepark-words remain future work. - The schema-bound host analyzer for bootstrap capability expectations, package
emission of deterministic
ServiceRevisionanalyzer inputs, and the launcher-visible startup-failure path are implemented. The artifacts remain host-only diagnostics and do not enter boot or launch authorization.
Validation: tools/check-userspace-runtime-surface.sh, make capos-rt-check,
make init-capos-build, make demos-capos-build, make shell-capos-build,
make capos-rt-capos-build, make run-smoke, make run-spawn,
make test-runtime-ring-reactor.
Capability ring and IPC
- The shared ring ABI carries CALL, RETURN, RECV, RELEASE, NOP, CANCEL, compact ParkSpace PARK/UNPARK, and owner-only service-facet minting without a spawn. Unauthorized or failed minting installs nothing. FINISH stays reserved for the future system capnp transport. The wire contract defines each opcode’s fields.
cap_enterprocesses submissions and can block until completions arrive or a timeout expires. Endpoints route ring-native IPC between processes, and direct IPC handoff lets a blocked receiver run before unrelated round-robin work once a matching CALL arrives.- One-hop promise pipelining is implemented over kernel-served and endpoint antecedents: a dependent call dispatches against a capability returned by another vat with no userspace round trip. Chains deeper than one hop remain out of scope until the ABI grows a distinct answer-allocation field.
- Transport errors and application exceptions surface through CQEs and typed
runtime client errors. Ordinary capability implementation errors, revoked
ordinary/endpoint use, live endpoint target errors after endpoint
identification, and endpoint RETURN application failures use serialized
CapExceptionpayloads when a caller result buffer can safely receive one. No-payload application failures reportCAP_ERR_APPLICATION_EXCEPTION_TRUNCATED; malformed transport metadata and unsafe result-buffer paths remain transport errors. debug_tapbuilds export metadata-onlyringtap:records for observed SQEs and posted CQEs on the QEMU/debug UART. The format is fixed, bounded, and deliberately recordspayload_len = 0until a separate payload-capture authority lands.tools/ringtap-viewer/parses those logs into SQE/CQE summaries.
Validation: cargo test-ring-loom, make run-smoke, make run-spawn,
make run-smoke CARGO_FLAGS='--features debug_tap',
make test-ringtap-failing-call, make run-promise-pipeline,
make test-service-facet-mint.
Kernel capabilities
The implemented capabilities cover console and terminal output, boot package and manual, frame and memory objects, virtual memory, endpoints, timers and wall clock, entropy, threads and park spaces, process spawning and control, pipes, notifications, sessions and credentials, audit and log surfaces, block devices and filesystems, stores and namespaces, and device authority. Per-cap methods, grant sources, fail-closed rules, and proof targets are in Repository Map.
Per-process controls bound endpoint queues and scratch; ResourceLedger
reserves cap slots, virtual pages, and frame grants before mutation, releasing
on rollback or teardown. Exhaustion leaves calls usable and reports
spawn OOM as overload. This is containment, not fairness; Resource
Governance separates enforced from policy
limits.
AuditLog reports ceilings, usage, and refusals but has no per-producer charge,
protected lane, persistence, or retained records.
Validation: make run-smoke, make test-memoryobject-shared, make run-spawn,
make test-shell, make test-terminal, cargo test-lib,
make test-untrusted-exhaustion.
Capability transfer and release
- IPC CALL and RETURN support sideband transfer descriptors. Copy and move transfer are implemented, and move transfer reserves the sender slot until destination insertion and commit.
- Transfer result caps carry interface ids to userspace.
CAP_OP_RELEASEremoves local capability-table slots. Runtime owned-handle drop queues one local release, andRuntime::flush_releases()forces queued releases when code cannot wait for the next ring-client acquisition or drop.
Validation: cargo test-lib, make run-smoke.
Manifest tooling and focused proofs
tools/mkmanifest turns a CUE manifest into a Cap’n Proto boot manifest. The
build uses repo-pinned Cap’n Proto and CUE tool paths through the Makefile;
direct mkmanifest invocation also rejects missing, unpinned, or
version-mismatched CUE compilers. mkmanifest cue-to-capnp extends the same
pinned-tool policy to general CUE-authored data messages.
Every focused proof boots its own manifest under manifests/ rather than the
default graph, so a proof grants exactly the caps it needs and nothing else.
Demo logic stays out of shell builtins and reaches its service only through
explicit StdIO plus endpoint grants, and the accelerated Paperclips proof
first verifies that normal server authority rejects fast-forward and a forged
proof_accelerator: @timer grant. demos/service-common/ holds the shared
caller-session endpoint loop and chat actor helpers those services reuse.
Validation: cargo test-mkmanifest, make generated-code-check,
make run-smoke, make run-spawn, make test-shell, make test-chat,
make test-adventure, make test-paperclips.
System manual
Boot-packaged Manual provides read-only shell, Web UI, and API access. It has 14 section-1 pages, three in section 7; generated section 2 covers every interface. Sections 3/5/8 are empty. One build ID grounds the corpus, not ISO contents.
Validation: make test-system-manual-smoke.
Storage and persistence
Storage is capability-scoped: a cap reaches one device, one mount, or one directory, and no method widens it. Attenuation is structural rather than a rights flag, so a read-only export cannot be upgraded at runtime.
- Block devices.
BlockDevice(readBlocks/writeBlocks/info/flush) is scoped to a single device index. The production build resolves it to the userspace-brokered NVMe arm; theqemubuild routes bounded sector I/O to the kernel-owned virtio-blk driver as a named fixture, not production storage. Gates:make test-virtio-blk,make test-multi-virtio-blk. - Transactional record store. The
CAPOSRS1WAL provides atomic multi-record commit frames over an abstract block seam, checksum-chained bounded-prefix recovery with ordered tail invalidation, fail-stop remount after indeterminate I/O, fail-closed geometry validation, secondary-key lookup, and a compaction-capable A/B superblock format. In-system proofs cover reboot durability and bounded forced-poweroff torn-write recovery across three boots of one disk image (make test-record-store,make test-record-store-compaction). - Filesystems and key-value store. Read-only
CAPOSRO1, FAT32 read, and writableCAPOSWF1serveDirectory/Filecapabilities; theCAPOSST1disk store serves aStore(put/get/has/delete) whose durability commit point is the superblock rewrite. All back onto virtio-blk or a brokered NVMe window, all fail closed on a malformed image, and their mount parsers are fuzzed. Gates includemake test-storage-fsandmake test-storage-persist. - Host directory passthrough (development fixture). virtio-9p serves
structurally separate read-only and opt-in writable
Directory/Filecapabilities over a bounded 9P2000.L subset, including host-verified multi-chunk writes, a single spawn-granted writer whose writable results are non-transferable, and WASI payload hot reload. This is development infrastructure, not a production storage path; hostile-server reclaim,Tfsynccrash durability, and host rename TOCTOU remain open residuals. Gates:make test-virtio-9p-fs,make test-virtio-9p-write,make test-wasi-9p-reload. - Task coordinator persistence. The Endpoint-serving coordinator restores
and writes through canonical current/temp/backup files when granted a
writable state
Directory, and stays in-memory otherwise. Its bounded 9p path fails closed on malformed or ambiguous state and preserves fencing generations, and the Phase C network process serves the HTTP/JSON task API while only its coordinator child holds the writable state cap. That 9p path is explicitly weaker than the fencedCAPOSRS1WAL overBlockDeviceand is not production storage durability. Gates:make test-task-coordinator-9p,make test-task-coordinator-api-9p,make test-task-coordinator-persist. - Installable system. Persistent data-region mount, config-overlay
compose/merge fallback, generation and rollback machinery, installable disk
packaging, target-disk install, first-boot provision, and update/rollback are
landed for a bounded local/QEMU contract.
Namespaceremains RAM-only, and no secure boot, image signing, or production release authority is claimed. Gates:make test-installable-install,make test-installable-provision,make test-installable-update.
Boot sources
The kernel boots from more than the default ISO path:
- ISO under BIOS, the default (
make run). - UEFI, booted through OVMF (
make test-uefi). - A GPT disk image with an ESP, under both UEFI and BIOS
(
make test-disk,make test-disk-bios). The same image builder produces the GCE-importable tarball. - A digest-verified ISO binary registry. Under the
boot_isofeature the kernel builds a validated(name, lba, size)registry from the ISO 9660 directory through its own ATAPI driver and loads binaries from the ISO rather than from embedded manifest blobs, verifying each against the manifest’s recorded SHA-256 before use.make test-boot-isoproves the load path,make test-boot-iso-readthe on-demand read, andmake test-boot-iso-failclosedthat a digest mismatch refuses the binary.
Design: Boot Flow.
Validation: make test-uefi, make test-disk, make test-disk-bios,
make test-boot-iso, make test-boot-iso-read,
make test-boot-iso-failclosed.
Host-side SDKs
The language support below covers what runs inside capOS. The SDKs are the other axis: what talks to a running instance from another machine.
Two packages, both named capos, are published and speak deliberately
different transports. The Python SDK (capos-python/, a pyo3 binding over
tools/remote-session-client) reaches the remote-session CapSet gateway on
guest port 2327 and is capability forwarding, bounded by session login, broker
profiles, and the session’s CapSet. The JavaScript SDK (capos-js/, pure
TypeScript with no runtime dependencies) reaches the web bridge HTTP API on
guest port 8080 and is a data bridge that carries DTOs and never an invokable
capability. Both published surfaces cover login, session metadata, MOTD, and
CapSet listing; build-identity calls exist in the source tree ahead of a
release.
Design: Host-Side SDKs, Capability Model.
Validation: make capos-sdk-check, make sdk-publish-dry-run.
Programming language support
Native Rust
Native capOS Rust is the only implemented booted Rust language path:
#![no_std], alloc, capos-rt, static ELF binaries, and the
targets/x86_64-unknown-capos.json custom target. Rust std is not supported.
Native C and POSIX
C boots through the libcapos C-substrate (make test-c-hello exercises
Console + Timer + EntropySource + a 4 KiB anonymous VM round trip) and through
the POSIX adapter. Both bypass WASI – they are static ELF binaries linked
against libcapos.a and, for POSIX, libcapos_posix.a. The implemented POSIX
surface is covered by focused make test-posix-* targets plus
make run-posix-file, and spans:
- pipes, fork-for-exec, direct
posix_spawn, minimal file actions,read, andwaitpidoverProcessSpawner/Pipe; - Console-backed stdio, where
read(0, ...)stays closed without a stdin grant; open,write,lseek,read,opendir,readdir, andclosedirover a granted rootDirectory;- a focused printf/string/numeric/ctype subset;
- Timer-backed
time,nanosleep, andsleepplus documented fail-closed signal-delivery stubs.
posix_spawn() accepts argv/envp for source compatibility but does not deliver
them until LaunchParameters/environment support lands. Broader C/libcapos
surface and full POSIX adapter scope remain future design. The POSIX DNS
resolver smoke is retired with the kernel socket owner and must be rebuilt on
the userspace network stack before it counts as validation.
WASI
Sandboxed wasm32-wasi is the booted WASI-hosted language path. The
wasm-host userspace binary (capos-wasm/, over vendored wasmi) hosts modules
whose wasi_snapshot_preview1 imports are backed by typed capOS capabilities:
- Console, Timer, and BootPackage; a per-instance argv text grant; a bounded
environment text grant through
initConfig.init.wasiEnv; and an optionalEntropySourcecap looked up under the well-known CapSet namerandom. - Filesystem: the manifest-granted root
Directoryis installed as a preopened fd. Payloads write and read back throughpath_open/fd_write/fd_close/ re-open/fd_read, and the preopen sandbox refuses absolute paths and parent-escape..segments. - Clocks:
clock_time_get(CLOCKID_REALTIME)reads UTC through a manifest-granted kernelWallClockcap.WallClockis a manifest-selectable boot-base UTC source with a historical fixed-base fallback; both sources remainuntrusted, so timestamps are not cross-reboot comparable or security-grade validity evidence. poll_oneoffservicesCLOCKID_MONOTONICandCLOCKID_REALTIMEclock subscriptions. Fd-readiness subscriptions still fail closedERRNO_NOTSUP.
Every granted capability has a matching ungranted refusal gate: without the
grant the corresponding import returns ERRNO_NOSYS or ERRNO_NOTSUP without
entering the kernel. Storage and socket imports (path_open, fd_read,
sock_send, sock_recv) keep their fail-closed gate.
Other languages
C++, Go, Python, JavaScript/TypeScript, and full POSIX shell/utilities are not
implemented as supported capOS runtime paths. Lua has a Phase 0 in-tree
capability-aware Lua-subset interpreter under demos/lua-smoke/
(make test-lua-smoke); it validates the long-term capability-userdata host
API design but is not a PUC Lua dialect-compatible runner.
Partially Implemented
Focused init-owned spawn and measurement boots
The focused init-owned spawn path is make run-spawn. There the kernel
boot-launches init with Console, BootPackage, and ProcessSpawner. Parent
endpoint facets used for later service-sourced imports are returned by
ProcessSpawner during child spawn, not granted at boot. init performs
metadata-only manifest validation, resolves kernel and service cap sources,
spawns children through ProcessSpawner, records exports, waits for children,
and reports failures through Console output.
Measurement startup follows the same boundary. make test-measure uses
manifests/system-measure.cue, where the kernel boots standalone init with
Console, BootPackage, and ProcessSpawner, and init spawns ring-nop and
thread-lifecycle with their measurement-only caps. Kernel bootstrap loads
only initConfig.init and validates only the kernel-owned manifest boundary;
mkmanifest and init own initConfig.services graph validation.
Recorded dispatch, schema-parsing, round-trip, throughput, and receiver-
selection measurements live in Benchmarks and the tracked
evidence bundles under docs/benchmark-data/. They are nested-QEMU/KVM
observations, not hardware or production-performance claims.
Scheduling
The scheduler is weighted fair queuing over generation-checked ThreadRef
per-CPU run queues, with per-CPU current and handoff slots, bounded stealing,
eligibility-resolved wake placement, and per-thread runtime and virtual-runtime
charging. Weight is a relative share among runnable work, and a migration
preserves the thread’s accumulated fair-share position rather than resetting
it. The runtime, virtual-runtime, and last-start ledger is unconditional
because WFQ and budget charging depend on it; context-switch, migration, and
placement counters remain measure-only diagnostics.
Landed on top of that baseline:
SchedulingContext: bind, revoke with generation identity, budget enforcement, endpoint donation and return, depletion notification, and a session-logout hook that makes a stale context fail closed.CpuIsolationLease: pool grants and leases with timeout-based auto-revoke driven by a recordedleaseLifetimeNs.- Bounded SQPOLL ring mode with periodic-tick service and bounded producer-wake, plus the clockevent/deadline substrate underneath it.
- Per-thread saturation inputs – preemption, voluntary-block, and
runnable-but-not-running time – exported through
SchedulingPolicyCap.snapshotand consumed by the AutoNoHz heuristic.
Tickless idle is not on for runnable contention: automatic nohz activation still waits on a proof that SQPOLL and poller progress do not depend on periodic scheduler ticks, and on the remaining network-polling, IRQ-affinity, and housekeeping dependencies. Priority and policy scheduling are deliberately deferred until the authority and IPC semantics settle. The recorded 1-to-4 thread-scale figure was manually accepted; the harness-enforced gates remain the 1-to-2 work and total speedups.
Design: Scheduling.
Validation: make test-thread-fairness,
make test-thread-fairness-interactive,
make test-thread-fairness-weight-change,
make test-thread-fairness-sleeper-floor, make run-scheduling-context,
make test-scheduler-cpu-isolation-lease,
make test-scheduler-cpu-isolation-pool-grant,
make test-scheduler-generic-sqpoll-nohz,
make test-scheduler-autonohz-policy-service, make test-thread-scale.
TLS and key custody
capOS terminates TLS 1.3 itself. capos-tls/ is a workspace crate carrying the
handshake core, X.509 verification and trust, DER, certificate store, ACME,
renewal, self-signed issuance, and a TlsServerSigner seam that keeps the
handshake away from private key bytes.
- Server termination:
remote-session-web-uiserves the Web UI over a TLS endpoint it terminates. Termination is a boot-manifest decision – atls_terminated_endpointmarker makes the network stack forward the marker and grant anEntropySource– and the service refuses to boot on either marker without the other, or on a request for termination in a build that links no TLS stack. The proof drives a host OpenSSL client over hostfwd through a full handshake, pins the served leaf against the fingerprint the guest logged, fetches a Web UI response, and confirms plaintext HTTP is refused on the same port. The TLS stack is a default-offtls-endpointfeature, so the plain-HTTP path links none of it. - Client handshake: a userspace process completes a TLS 1.3 client
handshake over the Phase C
TcpSocketcap. - Alert path, ALPN, and close behaviour are landed, including the RFC 8446 §6 alert path and the epoch and deferred-accept lifecycle around a connection that never completed a handshake.
- Key custody:
PrivateKey/PublicKeyRAM signing, handle-basedKeyVaultgeneration, open, list and destroy, and a development-onlyKeySourcebootstrap for local proofs. - ACME account and order flow with certificate-store rotation and renewal.
The bounds are real. Key custody is RAM-only and both chains are
development-signed; there is no public CA, DNS, or production key custody. The
validity clock is the untrusted sampled-base WallClock, so certificate
validity is not security-grade evidence. And this is all local and QEMU: a
capOS-terminated public endpoint is a separate on-hold step.
Design: Certificates and TLS.
Validation: make capos-tls-test, make run-cloud-tls-webui-terminated,
make test-cloud-tls-client-handshake,
make test-cloud-tls-webui-deferred-accept,
make test-cloud-tls-webui-epoch-drain-latch,
make test-crypto-keyvault-custody, make test-crypto-symmetric-key,
make test-crypto-keysource-bootstrap, make webui-login-peer-logic-test.
Remote-session CapSet gateway
The gateway is not started by the default manifest; it runs under its own focused manifests and proofs. Its connection table, per-slot aggregate charge, per-principal login table, deadline thresholds, selection-cycle work, and close-drain behavior are enforced ledger contracts with a bounded, secret-free readback.
The readback is read-side evidence. It measures rather than bounds head-of-line blocking: there is no independent reaper, no protected progress for unrelated slots, no synchronous-work budget, and no calibrated attacker/defender CPU ratio. One unauthenticated peer can still force credential hashing and serial multi-second waits that delay other slots and their timeout recovery.
Each bound, its derivation, its effective value, its overload and recovery behavior, and its open descriptor gaps are in the CapSet gateway connection descriptor inventory.
The host client has a loopback Web bridge and Tauri check/dev wrapper. Browsers get view models, not capabilities; neither tool is packaged or has a current guest gate.
Validation: make remote-session-tauri-policy-smoke.
SSH shell gateway
The authority prerequisites and the fixture authentication path that precede an encrypted SSH transport are implemented, each with a bounded QEMU smoke:
- Host-key fixture signing (
make test-ssh-host-key): a development-only non-productionSshHostKeycap returns public metadata, signs bounded fixture exchange hashes, fails wrong-algorithm requests closed, and does not leak the private seed. - Authorized-key lookup (
make test-ssh-authorized-key): a manifest-seededAuthorizedKeyStoreaccepts configuredssh-ed25519keys mapped to seed-account principals and denies unknown, disabled, and unsupported-algorithm keys. - Public-key session minting (
make test-ssh-public-key-session,make test-ssh-public-key-auth):SessionManager.sshPublicKeyverifies a bounded signature over fixture authentication bytes before minting apublicKeyUserSession, and logs stable audit reason codes for each denial path.UserSession.auditContextfails closed after logout. - Unsupported feature policy (
make test-ssh-feature-policy): thecapos-config::ssh_policysurface classifies password auth, exec, SFTP, direct-tcpip, agent/X11 forwarding, env import, and extra session or shell channels into stable audit reason codes. - Restricted shell launcher (
make test-restricted-shell-launcher): a manifest-declaredRestrictedShellLauncherlaunches onlycapos-shell, injects supplied terminal/session caps plus child-local stdio, rejects session/profile mismatch and dangerous pass-through grants, and strips hidden process-supervision result caps.
Encrypted SSH packet transport, OpenSSH-compatible key exchange and channel
handling, full userauth transcript validation, channel binding,
TerminalSessionFromByteStream wiring, a terminal host over the userspace
network stack, and a production OpenSSH harness remain open. The bounded
terminal-host proof is retired: it sat on the removed qemu-only kernel socket
owner, and any replacement must target the userspace network stack. The landed
proofs use development/fixture key material; they are not a production SSH
service and are not safe for non-loopback deployment.
Hardware and device authority
IOMMU and DMA backend
Backend selection is a runtime, fail-closed kernel decision: direct IOMMU remapping only when a probe verifies usable hardware, otherwise kernel-owned bounce buffers.
IOMMU reporting is currently policy-only. Malformed DMAR/IVRS structures fail closed. DMAR DRHD include-all or single-hop PCI endpoint device-scope metadata can mark retained DMA-capable PCI functions as IOMMU-attached; bridge and multi-hop scopes stay diagnostic-only until PCI topology traversal exists, and include-all fallback fails closed when retained coverage metadata is capped. Direct DMA remains blocked with zero trusted domains, remapping tables are not programmed, exported device addresses would be IOVA-only, host physical addresses are not user-visible, and every retained DMA-capable prototype function requires bounce buffering.
Bounded DMA, MMIO, and interrupt grants
Bounded manifest grants exist for DMAPool, DeviceMmio, and Interrupt.
Reachable today: DeviceMmio read-only userspace mapping over boot-preseeded
BAR pages plus brokered read32 and claimed-register write32; Interrupt
info and admission-only wait/acknowledge/mask/unmask; and DMAPool allocation
of eight fixed manager-attached bounce-buffer DMABuffer caps with typed free,
single-page map/unmap, and manager-accounted descriptor submit/complete. A
shared pure capos-lib::device_authority validator makes the range, alignment,
protection, and handle-identity decisions before the kernel maps or touches
anything, so writable, executable, unknown-protection, zero-size, unaligned,
overflowing, and out-of-BAR requests are denied first.
Blocked: allocations beyond the eight fixed slots, real DMA side effects,
writable userspace BAR mappings, arbitrary MMIO writes and doorbells,
unbrokered register access, blocking IRQ wait, hardware acknowledgement, IRQ
ownership, hardware mask/unmask, MSI/MSI-X programming, and general IRQ
delivery. One narrower runtime-visible exception exists for
make run-ddf-provider-consumer, where the selected TX queue publishes into
the kernel-owned virtio-net ring after the same authority, bounce-scrub, and
notify-policy gates.
Per-method invariants – authority, handle identity, physical range, buffer lifecycle, fixed-slot budget, descriptor effects, ledger and zero-live evidence, and stale/cross-reset behavior – are in DMA Isolation.
PCI, audit, and diagnostics
- The hardware bring-up path has bounded ACPI RSDP/RSDT/XSDT, MADT, MCFG, DMAR, and IVRS diagnostics plus reusable PCI config-space access through legacy I/O ports and Q35 PCIe ECAM. The x86 path programs masked MADT-backed I/O APIC routes for legacy IRQs while honoring source overrides.
- PCI memory-BAR subregions are validated and mapped through a shared kernel
helper before in-kernel drivers use device MMIO, and PCI capability walking
reports non-programming MSI/MSI-X metadata.
make run-pci-nvmeapplies the same metadata-only path to a QEMU NVMe controller, with controller init, admin/I/O queues, doorbells, and direct DMA not started or blocked. make test-diagnosticsboots a feature-gated COM1 early-boot diagnostics prompt before capability, scheduler, timer, manifest, or userspace startup, with bounded commands for status, CPU, memory, ACPI, PCI, IRQ, timers, devices, logs, reboot placeholder, and halt.HardwareAuditLog.snapshotdecodes lifecycle records through the kernel’s in-memory ring, including cursor requests outside the default latest 16-record tail, below-oldest clamping, and past-end empty cursor metadata on the overflowed ring. That kernel ring is itself volatile and unsigned: it is a staging buffer, not the durable record.- A userspace audit-log service drains that staging ring onto the
capability-native
Store, giving a recoverable segment ring with explicit retention, drop-oldest overflow that records gap and eviction markers, segment-scan crash recovery, manifest-scoped reader admission, and hash-chained tamper-evidence with per-segment HMAC seals. Audit records therefore do survive reboot. The seals use a development-only RAM-local key source; external verifier key custody, production key rotation and revocation enforcement, and authority-broker runtime reader admission are future work, so this is tamper-evidence for a local operator rather than evidence a third party can verify.
Each driver has a provenance map recording its cited spec, implemented wire-format subset, and capOS authority mapping.
Validation: make test-ddf-audit-service-persist-reboot,
make test-ddf-audit-service-keyed-signature,
make test-ddf-audit-reader-smoke,
make test-ddf-audit-reader-runtime-admission,
make test-diagnostics, make test-iommu-acpi, make run-net,
make test-hardware-grant-cycle, make test-hardware-audit,
make run-ddf-provider-consumer.
Networking
Networking is userspace-first. The kernel no longer depends on smoltcp, and
the qemu-only kernel TCP/UDP socket entry points fail closed; new protocol
logic belongs in the userspace stack.
Kernel-side virtio-net fixture
make run-net remains a lower-layer QEMU fixture, not a socket path. It covers
modern virtio PCI transport discovery for the common, notify, ISR, and
device-specific MMIO regions, feature negotiation (VIRTIO_F_VERSION_1, MAC
when safe, and VIRTIO_NET_F_MRG_RXBUF), RX/TX split-virtqueue initialization,
a TX descriptor completion proof, Ethernet ARP resolution from 10.0.2.15 to
10.0.2.2, and an ICMP echo round trip validated against the QEMU user-mode
gateway with checksum and identifier/sequence checks.
Its DMA pages pass through a bounded kernel-owned device_dma pool ledger that
accounts live bytes, page counts, page-rounded MMIO mapping bytes, interrupt
holds, ring depths, and descriptor submission/completion while exposing no
userspace DMA/MMIO/interrupt handles. Scratch-ledger proofs cover the
budget/OOM refusal matrix, zero-live teardown evidence, and stale DMA page
handles without touching the live virtio-net ledger.
QEMU exposes a transitional 1af4:1000 function with modern vendor
capabilities; capOS accepts that shape only through the modern capability
layout and lets the in-kernel owner claim and unmask only its own MSI-X routes.
The wire-format subset and register provenance are in
virtio-net.
Userspace network stack
The Phase C userspace smoltcp network-stack process owns the production socket
path. Landed local/QEMU evidence: a userspace stack service grants an
application client a TcpListenAuthority and serves TcpListener/TcpSocket
caps for a hostfwd TCP round trip; DHCP/IPv4 lease, default-route, and ARP
configuration; bounded ICMPv4 Echo Reply diagnostics; and the Web UI L4 serving
path with distinct public, local-health, and provider-health listener
capabilities. Production transport randomization requires a per-boot
EntropySource grant – a boot without it refuses to publish with a typed,
secret-free reason and binds no listener.
IPv6 is landed on the same stack: link-local addressing and Neighbor
Discovery, Router Advertisement with SLAAC, GCE-style DHCPv6 address
configuration including bounded DNS-server and domain-search option inputs,
ICMPv6 Echo Reply, and IPv6 TCP listen and connect, each with a local proof and
the last also over a real NIC datapath. The DHCPv6 path also has a bounded
synthetic-clock lifecycle proof covering Renew, Rebind, and address/resolver
withdrawal at expiry; its contract is recorded in the
DHCP Plan. A separate system
resolver proof models bounded, replacement-based admission of option-23
upstreams and option-24 search domains, exposes both through the typed status
surface with provenance distinct from DHCPv4 option 6, and preserves static
precedence and the prior configuration on refusal. The focused
system-cloud-prod-ipv6-dhcpv6-gce-config manifest connects the DHCPv6 client
proof to the resolver proof service through a shared demo-local endpoint codec;
it proves scoped authority and atomic replacement in local QEMU, not a deployed
production resolver handoff. The contract and evidence are documented in the
System Resolver Plan.
The address ABI is explicitly family-tagged so an
all-zero IPv4 configuration can never be misread as an IPv6 state: the legacy
accessor accepts IPv4 and fails closed on IPv6 with a malformedAddress class, and
capos-rt surfaces the family and an IPv6-support flag on NetworkConfig.
Operator tools expose read-only status and bounded IPv4/IPv6 ping. A granted
PacketTrace is header-only with capacity, filter, and expiry limits. The old
kernel-socket client proof is retired.
The shared socket-object table, its fixed service-object and transport-buffer byte readbacks, the ingress ledger, and the lane capacities are bounded and observable; their configured and effective values are in Resource Governance. All of it is local/QEMU evidence; what has been proven on a real cloud instance is below.
Validation: make test-cloud-prod-ipv6-dhcpv6-gce-config,
make test-network-dhcpv6-lease-lifecycle,
make test-network-system-dnsresolver, make test-network-status-tool,
make test-network-ping-tool, make test-network-ping6-tool, and
make test-network-packet-trace.
Cloud instances
capOS boots on a real Google Compute Engine instance.
make capos-cloudboot-image builds a GCE-compatible disk image and
make cloudboot-test imports it, launches a temporary instance with no public
IP and no service account, captures serial output plus a structured
provider.json evidence record, and deletes every resource it created. The
harness exits non-zero when teardown fails or evidence capture is incomplete,
so a resource-leaking run cannot be reported as a success.
The demonstrated operator access path is the serial-console shell, which emits
its own cloudboot-evidence: access-path serial-console-shell marker over COM1
rather than the harness inferring it from the kernel boot landmark. Remote
shell access on a cloud instance is not productized; SSH and WebShell remain
future tracks.
Bars proven on live instances, each on a temporary private instance that was then torn down:
- Imported-image serial boot through the
capos kernel startinglandmark to manifest load, init start, and shell spawn, one2-small. - Legacy virtio-net raw-frame bind against the live GCE NIC, booting the
production non-
qemucloud kernel from the legacy datapath manifest: candidate select over the PIO BAR, real device MAC read, feature negotiation, full 4096-entry vring materialization, a broadcast DHCP DISCOVER transmit, and a device-to-host RX DMA. QEMU caps queue size at 1024, so the vring materialization has no local equivalent. - gVNIC bring-up on the modern GCE NIC: the gVNIC PCI function
(
1ae0:0042) recorded with BAR and MSI-X metadata, BAR0 mapped throughDeviceMmio, manager-owned DMA pages for the admin queue and descriptor buffer, and one GQI/QPL TX/RX queue pair brought up far enough to send one DHCP DISCOVER raw Ethernet frame and receive one inbound IPv4 frame before teardown. A separate hardware-only proof records typedNic.transmit,Nic.receive,Nic.macAddress, andNic.linkStatusover that queue path with inline frame transfer and no host-physical or IOVA export. There is no reusable gVNIC provider service, no host conformance suite, and no gVNIC device model in QEMU, so this path has no local smoke that can execute the device. - NVMe Persistent Disk brokered READ through the provider authority path on
c3-standard-4: one READ, not a reusable storage provider, filesystem integration, virtio-scsi path, Local SSD path, direct-DMA claim, or device-autonomous MSI-X claim. - ICMP echo answered over the live NIC to a same-VPC probe.
- Private self-hosted Web UI serving: a same-VPC probe byte-verified the served bundle over the live legacy NIC in the no-public-IP posture.
Each bar is one recorded billable run against a single sandbox project, not a gate that anything reruns, and no instance carries a public endpoint, an unattended lifecycle, or production authority. Register-level detail and each driver’s implemented wire-format subset live in the per-device provenance maps: gVNIC, GCP Persistent Disk, virtio-net, and NVMe.
The no-spend parts of the provider harness do run as ordinary local gates.
Preflight, evidence-grammar validation for the private and public Web UI
proofs, ingress planning, teardown, and the provider-command allowlist each put
stub gcloud/gsutil binaries first on PATH and assert they were never
invoked.
Two GCE follow-ups are held rather than open. Public ingress with TLS needs the public-readiness chain and then fresh explicit authorization for exposure; private or generic GCE authorization does not cover it. The private IPv6 reachability proof is blocked on IAM: the sandbox credential can neither create nor list the dual-stack subnet and IPv6 firewall rule it needs, so it fails closed before reaching a billable resource.
AWS and Azure have device-protocol logic but no instance has ever been launched. ENA and MANA are pure host-testable encode/decode logic with host conformance suites vetted against the published specifications and the Linux reference drivers, with no QEMU proof by deliberate exception; the AWS and Azure storage work is a cloud-shape classification delta on the shared NVMe path, proven in QEMU. Live proofs on both are held pending provider access and explicit approval for a billable run.
Validation: make cloudboot-test and
make cloudboot-gcp-storage-nvme-io-read-test are billable and need sandbox
credentials. make cloudboot-gce-private-webui-preflight-check,
make cloudboot-public-webui-preflight-check,
make cloudboot-gce-private-webui-evidence-fixture-check,
make cloudboot-public-webui-proof-evidence-fixture-check,
make cloudboot-public-webui-ingress-plan-check,
make cloudboot-public-webui-teardown-fixture-check, and
make cloudboot-public-webui-provider-command-allowlist-check spend nothing.
Security and verification
The tree carries Miri, proptest, fuzz, Loom, Kani, generated-code, dependency policy, trusted-build-input, panic-surface, and DMA-isolation work.
make kani-libis a bounded Kani gate overcapos-libbitmap, cap-table stale-handle, transfer preflight, transfer rollback, and frame-grant accounting invariants. The heavier prepare-copy to provisional-destination seam proof needs more memory than a workstation run and passed once undermake kani-lib-fullon a high-memory builder. Coverage is not complete for every trust boundary.make kani-ringruns in default and measure configurations. Its seven harnesses check full-field SQE validation panic-freedom, accepted and one-field-invalid cases from an independent SQE oracle, fail-closed transfer-descriptor arithmetic, bounded SQ/CQ pending-or-recovery classification across counter wrap, an abstract concurrent-protocol state machine with non-vacuous witnesses, and the productionRingSqConsumerStateduplicate-owner and stale-generation rejection.- That is bounded model checking of the pure ring core at fixed 16-entry SQ / 32-entry CQ capacities. Weak-memory interleavings and scheduler/teardown races remain Loom-only, and the Kani gate does not prove the volatile ring page or the kernel dispatcher.
- Two protocol families are additionally modelled outside Rust: the DMA ownership handoff and the scheduler lease, LAPIC one-shot, and nohz transitions each have TLA+ and Alloy models, with Loom models for the DMA deferred-completion and nohz paths. These check the abstract protocol, not the implementation that runs.
Exact model bounds and exclusions are in the security and verification backlog.
Repository and Tooling State
Run-target naming
make run and make run-display are the only operator run entrypoints.
Every focused proof and every alternate manifest, firmware, device, or
measurement QEMU variant uses a test-* target. Seventeen further run-*
targets remain Gate C naming debt: they are live gates that have not yet been
renamed, and tools/check_doc_make_targets.py reports each citation of them as
a warning. The same check fails when any target’s non-exempt citation count
exceeds its pinned docs/workflow/run-target-citation-budget.toml ceiling or
an unlisted debt target gains a citation; reductions remain refreshable. New
command-bearing documentation follows the
workflow citation-authoring rule. Their
migration inventory and retained carve-out
records which are expected to move and which stay.
Task-coordinator interchange
tools/capos-task-coordinator-snapshot.py provides a deterministic offline
plan-loopyard step with classified create, resource, dependency, and status
entries, plus a local verify-plan companion that validates completeness and
determinism, reconstructs the plan’s post-application target state, and proves
that state is an idempotent planning fixed point. Neither command performs
authenticated ingestion or mutation; the operator-authorized PostgreSQL
importer that would close that gap is
not built.
Not Implemented
Absent as supported capOS behavior today: service restart policy, capability-scoped system monitoring beyond the Phase 1 log surface, notification-driven service composition, promise chains deeper than one hop, service-facing SharedBuffer APIs over the MemoryObject substrate, session quotas, durable multi-account credential storage, broader account policy, production SSH or WebShell ingress, public ingress and TLS exposure, any AWS or Azure instance boot, broader storage variants, high-throughput or multiqueue NIC work, direct-remapping DMA, aarch64, and persistence beyond the landed installable data-region and generation paths.