Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

capOS SDK Crate And Dual Transport Backlog

Detailed decomposition for the front-door capos SDK crate: one published crate whose typed capability clients run unchanged against two transports – the in-process capability ring (an application running inside capOS) and a remote connection (a host-side RPC client). This extends the Crate publication roadmap track with a concrete architecture and publication ordering, and consumes the remote transport planned in Remote Session CapSet Client. docs/tasks/README.md should point here when selecting slices; it should not inline the details.

Visible Outcome

  • A Rust author writes against typed capability clients (Console, Timer, EntropySource, VirtualMemory, and future caps) once. cargo add capos builds an in-system no_std application that reaches the kernel through the ring. cargo add capos --no-default-features --features remote builds a host program that reaches a capOS instance through the remote session transport, using the same client API.
  • The crates.io names capos (front-door facade) and the capos-* family (capos-abi, capos-lib, capos-config, capos-rt) are published with real content, stable versioning, rendered docs, and license/metadata.

Why This Is High Priority

Two reasons, one architectural and one external:

  • Architecture. The Cap’n Proto-first design already treats each process as a capnp-rpc vat and the per-process ring as that vat’s connection to the kernel (design principle 5). “In-system app” and “remote client” differ only in the transport under the typed clients, so a single SDK with a transport seam is the natural shape rather than two parallel client stacks.
  • Namespace contention. The capos/capos-* crate names overlap with an unrelated capability-OS effort already publishing under the same prefix on crates.io. crates.io is a flat, first-come namespace, so publishing the real reusable crates early (and reserving the bare capos facade) both prevents name contention and establishes a dated public-use record. Publish crates with real content; do not register empty placeholder names, which the crates.io policy can reclaim.

Transport Seam

The seam is a Transport trait (working name) that the typed capability clients depend on instead of the concrete ring. It must express the existing ring opcode semantics faithfully rather than collapse them:

  • call(cap, interface_id, method_id, request_bytes) -> CallHandle – maps to the ring CALL SQE.
  • completion retrieval (poll / wait) – maps to consuming a CQE after cap_enter.
  • release(cap) – maps to the local RELEASE SQE.
  • server-side recv / return_(call, response_bytes, result_caps) – maps to RECV / RETURN for endpoint-owning servers. The first SDK slice may scope to the client side (CALL/RELEASE + completions) and add the server side when an endpoint-owning userspace service consumes the SDK.

Two implementations:

  • RingTransport (no_std, default ring feature): wraps the existing capos-rt single-owner ring client and cap_enter. This is current behavior moved behind the trait, not new behavior.
  • RemoteTransport (std, remote feature): connects over the remote session transport, authenticates through SessionManager/AuthorityBroker, holds a forwarded RemoteCapSet, and dispatches typed calls over the same length-prefixed DTO gateway used by tools/remote-session-client/.

Crate Layout

craterolestd/no_std
caposfront-door facade: prelude, re-exports runtime + typed clients, transport selection by featureno_std core; std only behind remote
capos-rtring runtime (_start, syscalls, ring client); provides RingTransportno_std
capos-abiABI/policy constantsno_std
capos-libhost-testable pure logic (ELF, CapTable, ring/SQE validation)no_std + alloc
capos-configmanifest/CUE loader, ring structsno_std + alloc

Feature flags on capos: ring (default, in-system, no_std), remote (host client, pulls std + the remote transport deps). The shared core – typed capability clients plus capnp encode/decode (capnp 0.25 is no_std + alloc) – must stay no_std; std is confined to the remote transport. Open decision: whether RemoteTransport lives in capos behind remote or in a separate capos-remote crate the facade re-exports.

Honesty / Caveats

  • The remote transport is transitional. The remote session path is currently length-prefixed schema-framed DTOs, not standard capnp-rpc with live object proxies; the rewrite is gated on a reviewed capOS userspace async runtime or a sync-friendly Cap’n Proto RPC adapter (see Remote Session CapSet Client, Gate 1 / Task 1). So the first remote form proxies through the trusted host backend boundary; it is not arbitrary remote capability invocation with promise pipelining. Do not document it as live guest-wire capnp-rpc.
  • capnp-rpc 0.25 is std-only and needs a futures executor, which is why the remote transport is std-only and host-side by necessity. This matches the in-system/no_std versus host/std split rather than fighting it.
  • The trust models differ but the client API does not: in-system the kernel hands an unforgeable bootstrap CapSet; remote, the client only ever sees caps explicitly forwarded into its authenticated session. Keep the API identical and the authority boundary explicit.

Publication Decision

