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 caposbuilds an in-systemno_stdapplication that reaches the kernel through the ring.cargo add capos --no-default-features --features remotebuilds 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 thecapos-*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 barecaposfacade) 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 ringCALLSQE.- completion retrieval (
poll/wait) – maps to consuming aCQEaftercap_enter. release(cap)– maps to the localRELEASESQE.- server-side
recv/return_(call, response_bytes, result_caps)– maps toRECV/RETURNfor 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, defaultringfeature): wraps the existing capos-rt single-owner ring client andcap_enter. This is current behavior moved behind the trait, not new behavior.RemoteTransport(std,remotefeature): connects over the remote session transport, authenticates throughSessionManager/AuthorityBroker, holds a forwardedRemoteCapSet, and dispatches typed calls over the same length-prefixed DTO gateway used bytools/remote-session-client/.
Crate Layout
| crate | role | std/no_std |
|---|---|---|
capos | front-door facade: prelude, re-exports runtime + typed clients, transport selection by feature | no_std core; std only behind remote |
capos-rt | ring runtime (_start, syscalls, ring client); provides RingTransport | no_std |
capos-abi | ABI/policy constants | no_std |
capos-lib | host-testable pure logic (ELF, CapTable, ring/SQE validation) | no_std + alloc |
capos-config | manifest/CUE loader, ring structs | no_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-rpcwith 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 firstremoteform proxies through the trusted host backend boundary; it is not arbitrary remote capability invocation with promise pipelining. Do not document it as live guest-wirecapnp-rpc. capnp-rpc0.25 isstd-only and needs a futures executor, which is why theremotetransport isstd-only and host-side by necessity. This matches the in-system/no_stdversus host/stdsplit 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:
capos-abi0.1.0– sharedno_stdABI and policy constants.capos-capnp-build0.1.0– build-time schema generation helper used bycapos-config; it is a real package becausecapos-configcannot publish with an unpublished path-only build dependency.capos-config0.1.0– manifest/config/ring structs and generated schema module built from packaged schema source; depends oncapos-abiandcapos-capnp-build.capos-lib0.1.0– reusableno_std + allocpure logic; depends oncapos-abiandcapos-config.capos-rt0.1.0– in-systemno_stdruntime and ring transport, published with the bare facade slice after the transport seam lands.capos0.1.0– front-door facade withringas the defaultno_stdfeature andremotereserved as astdfeature until the remote transport slice closes.libcapos/libcapos-posix– C-substrate distribution. Decision2026-06-02 16:10 UTC: these ship as release artifacts only (prebuiltlibcapos.a/libcapos_posix.aplus thecapos/*.hheaders attached to a GitHub release/tarball), not crates.io crates, because their consumers are C programs that link the archive, not Rust crates run throughcargo add, and they build only for the customx86_64-unknown-capostarget. The artifact build is bundled into the same explicit operator publish wave as the Rust crates above (built viamake 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 a0.y.zrelease 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 whole0.xseries as unstable and do not promise API stability until a1.0.0is deliberately cut. - Schema/ABI contract maps to a breaking bump. These crates encode the
capOS wire contract:
capos-configcarries the generated Cap’n Proto bindings and the ringCapSqe/CapCqestructs,capos-abicarries the policy/quota constants, and the typed clients incapos-rt/caposencode the SQE CALL/RELEASE wire format. A change toschema/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 coherentcapos-rt/capos-config/capos-abigraph. Independent per-crate versioning is deferred until a crate has external consumers and a stability story of its own; revisit at1.0. - MSRV is stable Rust
1.88.0. Verified by the slice-2 publish dry-run:capos-configusesletchains (&& let Some(..) = ..) stabilized in1.88.0, so the set does not build on the Rust 2024 edition floor1.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:
- A coordinated multi-package
cargo publish --dry-runovercapos-abi -> capos-capnp-build -> capos-config -> capos-libin 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 thecapos-configpackagedschema/capos.capnp), or the dependency order regresses. Coordinated multi-package dry-run is nightly-only, so this step runs on the repo-pinned nightly. - An MSRV-floor
cargo +1.88.0 buildof 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.
- Publish-set + reservation decision (
docs-status, closed2026-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. - First public release of existing layers (
behavior, prep landed2026-05-23;0.1.0published2026-06-05). Publish metadata (description,MIT OR Apache-2.0license, repository, keywords, categories,rust-version = 1.88.0, README, docs.rs config) added tocapos-abi,capos-capnp-build,capos-config, andcapos-lib.capos-configis now self-contained: it shipsschema/capos.capnp(an in-repo symlink to the single repo-root schema, materialized into the package archive) andcapos-capnp-buildresolves it from the crate’s own manifest dir viagenerate_packaged_schema_bindings(). Verified with a coordinatedcargo publish --dry-runof the set in dependency order plus a stable-1.88.0build and a local docs render. The initial0.1.0versions were published fromorigin/mainon2026-06-05through the local Cargo API-token path after the final crates.io name re-check. Thecapos-configdocs.rs build is accommodated by a packaged generated-binding fallback used only whenDOCS_RSis set, so the docs.rs sandbox no longer needs the external pinnedcapnpbinary. The repository-URL rewrite is no longer a blocker: decision2026-06-02 16:10 UTCkeepsrepository = "https://github.com/ei-grad/capos"for the first wave (publishing many crates from one repo is standard; therepositoryfield can be updated later at repo-migration time without republishing). This claims thecapos-*prefix with shipped code. - Reserve bare
capos+ transport seam (behavior, closed2026-05-23 23:07 UTC).capos-rtnow defines theTransporttrait (src/transport.rs): the client-side seam ofsubmit_call/submit_call_with_copy_transfers/submit_call_borrowed_wait_forever(CALL),wait/try_complete(completion aftercap_enter), andrelease_wait(localRELEASE).RingTransportis the existing single-ownerRingClientviewed through the seam (current behavior, not new behavior); bothRingClientandRuntimeRingClientimplementTransport. The 189 client-side typed-client methods take&mut impl Transport; the result-cap-adopting methods stay on the concreteRuntimeRingClientbecause generalizing result-cap adoption across transports is later server-side/promise work. The new standalonecapos/facade crate re-exports the runtime, typed clients, theentry_point!macro, and apreludebehind the defaultringfeature; the later 4a slice maderemotea host-only feature over the transitional DTO backend. QEMU proof:make run-spawnbootsdemos/timer-smoke, whose typed-client code now imports fromcaposinstead ofcapos-rt, and asserts[timer-smoke] Timer now/sleep ok..capos-rtandcapos0.1.0were published with the slice-2 set after the final name re-check andmake capos-sdk-checkcustom userspace target verification; the repository-URL rewrite is no longer a blocker – see slice 2. remotetransport backend, split into 4a/4b.- 4a – transitional
RemoteTransport(behavior, closed2026-06-06). Thecaposfacade’sremotefeature now builds on the host target without the defaultringfeature, enablescapos-rt/host-testfor the shared typed clients, and providesRemoteTransportover the existing host DTO backend boundary used bytools/remote-session-client.RemoteTransportauthenticates through the same DTO gateway, obtains forwarded caps throughCapSetGet, assigns synthetic host-side cap ids, and provesSystemInfo.motdthroughSystemInfoClient::motd_waitover the current length-prefixed DTO wire without making the unpublished host tool crate part of the publishedcapospackage 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-wirecapnp-rpcwith promise pipelining. - 4b – live-proxy
capnp-rpcupgrade (behavior, blocked). Replace the DTO wire underRemoteTransportwith standardcapnp-rpcframing 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.
- 4a – transitional
- Versioning + publish CI (
harness-hardening, closed2026-05-24). The “Versioning Policy” section above pins pre-1.0 SemVer, the schema/ABI-to-breaking-bump mapping, lockstep versioning, and the1.88.0MSRV.make sdk-publish-dry-runreproduces the slice-2 publish verification in one command (coordinated multi-packagecargo publish --dry-runover the four host-buildable crates in dependency order + an MSRV-floor build); see “Publish Dry-Run Gate”. ThecaposfacadeREADME.mddocuments the workingringdefault and the transitionalremotefeature..github/workflows/publish-crates.ymlruns the same release gates, obtains a short-lived crates.io token through trusted publishing only fromrefs/heads/main, skips versions already present on crates.io, and publishes the six crates in dependency order when its manualpublishinput is enabled with the current explicit user release instruction recorded in the dispatch input. Non-mainpublish=truedispatches and publish dispatches without that current instruction fail before any crates.io token is requested. The initial six-crate0.1.0release 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 newdocs/proposals/capos-sdk-proposal.md. - Coordinates (do not run blindly in parallel):
capos-rt/– theTransport-trait refactor of typed clients. Serial with other capos-rt client changes.tools/remote-session-client/and the Remote Session CapSet Client plan – theremotetransport reuses that host transport. Thecapnp-rpcrewrite is owned there, not here.
- Must NOT touch:
schema/capos.capnportools/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).