# Plan: libcapos C-Substrate v0

## Overview

Phase 0 of the C-language substrate from
`docs/proposals/userspace-binaries-proposal.md` (section "C via libcapos").

Goal: ship a thin Rust staticlib (`libcapos.a`) that exposes the capos-rt
syscall + ring + CapSet + heap surface to C consumers via `extern "C"`,
plus a C "hello world" smoke that exercises the surface end to end. No
POSIX shape -- no errno, no fd table, no `open`/`read`/`write`, no
signals, no fork/exec, no sockets. Those belong to a future
`libcapos-posix` layer above libcapos.

## Conflict Surface

Owned by this plan:

- `libcapos/` (new crate, `crate-type = ["staticlib"]`, `name = "capos"`
  so the archive lands at `libcapos.a`).
- `libcapos/include/capos/capos.h` (public C header).
- `demos/c-hello/` (new C smoke binary).
- `system-c-hello.cue` (focused boot manifest).
- `tools/qemu-c-hello-smoke.sh` (boot assertion harness).
- `Makefile` additions: `libcapos`, `c-hello`, `run-c-hello` targets;
  fmt and fmt-check inclusion; `DEPENDENCY_POLICY_*` lists.
- `.cargo/config.toml`: `build-libcapos` alias.

Disjoint from active tracks: no schema changes, no kernel edits, no
`capos-rt`/`init`/`demos` Rust changes. Parallel-safe with Lua piccolo
(L.1), LaunchParameters (L.3), Remote Session DTO additions, and Device
Driver Foundation work.

Doc updates owned by this plan:

- `docs/programming-languages.md` -- C row status moved from "Future
  design" to "Phase 0 in tree (libcapos C-substrate v0)".
- `docs/proposals/userspace-binaries-proposal.md` -- "Future Phase:
  libcapos for C" closes out as shipped; "Future: C via libcapos"
  gains an implementation status note.
- `WORKPLAN.md` -- C-track ad-hoc bullet pointing at this plan + the
  proposal.

## Scope (v0)

C-callable surface (all `extern "C"`, header at
`include/capos/capos.h`):

- `capos_capset_get(name, name_len, out_cap_id, out_iface_id)` -- bootstrap
  CapSet lookup by name.
- `capos_cap_call(cap_id, method_id, params, params_len, result, result_len)`
  -- synchronous capability invocation. Wraps
  `capos_rt::RingClient::submit_call_borrowed_wait_forever`. Returns the
  kernel CQE result int directly.
- `capos_console_write_line(console_cap, text, text_len)` -- typed wrapper
  for the v0 smoke; handles capnp encoding internally so the C side does
  not need a capnp implementation.
- `capos_sys_exit(code)` -- raw `exit` syscall.
- `capos_sys_cap_enter(min_complete, timeout_ns)` -- raw `cap_enter`.
- `malloc` / `free` / `calloc` / `realloc` -- C heap shims that delegate
  to the Rust global allocator already initialized by `capos-rt`.

Build chain:

- `cargo build-libcapos` builds `libcapos.a` for
  `targets/x86_64-unknown-capos.json` with `-Zbuild-std=core,alloc`.
- C link: `clang --target=x86_64-unknown-none-elf -ffreestanding -nostdlib
  -static -fuse-ld=lld -Wl,-T,demos/linker.ld`. Links against
  `libcapos.a`. The capos-rt `_start` (already present in `libcapos.a`
  via the Rust dep) is the entry point; libcapos's `capos_rt_main` parks
  the Runtime in a static and dispatches to C `main(0, NULL)`.

Smoke (`make run-c-hello`):

- `system-c-hello.cue` boots c-hello as the init binary with one Console
  cap.
- C `main()` calls `capos_capset_get("console", ...)` -> calls
  `capos_console_write_line(console_cap, "[c-hello] hello from c-hello",
  ...)` -> returns 0 -> `capos_sys_exit(0)` via the trampoline return.
- `tools/qemu-c-hello-smoke.sh` greps the kernel log for the marker
  line, the `proc: process N exited with code 0` line, and
  `sched: last process exited, halting`.

## Out Of Scope (v0)

Deferred to later libcapos phases:

- Generated typed wrappers per capability interface (currently only
  Console is wrapped because the smoke needs it).
- Transferred result-cap propagation across the C ABI (the v0 smoke does
  not pass caps).
- Multi-thread routing of the Runtime ring (the v0 substrate is
  single-threaded; the Runtime stash is a `RefCell<Option<Runtime>>`
  with manual `Sync` and a documented invariant).
- Argv/envp delivery (the trampoline always passes `argc=0, argv=NULL`;
  real argv waits on the LaunchParameters cap from the language-support
  planning Task L.3).
- Any POSIX surface (errno, fd table, file I/O, sockets, signals,
  fork/exec) -- belongs to the planned `libcapos-posix` layer.

## Validation Gates

Run before merging:

- `make fmt-check`
- `cargo build --features qemu`
- `cargo test-lib`
- `cargo test-config`
- `make capos-rt-check`
- `make libcapos`
- `make c-hello`
- `make run-smoke`
- `make run-c-hello`

## Closeout

When v0 is reviewed and merged, mark the WORKPLAN bullet `[x]` with the
closeout commit SHA and a minute-precision UTC timestamp. Successor work
(`libcapos-posix` scaffold, generated wrappers, multi-thread Runtime
routing) gets new plan files referenced from new WORKPLAN bullets; do
not append to this plan.