Decision recorded 2026-05-22 23:41 UTC: the SDK track publishes real crates only, never empty placeholder packages. This follows the Cargo publishing model where crate names are first-come, first-served, published versions are permanent, and publishers should fill out license, repository, readme, description, keyword, and category metadata before upload. It also follows the crates.io usage policy against packages that exist only to reserve a name for a prolonged period without genuine functionality or development activity. The exact planned crates.io names (capos, capos-abi, capos-capnp-build, capos-config, capos-lib, and capos-rt) were not present in the crates.io API or sparse index when checked on 2026-05-22 23:39 UTC, and were re-confirmed unclaimed on 2026-06-02 16:10 UTC; the adjacent capos-bitstruct crate exists under the unrelated cap-os/rust-tools repository and is the visible namespace contention signal. libcapos and libcapos-posix are not crates.io crates (they ship as release artifacts, see item 7), so their names are deliberately left unclaimed on crates.io – an accepted residual risk. Re-check the registry immediately before any real publish.

The publish set and order are:

  1. capos-abi 0.1.0 – shared no_std ABI and policy constants.
  2. capos-capnp-build 0.1.0 – build-time schema generation helper used by capos-config; it is a real package because capos-config cannot publish with an unpublished path-only build dependency.
  3. capos-config 0.1.0 – manifest/config/ring structs and generated schema module built from packaged schema source; depends on capos-abi and capos-capnp-build.
  4. capos-lib 0.1.0 – reusable no_std + alloc pure logic; depends on capos-abi and capos-config.
  5. capos-rt 0.1.0 – in-system no_std runtime and ring transport, published with the bare facade slice after the transport seam lands.
  6. capos 0.1.0 – front-door facade with ring as the default no_std feature and remote reserved as a std feature until the remote transport slice closes.
  7. libcapos / libcapos-posix – C-substrate distribution. Decision 2026-06-02 16:10 UTC: these ship as release artifacts only (prebuilt libcapos.a / libcapos_posix.a plus the capos/*.h headers attached to a GitHub release/tarball), not crates.io crates, because their consumers are C programs that link the archive, not Rust crates run through cargo add, and they build only for the custom x86_64-unknown-capos target. The artifact build is bundled into the same explicit operator publish wave as the Rust crates above (built via make libcapos / make libcapos-posix, verified through the existing C smokes). Their crates.io names are intentionally not reserved (accepted residual risk).

Do not reserve capos-remote yet. If slice 4a proves the remote backend should live outside the facade crate, publish capos-remote with real host transport code in that slice.

The MSRV target for Rust crates was 1.85.0 (the Rust 2024 edition floor) until the first-public-release slice verified the package set against stable. The verified MSRV is stable Rust 1.88.0: capos-config uses let chains in if/while conditions (&& let Some(..) = ..), stabilized in 1.88.0, so the set does not build on 1.85.0. rust-version = "1.88.0" is set on all four published crates. The current repository still builds the OS with the date-pinned nightly-2026-04-20 toolchain. capos-rt must document the capOS target toolchain requirement separately if its package cannot be built on stable for the custom userspace target.

The license gate is satisfied: the repository carries LICENSE-APACHE, LICENSE-MIT, and LICENSING.md, and per LICENSING.md the published SDK crates use MIT OR Apache-2.0 (the kernel/system is Apache-2.0-only and is not in the publish set). The publish metadata uses that SPDX expression in the license field of capos-abi, capos-capnp-build, capos-config, capos-lib, capos-rt, and capos. Each carries repository/readme/description/keywords/categories metadata and docs.rs settings.

Generated Cap’n Proto bindings do not ship as a separate published crate in the first release set. capos-config ships the schema source needed to build its own generated module, and capos-capnp-build remains the single no-std patch helper for that generation path. The publish slice must make the capos-config package self-contained instead of relying on repository-sibling paths such as ../schema/capos.capnp. A separate generated-bindings crate is deferred until an external consumer needs schema bindings without the manifest/config crate.

Versioning Policy

The published crates – capos, capos-rt, capos-abi, capos-lib, capos-config, and capos-capnp-build – follow one policy:

  • Pre-1.0 SemVer. The set starts at 0.1.0. Per Cargo’s SemVer rules a 0.y.z release treats the minor field as the breaking-change field: a breaking API/ABI change bumps the minor (0.1 -> 0.2); a backward-compatible addition or fix bumps the patch (0.1.0 -> 0.1.1). Treat the whole 0.x series as unstable and do not promise API stability until a 1.0.0 is deliberately cut.
  • Schema/ABI contract maps to a breaking bump. These crates encode the capOS wire contract: capos-config carries the generated Cap’n Proto bindings and the ring CapSqe/CapCqe structs, capos-abi carries the policy/quota constants, and the typed clients in capos-rt/capos encode the SQE CALL/RELEASE wire format. A change to schema/capos.capnp, the generated bindings, the ring wire layout, or a typed-client method’s wire encoding is a breaking change and bumps the minor field (pre-1.0). The SDK is a schema consumer; the schema change lands under its owning plan, and this set’s version bump follows it.
  • Lockstep versioning across the set. Because the crates share the wire contract and depend on each other by exact path/version, publish them at the same version and bump them together rather than independently. A consumer pinning capos = "0.1" then gets a coherent capos-rt/capos-config/ capos-abi graph. Independent per-crate versioning is deferred until a crate has external consumers and a stability story of its own; revisit at 1.0.
  • MSRV is stable Rust 1.88.0. Verified by the slice-2 publish dry-run: capos-config uses let chains (&& let Some(..) = ..) stabilized in 1.88.0, so the set does not build on the Rust 2024 edition floor 1.85.0. rust-version = "1.88.0" is set on the four published workspace crates. The OS itself still builds only on the date-pinned nightly; the MSRV applies to the host-target build of the reusable crates, not the kernel.

Publish Dry-Run Gate

make sdk-publish-dry-run is the repeatable, one-command reproduction of the slice-2 publish verification. It runs, on the host target:

  1. A coordinated multi-package cargo publish --dry-run over capos-abi -> capos-capnp-build -> capos-config -> capos-lib in dependency order. Cargo packages each crate, unpacks the earlier ones into a temporary registry, and verify-builds the later ones against them, so it fails loudly if publish metadata, packaging (including the capos-config packaged schema/capos.capnp), or the dependency order regresses. Coordinated multi-package dry-run is nightly-only, so this step runs on the repo-pinned nightly.
  2. An MSRV-floor cargo +1.88.0 build of the same set, catching a regression that would need a newer toolchain than the recorded MSRV (the nightly dry-run alone would not).

The gate is prep-only: every dry-run upload aborts, and no real cargo publish runs. Like dependency-policy-check, it is a focused target, not part of make check, because the verify-builds are slow.

capos-rt and capos are part of the publish set but are not in this host gate: they build only on the custom userspace target/code model, so a host-target cargo publish --dry-run verify-build does not apply. The release gate verifies their capOS-target builds via make capos-sdk-check rather than a host dry-run. The initial publication used the local Cargo API-token path after the final crates.io name re-check; subsequent releases can use .github/workflows/publish-crates.yml after each crate’s trusted publisher is configured on crates.io for this repository, workflow, refs/heads/main, and the crates-io-publish GitHub environment.

Ordered Slices

The near-term high-priority slices (1-3, 5) do not depend on the capnp-rpc transport rewrite and have landed. Slice 4 is split: slice 4a (a transitional RemoteTransport over the existing host DTO backend) can ship now, while slice 4b (the live-proxy capnp-rpc upgrade) is gated on the remote-session async-runtime decision.

  1. Publish-set + reservation decision (docs-status, closed 2026-05-22 23:41 UTC). The decision above pins the publish order, exact crate names, MSRV target, feature-flag story, license gate, metadata requirements, and generated-binding packaging decision.
  2. First public release of existing layers (behavior, prep landed 2026-05-23; 0.1.0 published 2026-06-05). Publish metadata (description, MIT OR Apache-2.0 license, repository, keywords, categories, rust-version = 1.88.0, README, docs.rs config) added to capos-abi, capos-capnp-build, capos-config, and capos-lib. capos-config is now self-contained: it ships schema/capos.capnp (an in-repo symlink to the single repo-root schema, materialized into the package archive) and capos-capnp-build resolves it from the crate’s own manifest dir via generate_packaged_schema_bindings(). Verified with a coordinated cargo publish --dry-run of the set in dependency order plus a stable-1.88.0 build and a local docs render. The initial 0.1.0 versions were published from origin/main on 2026-06-05 through the local Cargo API-token path after the final crates.io name re-check. The capos-config docs.rs build is accommodated by a packaged generated-binding fallback used only when DOCS_RS is set, so the docs.rs sandbox no longer needs the external pinned capnp binary. The repository-URL rewrite is no longer a blocker: decision 2026-06-02 16:10 UTC keeps repository = "https://github.com/ei-grad/capos" for the first wave (publishing many crates from one repo is standard; the repository field can be updated later at repo-migration time without republishing). This claims the capos-* prefix with shipped code.
  3. Reserve bare capos + transport seam (behavior, closed 2026-05-23 23:07 UTC). capos-rt now defines the Transport trait (src/transport.rs): the client-side seam of submit_call / submit_call_with_copy_transfers / submit_call_borrowed_wait_forever (CALL), wait / try_complete (completion after cap_enter), and release_wait (local RELEASE). RingTransport is the existing single-owner RingClient viewed through the seam (current behavior, not new behavior); both RingClient and RuntimeRingClient implement Transport. The 189 client-side typed-client methods take &mut impl Transport; the result-cap-adopting methods stay on the concrete RuntimeRingClient because generalizing result-cap adoption across transports is later server-side/promise work. The new standalone capos/ facade crate re-exports the runtime, typed clients, the entry_point! macro, and a prelude behind the default ring feature; the later 4a slice made remote a host-only feature over the transitional DTO backend. QEMU proof: make run-spawn boots demos/timer-smoke, whose typed-client code now imports from capos instead of capos-rt, and asserts [timer-smoke] Timer now/sleep ok.. capos-rt and capos 0.1.0 were published with the slice-2 set after the final name re-check and make capos-sdk-check custom userspace target verification; the repository-URL rewrite is no longer a blocker – see slice 2.
  4. remote transport backend, split into 4a/4b.
    • 4a – transitional RemoteTransport (behavior, closed 2026-06-06). The capos facade’s remote feature now builds on the host target without the default ring feature, enables capos-rt/host-test for the shared typed clients, and provides RemoteTransport over the existing host DTO backend boundary used by tools/remote-session-client. RemoteTransport authenticates through the same DTO gateway, obtains forwarded caps through CapSetGet, assigns synthetic host-side cap ids, and proves SystemInfo.motd through SystemInfoClient::motd_wait over the current length-prefixed DTO wire without making the unpublished host tool crate part of the published capos package graph. Unsupported calls fail closed with ring-style transport completions. Negative-path hardening now covers wrong-interface and missing-cap denials, released local cap ids, remote denied calls, malformed and mismatched DTO responses, and disconnects during synchronous DTO calls. This is not blocked on the async-runtime decision and remains transitional host-backend proxying, not guest-wire capnp-rpc with promise pipelining.
    • 4b – live-proxy capnp-rpc upgrade (behavior, blocked). Replace the DTO wire under RemoteTransport with standard capnp-rpc framing and live object proxies. Gated on a reviewed capOS userspace async runtime or a sync-friendly Cap’n Proto RPC adapter, tracked by remote-session Gate 1 (docs/backlog/remote-session-capset-client.md). Do not block 4a on it.
  5. Versioning + publish CI (harness-hardening, closed 2026-05-24). The “Versioning Policy” section above pins pre-1.0 SemVer, the schema/ABI-to-breaking-bump mapping, lockstep versioning, and the 1.88.0 MSRV. make sdk-publish-dry-run reproduces the slice-2 publish verification in one command (coordinated multi-package cargo publish --dry-run over the four host-buildable crates in dependency order + an MSRV-floor build); see “Publish Dry-Run Gate”. The capos facade README.md documents the working ring default and the transitional remote feature. .github/workflows/publish-crates.yml runs the same release gates, obtains a short-lived crates.io token through trusted publishing only from refs/heads/main, skips versions already present on crates.io, and publishes the six crates in dependency order when its manual publish input is enabled with the current explicit user release instruction recorded in the dispatch input. Non-main publish=true dispatches and publish dispatches without that current instruction fail before any crates.io token is requested. The initial six-crate 0.1.0 release is complete; future releases use the workflow only after explicit user authorization for that release and once crates.io trusted publishers are configured for the six crates.

Conflict Surface

  • Owns: NEW capos/ facade crate, this backlog file, the roadmap “Crate publication” section, [package.metadata]/publish metadata on the published crates, and any new docs/proposals/capos-sdk-proposal.md.
  • Coordinates (do not run blindly in parallel):
    • capos-rt/ – the Transport-trait refactor of typed clients. Serial with other capos-rt client changes.
    • tools/remote-session-client/ and the Remote Session CapSet Client plan – the remote transport reuses that host transport. The capnp-rpc rewrite is owned there, not here.
  • Must NOT touch: schema/capos.capnp or tools/generated/ (the SDK is a schema consumer, not a producer) and kernel behavior. If a slice needs a schema change, it queues on the shared schema serial surface under the owning plan, not this one.

Grounding Files

  • docs/roadmap.md “Crate publication” track.
  • docs/backlog/remote-session-capset-client.md (remote transport, gating).
  • docs/proposals/remote-session-capset-client-proposal.md.
  • docs/proposals/userspace-binaries-proposal.md (the C substrate layer under the same SDK family).
  • docs/capability-model.md, README “Core Idea” (design principle 5: each process is a capnp-rpc vat; the ring is its connection).