Roadmap
Each row is one loop unit: one milestone, one branch, one GitHub PR. Do not stack a later stage onto an unclosed earlier PR. Cite frozen FR/NFR IDs in the PR when touching behavior; do not invent new IDs in chat.
Primary ISA is AArch64 (ADR-003). M0/M1 probes are UART + qemu-system-aarch64, not VGA 0xb8000.
| ID | Milestone | Probe that closes it | Status |
|---|---|---|---|
| M0 | Kernel tree + harness scaffold on GitHub | Files on the default-target PR; ledger started | In harness PRs #2/#3 (source). Merge is a human/MRC job. |
| M1 | QEMU virt boots UART “Hello World!” | qemu-system-aarch64 serial shows the string; see honesty ledger | Verified in the 2026-09-08 cloud probe (not CI). x86 VGA probe is historical only. |
| M2 | Integration test harness | QEMU virt ARM semihosting exit + #[test_case]; scripts/qemu-smoke.sh fail-closed | Verified in the 2026-09-08 cloud probe (not CI). Sponsor stacked this on the ISA PR. |
| M3 | Exception vectors + breakpoint | Test or QEMU serial proof the handler runs | Verified: 2026-09-09 cloud qemu-smoke + GHA smoke.yml on the M3 PR (see honesty ledger). |
| M4 | Fatal exception stack | Fatal path does not silently lock the VM | Verified: 2026-09-09 cloud qemu-smoke (see honesty ledger). GHA on the M4 PR branch still Unknown. |
| M5 | Hardware interrupts (GIC + timer) | Timer tick observable (serial or test) | Verified: 2026-09-09 cloud qemu-smoke + GHA smoke.yml on the M5 PR (see honesty ledger). |
| M6 | Input | Injected PL011 RX byte observable on serial (or test) | Verified: 2026-09-09 cloud qemu-smoke (see honesty ledger). GHA on the M6 PR branch still Unknown. |
| M7 | Paging + frame allocator | Map/unmap or allocator test | Verified: 2026-09-09 cloud qemu-smoke (see honesty ledger). GHA on the M7 PR branch still Unknown. |
| M8 | Heap (alloc) | Box/vec smoke on the heap | Verified: 2026-09-09 cloud qemu-smoke + GHA smoke.yml on the M8 PR (see honesty ledger). |
| M9 | Cooperative scheduler | Two tasks observed to run | Verified: 2026-09-09 cloud qemu-smoke + GHA smoke.yml (see honesty ledger). |
M0–M9 are on main (M9 = merge of PR #15 / FR-11). Pillar work through ADR-020 (#17–#28) plus docs #30/#51/#29, A1–A6 (ADR-021–ADR-027) are on main. This PR is Track A / A7 (issue #38, parent issue #31): ADR-028 virtio-blk + FAT16. Merge is still a human/MRC job (ADR-002).
Pillars (post-M9)
Bring-up M0–M9 stays one loop unit each. After M9, work is grouped under the three pillars.
| ID | Work | Probe that closes it | Status |
|---|---|---|---|
| P-SEC-1 | Threat-model v1.1 slice (NFR-10) | Read security.md; no “secure OS” claim | Verified (file + review). v1.1 adds linker stacks, guards, EL0 first mile, remaining W^X gaps. |
| P-SEC-2 | W^X / NX heap + coop stacks | Page-table PXN on heap + caught execute-from-heap IABORT (wx: ok) | Verified: 2026-09-10 cloud qemu-smoke (see honesty ledger). Map: ADR-012. Linker stack pages still X. |
| P-SEC-2b | Linker-stack guard pages | Unmapped 4 KiB holes; store faults (guard: ok) | Verified: 2026-09-10 cloud qemu-smoke (see honesty ledger). ADR-014. Not kernel W^X. |
| P-PERF-1 | Baseline CNTPCT probe (NFR-07) | Serial perf: cntpct and/or #[test_case]; not a published bench | Verified: 2026-09-09 cloud qemu-smoke + GHA (see honesty ledger). |
| P-PERF-2 | IRQ-to-handler CNTPCT delta | Serial perf: irq-delta + samples max >= min; not a latency budget | Verified: 2026-09-10 cloud qemu-smoke (see honesty ledger). |
| P-PERF-3 | Host debug ELF size (NFR-08) | Host marker perf: elf-size bytes=<n>; not a budget | Verified: 2026-09-10 cloud qemu-smoke host print (see honesty ledger). |
| P-SEC-3 | EL0 first mile | el0: svc + el0: nx kernel + el0: ok | First mile Verified: 2026-09-10 cloud qemu-smoke (see honesty ledger). |
| P-SEC-2c | RO+NX text/data (ADR-015) | ro: nx data + ro: write fault + ro: ok | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). Identity image W^X on virt; not “secure OS.” |
| P-PERF-4 | Boot-to-ready CNTPCT (NFR-08) | perf: boot-delta ticks=<n>; not a budget | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). |
| P-PERF-5 | A9 slot-disconnect CNTPCT pair (ADR-046) | perf: app-load + perf: embed-load + perf: slot-delta app=<a> embed=<b>; not a percent | Verified when this tip’s qemu-smoke prints the pair (see honesty ledger). QEMU TCG lab. Production slot stays FAT-only. |
| P-PERF-6 | FAT16 vs memfs write CNTPCT pair (ADR-051) | perf: fat-write + perf: memfs-write + perf: fs-write-delta fat=<a> memfs=<b>; not a percent | Verified when this tip’s qemu-smoke prints the pair (see honesty ledger). QEMU TCG lab. Keep fat: write / fat: ok. |
| P-PERF-7 | FAT16 vs memfs read CNTPCT pair (ADR-065) | perf: fat-read + perf: memfs-read + perf: fs-read-delta fat=<a> memfs=<b>; not a percent | Verified when this tip’s qemu-smoke prints the pair (see honesty ledger). QEMU TCG lab. Keep fat: read / fat: ok / ADR-051 write pair. |
| P-PERF-8 | Net-ping path CNTPCT (ADR-069) | perf: net-ping ticks=<n>; smoke greps prefix only; not a percent | Verified when this tip’s qemu-smoke prints the prefix (see honesty ledger). QEMU TCG lab. Keep N1/N2/N4. |
| P-PERF-9 | UDP DNS path CNTPCT (ADR-072) | perf: udp-dns ticks=<n>; smoke greps prefix only; not a percent | Verified when this tip’s qemu-smoke prints the prefix (see honesty ledger). QEMU TCG lab. Keep N1/N2/N3/N3.x/N4 + ADR-069. |
| P-PERF-10 | Thin TCP echo path CNTPCT (ADR-078) | perf: tcp-echo ticks=<n>; smoke greps prefix only; not a percent | Verified when this tip’s qemu-smoke prints the prefix (see honesty ledger). QEMU TCG lab. Keep N1–N5 / N5.x + ADR-069/072. |
| P-SEC-3b | User TTBR0 + EL0 cannot read kernel .data | el0: no kernel read; user table omits .data | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). Isolation / PAN stay Planned. |
| P-SEC-3c | ASID-tagged TLB isolation | asid: dual + asid: conflict + asid: ok; no TLBI VMALLE1 on the switch | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). Umbrella isolation / PAN stay Planned. |
| P-SEC-3d | Standing EL0 context | el0: standing + el0: restored; is_active() true only while standing | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). Not POSIX. Default ERET clears IRQ mask (ADR-041). |
| P-SEC-3e | TTBR1 kernel-private page (ADR-016 first cut) | ttbr1: el1 + ttbr1: no el0 + ttbr1: ok | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). |
| P-SEC-3f | EL1 fetch from TTBR1 high VA (ADR-017) | ttbr1: el1 exec + ttbr1: vbar + existing ttbr1: ok | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). Identity boot stub stays. |
| P-SEC-3g | Identity-tear first cut (ADR-018) | ident: split + ident: fault + ident: high + ident: no el0 + ident: ok | Verified: 2026-09-11 cloud qemu-smoke (see honesty ledger). Full identity teardown / PAN / umbrella isolation were Planned at this mile (umbrella: P-SEC-3l). Do not claim “the kernel moved.” |
| P-SEC-3h | High-VA jump + 16 KiB identity text range (ADR-019) | ident: jump + ident: range + ident: text + existing ident: ok | Verified: 2026-09-11 cloud qemu-smoke + arm64 workstation Docker on 24d94e6 (see honesty ledger). Do not claim “the kernel moved.” |
| P-SEC-3i | High-VA vtable rewrite + live identity .text tear (ADR-020) | ident: reloc + ident: live + existing ident: ok; println! after the tear | Verified: 2026-09-11 cloud qemu-smoke + arm64 workstation Docker on e80dc93 + GHA merge-commit 34651404108 (see honesty ledger). .rodata / .data / heap stay. Do not claim “the kernel moved.” |
| P-SEC-3j | Identity .rodata / .data / heap tear | .rodata + .data/stacks + heap unmapped + high-only access. | .rodata Verified (ADR-025). .data/stacks Verified (ADR-037). Heap Verified (ADR-038) on this branch qemu-smoke (ident: heap-reloc / ident: heap / ident: heap-fault / ident: heap-high; heap-stay gone). Do not claim “the kernel moved.” |
| P-SEC-3k | PAN enable on virt | ID_AA64MMFR1_EL1.PAN != 0 and an EL1-vs-EL0 access fault | Verified (ADR-080 / #125 mile 2): default -cpu cortex-a76, pan: present + pan: enabled + pan: el1-fault. Historical a57 pan: absent (ADR-026 / ADR-054 / ADR-079). One mile; alone it is not the ADR-091 umbrella claim. |
| P-SEC-3m | EL0 entry without TLBI VMALLE1 (ADR-039) | el0: no-vmalle1; src/exception.rs has no tlbi vmalle1 | Verified: 2026-09-12 agent-box qemu-smoke (see honesty ledger). One mile; alone it is not the ADR-091 umbrella claim. PAN enable was a non-goal on the then-default a57 (enabled on a76 since ADR-080); umbrella: P-SEC-3l. |
| P-SEC-3n | Lower-EL IRQ while standing (ADR-040) | el0: irq; #[test_case] lower_el_irq_while_standing | Verified when this tip’s qemu-smoke prints el0: irq (see honesty ledger). FIQ/SError still park. One mile; alone it is not the ADR-091 umbrella claim. |
| P-SEC-3o | IRQ-unmasked default standing/task ERET (ADR-041) | el0: irq-default; #[test_case] lower_el_irq_default_eret | Verified when this tip’s qemu-smoke prints el0: irq-default (see honesty ledger). Short non-standing probes stay masked. FIQ/SError still park. One mile; alone it is not the ADR-091 umbrella claim. |
| P-SEC-3p | Identity _start stay honesty (ADR-042 / ADR-047) | ident: start-stay; #[test_case] identity_boot_stub_stays_while_live_torn | Verified honesty. Never yank _start. One mile; alone it is not the ADR-091 umbrella claim. |
| P-SEC-3s | Leftover identity RAM tear (ADR-049) | ident: ram / ident: ram-fault / ident: ram-high; reject ident: ram-stay | Verified on this branch when smoke passes. Optional mile from ADR-047. One mile; alone it is not the ADR-091 umbrella claim. Not “kernel moved.” |
| P-SEC-3q | Lower-EL FIQ while standing + SError park (ADR-043) | el0: fiq + el0: serror-park; #[test_case] lower_el_fiq_while_standing | Verified (FIQ taken + park honesty). Taken SError = deferred/non-goal (ADR-047). One mile; alone it is not the ADR-091 umbrella claim. |
| P-SEC-3r | Taken lower-EL SError QMP attempt (ADR-045) / hard-stop (ADR-053) / reopen (ADR-081) / B1 pin (ADR-083) / B2 spike (ADR-084) / B2-P KVM (ADR-085) | Guest A-clear path + el0: serror-arm + QMP inject-nmi log; el0: serror-park if not taken; bare el0: serror under opt-in pin | Stock virt TCG: deferred / non-goal (hard-stopped) (ADR-053 / ADR-081 / ADR-047); park Verified; do not require el0: serror on default smoke. B1: Verified (opt-in pin only) — ADR-083 pinned QEMU TYPE_NMI→SError, GHA job qemu-nmi-pin (CTOS_BUILD_QEMU_NMI=1). B2 (different runner/accelerator): Planned / blocked — ADR-084 2026-09-25 free-runner spike: no arm64 KVM/HVF/WHPX on any free GitHub-hosted runner (runs 36193423604 / 36193613972); needs sponsor choice (self-hosted arm64 KVM, paid bare metal, or wait). B2-P: Verified (opt-in, one-off paid run) — ADR-085 2026-09-26: AWS Graviton3 c7g.metal, QEMU -accel kvm + 0002 KVM_SET_VCPU_EVENTS serror_pending → bare el0: serror + b2: taken (fail-closed smoke PASS, run 4). Not a CI gate. Also fixed TCR_EL1.TG1 (reserved → 4K). Evidence class (D1): ADR-090 2026-09-26: B1 pin job made unconditional (every push/PR; a skipped job would pass a required check) + B2-P run 4 = the umbrella’s taken-SError evidence. Stock TCG hard-stop unchanged. One mile; alone it is not the ADR-091 umbrella claim. |
| P-SEC-3l | Umbrella EL0 isolation | Standing + PAN + full TTBR1 / identity teardown | Verified (scoped: the accepted ADR-091 sentence only; M6, 2026-09-28). History: was Planned / non-claim until checklist (ADR-055 / ADR-047 / sponsor closure ADR-060). Constraints: PAN enable Verified separately (ADR-080) but not the umbrella; _start stays; taken SError hard-stopped (ADR-081). Specific miles are not this row. 2026-09-26 audit ADR-086: all ADR-055 §A miles Verified this session; §B not Met (taken SError only opt-in B1/B2-P; five identity ranges remain in kernel TTBR0 incl. the _start stub; no umbrella accept). Milestones M1 (inventory ratchet + RAM-side tears) → M2 (MMIO high alias) → M3 (stub policy, D2) → M4 (SError evidence class, D1) → M5 (accept, D4) → M6 (flip). M1 done (ADR-087): I2/I4/I5 torn from kernel + user TTBR0; fail-closed ident: inv walk allows only {MMIO block, _start stub}; planted identity page caught (ident: inv-neg, inv-leak-probe). Sponsor D1–D5 answered 2026-09-26 ~14:35 CEST (D4 accept still pending the sponsor reading the draft). M2 done (ADR-088): GIC/PL011/virtio via TTBR1 Device blocks; identity MMIO block torn; inventory k=1 u=1 a=1 = _start stub only (ident: inv-ok allow=stub). M3 (ADR-089): sponsor D2: _start stub page = the one documented identity exception; checklist identity row reworded and Met (ADR-087/088 evidence). M4 (ADR-090): sponsor D1: taken-SError evidence class = B1 qemu-nmi-pin (now unconditional) + B2-P run 4; checklist row 1 reworded and Met on that class. M5 (ADR-091, Draft): proposed umbrella sentence + scope, pending the sponsor’s written accept (B3 not Met); M6 flip not started. ADR-092 (2026-09-27, sponsor option (b)): G1 EL0-reachability walk (k/u/ASID-B TTBR0 + TTBR1, allowlist = five user slots, fail-closed + leak build) and G2 EL0 write permission fault closed; new gap G4 (syscall copy helpers skip the EL0 permission check); ADR-091 revision 2 sentence back with the sponsor (B3 not Met). ADR-094 (2026-09-27): G4 closed; ADR-091 revision 3. ADR-095 (2026-09-28): G3 KVM re-check PASS. M6 (2026-09-28): sponsor’s written accept (B3, 16:41 CEST); ADR-091 Accepted; row flipped. Say “EL0 isolated” only as the ADR-091 sentence, with its limits (one core, the TCG default smoke machine; not a speculative-execution or side-channel claim, not a real-hardware claim, not a certification; app text is EL0 execute-only; EL1 can still read it (no FEAT_EPAN); no multi-core race story). |
Hub: pillars.md. ABI contract: syscall.md.
Track A — freestanding app hosting (#31)
A1 is the SVC ABI mile. A2 is the CRT / libctos mile. A3 is the guest ELF PT_LOAD loader mile. A4 is standing EL0 as normal mode for a loaded image. A5 is isolation completion (identity .rodata / .data / heap tears + EL0 entry without VMALLE1 + PAN ID-field; PAN enable was a non-goal on the then-default a57 per ADR-047, enabled on a76 since ADR-080). A6 is thin VFS + in-RAM memfs. A7 is virtio-blk + FAT16. A8 is documented sample rebuild recipes (no new runtime). A9 is the OS/app slot first cut plus leftover cross-update / embed-off (ADR-032). Track A children A1–A9 have a first cut; sample catalog deepened with fs-libctos (ADR-059), fat-libctos (ADR-061), and yield-libctos (ADR-062); product “app hosting is done” is Verified under ADR-048 criteria Met×5 (ADR-052). Not Linux containers. Not glibc. One child issue → one PR. Do not round A1–A9 miles alone into Linux/POSIX claims.
| ID | Work | Probe that closes it | Status |
|---|---|---|---|
| A1 | Stable SVC ABI + docs (#32, ADR-021) | svc: yield + svc: user-hi + svc: uart + svc: exit + svc: ok; #[test_case] | ABI mile Verified: 2026-09-11 cloud qemu-smoke on 8846bc5 (onto aa46219 / #29). App hosting stays Planned. |
| A2 | Freestanding CRT / libctos (#33, ADR-022) | libctos: hi + libctos: ok + libctos: linked; #[test_case] | CRT mile Verified: 2026-09-12 cloud qemu-smoke on c9b292b. App hosting stays Planned. |
| A3 | ELF (or raw image) loader into user TTBR0 (#34, ADR-023) | loader: mapped + loader: ok; payload libctos: hi / libctos: ok; #[test_case] | Loader mile Verified: 2026-09-12 cloud qemu-smoke (see honesty ledger). Not a Linux ABI. App hosting stays Planned. |
| A4 | Standing EL0 as normal mode (#35, ADR-024) | el0: task-enter + el0: task-active + el0: task-exit + el0: task-restored + el0: restore-fail + el0: task-ok; #[test_case] | Standing-task mile Verified: 2026-09-12 cloud qemu-smoke (see honesty ledger). Not isolation. App hosting stays Planned. |
| A5 | Isolation completion (#36, ADR-025 / ADR-026 / ADR-032 / ADR-037 / ADR-038 / ADR-039 / ADR-040 / ADR-041 / ADR-042 / ADR-043 / ADR-044 / ADR-045 / ADR-047 / ADR-049 / ADR-053 / ADR-054 / ADR-055 / ADR-060 / ADR-079 / ADR-080 / ADR-081 / #125) | ident: rodata + ident: data + ident: heap + ident: ram; el0: no-vmalle1; el0: irq + el0: irq-default + el0: fiq + el0: serror-park; pan: id= + pan: present + pan: enabled + pan: el1-fault (cortex-a76) | Prior A5 + PAN enable (ADR-079/080). Locks: taken SError still hard-stopped after ADR-081 re-probe (ADR-053 / ADR-081 / ADR-082); umbrella: P-SEC-3l (ADR-055); never yank _start. One mile; alone it is not the ADR-091 umbrella claim. |
| A6 | Thin VFS + memfs (#37, ADR-027) | fs: create + fs: write + fs: read + fs: el0 + fs: ok; #[test_case] | memfs mile Verified (see honesty ledger). Not POSIX. App hosting stays Planned. |
| A7 | virtio-blk + FAT16 (#38, ADR-028) | blk: virtio + blk: cap + blk: rw + blk: ok; fat: mount + fat: read + fat: ok; #[test_case] | block + FAT mile Verified when this tip’s qemu-smoke passes (see honesty ledger). Same VFS open. Not POSIX. Not FAT32. App hosting stays Planned. |
| — | FAT16 write depth (Future slice 3, ADR-050) | fat: write + fat: rewrite + fat: create + host --check-write; keep fat: ok / slot: ok | Verified when this tip’s qemu-smoke passes (see honesty ledger). Not POSIX. Not FAT32. App hosting stays Planned. |
| — | FAT vs memfs write CNTPCT (Future slice 4, ADR-051 / P-PERF-6) | perf: fat-write + perf: memfs-write + perf: fs-write-delta; keep fat: ok / slot: ok | Verified when this tip’s qemu-smoke prints the pair (see honesty ledger). Not a bench. Not a percent. App hosting stays Planned. |
| — | FAT vs memfs read CNTPCT (perf slice 3, ADR-065 / P-PERF-7) | perf: fat-read + perf: memfs-read + perf: fs-read-delta; keep fat: ok / write pair / slot: ok | Verified when this tip’s qemu-smoke prints the pair (see honesty ledger). Not a bench. Not a percent. App hosting stays Planned. |
| — | Net-ping CNTPCT (perf slice, ADR-069 / P-PERF-8) | perf: net-ping ticks=<n>; prefix grep; keep N1/N2/N4 | Verified when this tip’s qemu-smoke prints the prefix (see honesty ledger). Not a bench. Not a percent. N3 UDP unlocked by ADR-070 (not BSD sockets). |
| — | UDP DNS CNTPCT (perf slice, ADR-072 / P-PERF-9) | perf: udp-dns ticks=<n>; prefix grep; keep N1/N2/N3/N3.x/N4 + ADR-069 | Verified when this tip’s qemu-smoke prints the prefix (see honesty ledger). Not a bench. Not a percent. Not TCP. |
| — | Thin TCP echo CNTPCT (perf slice, ADR-078 / P-PERF-10) | perf: tcp-echo ticks=<n>; prefix grep; keep N1–N5 / N5.x + ADR-069/072 | Verified when this tip’s qemu-smoke prints the prefix (see honesty ledger). Not a bench. Not a percent. Not sockets. EL0 surface (not kernel N5 timer). |
| — | FAT16 mkdir depth (ADR-073 / sponsor mile 2) | fat: mkdir; keep fat: ok / write / readdir / delete / grow / slot: ok | Verified when this tip’s qemu-smoke passes (see honesty ledger). Not POSIX mkdir. Not a directory tree. Not TCP (mile 3). |
| — | Thin TCP (ADR-074 / N5 / sponsor mile 3) | net: tcp-ok (+ tcp-syn/tcp-est/tcp-tx/tcp-rx); guestfwd 10.0.2.4:7; keep N1–N3 + blk/FAT | Verified when this tip’s qemu-smoke prints net: tcp-ok (see honesty ledger). Not BSD sockets. Not listen/accept. Not TLS/HTTP. |
| — | FAT16 readdir depth (ADR-056) | fat: readdir + fat: entries; keep fat: ok / write / slot: ok | Verified when this tip’s qemu-smoke passes (see honesty ledger). Not POSIX getdents. Not FAT32. Not Linux/containers. |
| — | FAT16 delete depth (ADR-057) | fat: delete; keep fat: ok / write / readdir / slot: ok | Verified when this tip’s qemu-smoke passes (see honesty ledger). Not POSIX unlink. Not FAT32. Not Linux/containers. |
| — | VFS prefix mounts (ADR-058) | vfs: mount + vfs: mounts; keep fs: ok / fat: ok / slot: ok | Verified when this tip’s qemu-smoke passes (see honesty ledger). Not Linux mount(2). Not a new disk FS. |
| A8 | Documented sample apps (#39) | recipes cite only existing serial markers; docs-build | Recipes Verified (docs; smoke markers unchanged). Hub: what-can-run.md. No new ADR. Not app hosting. |
| A9 | OS–app slots (#48, ADR-030 / ADR-032) | host OS ELF + hello-libctos.elf; FAT /hello; slot: ok; same ELF on this OS and ba6541c | First cut + leftover when this tip’s qemu-smoke prints cross-update lines (see honesty ledger). A2–A4 load FAT (no embed). Product claim Verified under ADR-048/052 (this mile alone ≠ Linux hosting). |
| — | Freestanding fs-libctos catalog (ADR-059) | libctos: fs-ok + fsdemo: ok; keep slot: ok / /hello | Verified when this tip’s qemu-smoke passes (see honesty ledger). Memfs via ADR-058 /mem. Not POSIX. Not a reopen of ADR-048/052. |
| — | Freestanding fat-libctos catalog (ADR-061) | libctos: fat-ok + fatdemo: ok; keep /hello / /fsdemo | Verified when this tip’s qemu-smoke passes (see honesty ledger). FAT /probe via thin VFS. Not POSIX. Not a reopen of ADR-048/052. |
| — | Freestanding yield-libctos catalog (ADR-062) | libctos: yld-ok + ≥3× libctos: beat + yldemo: ok; keep prior three | Verified when this tip’s qemu-smoke passes (see honesty ledger). Cooperative yield rounds. Not preemption. Not a reopen of ADR-048/052. |
| — | FAT16 nested mkdir + empty rmdir (ADR-077) | fat: nested + fat: rmdir; keep fat: mkdir / grow / delete / net / samples | Verified when this tip’s qemu-smoke passes (see honesty ledger). Not POSIX. One nest level only. No new SVC. Not TCP CNTPCT. |
| — | Freestanding mkdir-libctos catalog (ADR-075) | libctos: mkdir-ok + mkdemo: ok; keep fat: mkdir / prior six | Verified when this tip’s qemu-smoke passes (see honesty ledger). EL0 fs_mkdir (27). Not POSIX. Not nested trees. Not EL0 TCP. |
| — | Freestanding tcp-libctos catalog (ADR-076) | libctos: tcp-ok + tcpdemo: ok; keep net: tcp-ok / prior seven | Verified when this tip’s qemu-smoke passes (see honesty ledger). EL0 net_tcp_echo (28). Not BSD sockets. Not listen/accept. |
On-disk filesystem work started as A7 (FAT16 read, not xv6-like). FAT16 write depth is ADR-050. FAT16 root listing is ADR-056. FAT16 root delete is ADR-057. FAT16 root mkdir is ADR-073. Nested mkdir + empty rmdir is ADR-077. EL0 fs_mkdir is ADR-075. EL0 net_tcp_echo is ADR-076. Thin VFS prefix mounts are ADR-058. Freestanding catalog samples on the same volume: ADR-059 / ADR-061 / ADR-062 / ADR-068 / ADR-071 / ADR-075 / ADR-076. See Filesystem: new vs extend.
Tracks (subordinate to principles)
Track A (freestanding apps, #31), Track B (Linux-compat research, #40), and Track N (network foundation, ADR-063) do not override principles.md. A9 is after Track A ABI/loader. Track B B1: Linux-compat subset research frame (ADR-031) — not claiming Linux userspace yet. Track B B2: Linux AArch64 vs ctos SVC gap map (linux-aarch64-syscall-gap.md) — inspection only; no Linux numbers in src/. Track B B3: standing EL0 stays the ctos process model (ADR-035) — Linux clone/execve/wait4 is a gap; do not add those syscalls. Track B B4: Linux ELF / auxv / PT_INTERP vs the freestanding loader (ADR-033) — gap ADR; Track A loader stays reusable; not claiming dynamic Linux ELF. Track B B5: Linux VFS vs thin ctos VFS (ADR-034) — concept compare only; no POSIX flags / dentries. Track B B6: never Linux-compat implementation from this ladder (ADR-036). Track B B7: guest OCI/Docker is a non-goal (ADR-029).
Track N — network foundation (ADR-063)
Separate track from A/B. Hub: track-n.md. N0 scope: ADR-063. N1 first frame: ADR-066. N2 ICMP ping: ADR-067. N3 UDP transport: ADR-070. N3.x EL0 UDP SVC: ADR-071. N4 EL0 net sample: ADR-068. N5 thin TCP: ADR-074. Reuse virtio-mmio patterns from blk; do not break FAT/blk smoke. Still not product: BSD sockets, TCP stack as product / listen-accept, DHCP/DNS as product, Wi‑Fi, Linux net stack, “has networking / sockets OS” marketing.
| ID | Work | Probe that closes it | Status |
|---|---|---|---|
| N0 | Scope ADR + docs wiring (ADR-063) | Docs-build; ledger Planned; no Verified invent | Accepted (docs) — this PR |
| N1 | Link bring-up / first frame (virtio-net mmio, ADR-066) | Discover + TX ARP + RX SLIRP gateway ARP reply; -netdev user,id=net0 -device virtio-net-device,netdev=net0; net: ok; keep blk/FAT green | Verified when this tip’s qemu-smoke prints net: ok |
| N2 | ARP + ICMP ping (kernel-path, ADR-067) | After ARP: ICMP echo vs 10.0.2.2; net: icmp-tx / net: icmp-rx / net: ping-ok; QEMU args unchanged | Verified when this tip’s qemu-smoke prints net: ping-ok |
| N3 | Minimal UDP transport (ADR-070) | UDP DNS query/reply vs SLIRP 10.0.2.3:53; net: udp-tx / net: udp-rx / net: udp-ok; keep N1/N2 | Verified when this tip’s qemu-smoke prints net: udp-ok |
| N4 | Freestanding sample using net SVCs (ADR-068) | libctos: net-ok / netdemo: ok; keep N1/N2/N3/samples | Verified when tip prints those markers |
| N3.x | EL0 UDP DNS SVC + udpdemo (ADR-071) | libctos: udp-ok / udpdemo: ok; keep N1/N2/N3/N4/samples | Verified when tip prints those markers |
| N5 | Thin TCP active-open + one payload (ADR-074) | net: tcp-ok (+ syn/est/tx/rx); guestfwd 10.0.2.4:7; keep N1–N3 | Verified when tip prints net: tcp-ok |
Docs website
mdBook + GitHub Pages (not a kernel milestone, not a new FR/NFR ID). https://ctos.artof.link HTTPS is Verified after #30 (deploy 34653046584 + HTTPS 200). Route 53 CNAME remains in place (zone <route53-zone-id>). Overview: what can run today. Publish notes: website.md.
Isolation leftovers (decided — ADR-047 / sponsor closure ADR-060)
- Taken SError: deferred / non-goal (hard-stopped) on virt TCG (ADR-053 / ADR-081 / ADR-082). Re-probed 2026-09-19 on cortex-a76 — still
machine does not provide NMIs. Unlock = ADR-081 B1/B2/B3; B1 research ADR-082, then B1 opt-in pin Verified (ADR-083,qemu-nmi-pin). B2 free-runner spike blocked (ADR-084, 2026-09-25: no arm64 KVM/HVF/WHPX on free hosted runners) — sponsor then chose B2-P: Verified (opt-in, one-off) on AWS Graviton3 under KVM (ADR-085, 2026-09-26). Park Verified; A-clear = dormant prep (ADR-045). - PAN: CPU reopen + enable Verified (ADR-079 / ADR-080; default
-cpu cortex-a76). Historical a57pan: absent. - Yank
_start: never while QEMU-kernelneeds0x4008_0000.ident: start-stayVerified honesty. Leftover identity RAM after heap: Verified optional mile (ADR-049; not umbrella-required). - Umbrella “EL0 isolated”: Verified only as the accepted ADR-091 sentence (M6, 2026-09-28; revision 4 accepted 2026-10-02; checklist ADR-055 all Met). Limits: one core, the TCG default smoke machine; not a speculative-execution or side-channel claim, not a real-hardware claim, not a certification; app text is EL0 execute-only; EL1 can still read it (no FEAT_EPAN); no multi-core race story. Sponsor one-pager: ADR-060.
- Product app hosting “done”: Verified under ADR-048 / ADR-052 (freestanding slots; not Linux/POSIX/containers).