ABI Evolution Policy
This policy governs externally visible capOS ABIs:
- Cap’n Proto schema in
schema/capos.capnp. - Generated schema bindings checked by
make generated-code-check. - Ring and bootstrap ABI constants and layouts in
capos-config/src/ring.rs,capos-config/src/capset.rs, andcapos-abi/src/lib.rs. - Debug/log formats only when a document explicitly declares them stable.
The current project is still a research tree, not a released platform with a public compatibility promise. Even so, schema and ring changes must follow this policy before external clients, host tools, or out-of-tree runtimes depend on them.
Design Grounding
This policy is grounded in current capOS docs and the checked-in prior-art notes that apply to schema and transport evolution:
docs/architecture/capability-ring.mdfor the implemented process-wide ring, fixed 64-byteCapSqe, fixed 32-byteCapCqe, opcode boundary, and current completion semantics.docs/proposals/ring-v2-smp-proposal.mdfor the undecided future per-thread-ring version-negotiation shape.docs/proposals/error-handling-proposal.mdfor the transport/application error split and unsupported-operation behavior.docs/trusted-build-inputs.mdfor generated-code drift checks and pinned Cap’n Proto tooling.docs/design-risks-register.mdfor the prior open ABI compatibility and Ring v2 compatibility questions.docs/research/capnp-error-handling.mdfor Cap’n Proto exception and schema error-model precedent. Other files indocs/research/were listed during review; no OS scheduling, filesystem, networking, or hardware prior-art file directly changes this schema/ring ABI policy.
Compatibility Classes
Every ABI change must name one class in its task, review, or commit message.
| Class | Meaning | Required handling |
|---|---|---|
| Compatible addition | Existing clients keep working without recompilation or behavior change. | Add tests or generated-code drift evidence. Update docs when semantics matter. |
| Compatible tightening | Existing malformed or previously unspecified inputs fail earlier or more specifically. | Document the rejected shape and expected error. Add hostile coverage when reachable from userspace. |
| Soft deprecation | Old shape still works, but new callers should stop using it. | Mark the field/method/opcode as deprecated in docs and keep a replacement path live through the deprecation window. |
| Breaking change | Existing valid clients can fail, observe different semantics, or require regenerated code. | Requires a proposal or backlog plan, migration notes, compatibility proof or explicit break decision, and REVIEW_FINDINGS.md/risk updates when relevant. |
| Internal-only | Not visible outside one crate or generated artifact and not serialized, mapped, or invoked across a boundary. | Normal code review; do not label serialized or mapped data as internal-only. |
Cap’n Proto Schema Rules
Schema interface IDs, method ordinals, struct field ordinals, enum discriminants, union tags, and named constants are stable once checked in.
Allowed compatible changes:
- Add a new field with a new ordinal and a default value that old readers can safely ignore.
- Add a new method with a new ordinal when old clients do not need it.
- Add a new result union arm only when old clients already treat unknown or unsupported domain outcomes as a controlled failure.
- Add a new interface or struct with a fresh ID/name.
- Add documentation that narrows previously undocumented behavior without changing wire compatibility.
Disallowed without a breaking-change plan:
- Reuse a removed field, method, enum, or union ordinal.
- Change the meaning, type, units, authority, or lifetime of an existing field.
- Rename a schema item when generated code or logs expose the old name as a public integration surface.
- Make an optional/defaulted field mandatory for existing callers without a versioned fallback.
- Replace a schema result union with a transport error or vice versa without an error-layer migration note.
Removed schema space stays reserved. If a field or method is retired, leave a comment at the old ordinal explaining why it is reserved and where the replacement lives.
Ring ABI Rules
The ring ABI is a fixed-layout shared-memory contract. CapSqe, CapCqe,
ring header fields, opcodes, flags, transfer descriptor layout, CQE result
codes, and fixed virtual addresses are kernel/userspace ABI.
Rules for the current process-wide ring:
- Do not change the size, alignment, byte order, or meaning of an existing ring struct field without a breaking-change plan.
- Preserve objective layout checks for current ABI structs. At minimum,
capos-config/src/ring.rsmust keep compile-time checks forCapSqe,CapCqe,CapTransferDescriptor, endpoint caller-session metadata, endpoint message headers, and ring capture records. Any new negotiated ring layout must add equivalent checked constants for SQE size, CQE size, transfer descriptor size, ring header offsets, SQE/CQE array offsets, and feature/version fields. - Do not change
SQE_ARRAY_OFFSET,CQE_ARRAY_OFFSET,SQ_ENTRIES,CQ_ENTRIES,RING_VADDR, or fixed SQE/CQE sizes by arithmetic side effect. A change to any of those values is a layout change and must name its compatibility class. - Reserved SQE fields must be rejected unless the opcode explicitly defines them. New meanings for reserved fields require hostile tests that old kernels fail closed.
- New opcodes must start as reserved or unsupported. A reserved opcode should
return
CAP_ERR_UNSUPPORTED_OPCODE; malformed non-reserved opcodes should returnCAP_ERR_INVALID_REQUEST. - New flags must specify whether old kernels reject them, ignore them, or treat them as malformed. Silent ignore is allowed only for flags that cannot carry authority or resource effects.
- New negative CQE result codes must be appended as new constants. Existing negative result codes cannot be renumbered or repurposed.
- Capability transfer descriptors must continue to reject unknown reserved bits until a documented transfer mode consumes them.
Ring v2 or per-thread-ring work must declare whether it is:
- a negotiated compatible extension to the current ring page;
- a new ring layout selected by boot/runtime version negotiation; or
- an intentional ABI break.
That decision belongs in the Ring v2 proposal/backlog before implementation.
Version Negotiation
When an ABI cannot be evolved by compatible addition, introduce an explicit version gate instead of inferring compatibility from struct size or accidental behavior.
Acceptable gates include:
- manifest or boot-package
schemaVersionfields; - a future runtime boot-info field that names ring layout and feature bits;
- interface methods that return a structured unsupported-version result;
- manifest/tooling checks that reject unsupported data versions before boot.
Unsupported versions must fail closed with a stable, documented error. A client must not need to parse debug text to distinguish “unsupported version” from “malformed input”.
Deprecation Window
Before external consumers exist, a deprecation may be removed after the
replacement path, docs, and smokes land in main.
After external consumers are declared for an ABI, deprecated schema or ring surfaces must remain for at least one full selected milestone after the replacement is documented and tested. Removing them earlier is a breaking change and must be called out as such.
Deprecation notes must name:
- the old field, method, opcode, flag, or constant;
- the replacement;
- the last proof target that still exercises the old shape;
- the planned removal condition.
Review Gates
Schema or ring ABI changes must include the relevant checks:
make generated-code-checkforschema/capos.capnpchanges.cargo test-configfor manifest/schema validation changes.cargo test-ring-loomfor ring queue protocol changes.- Compile-time layout assertions and host tests for ring struct size, alignment, offsets, entry counts, and fixed virtual addresses when a ring layout changes.
cargo test-libfor CapTable/capability transfer semantics.- A focused QEMU smoke when a userspace-visible behavior changes.
make docsfor policy or manual changes.
Reviewers should reject ABI changes that lack a compatibility class, migration notes for breaking behavior, or an unsupported-version/error story for new version gates.
Current Open ABI Decisions
- Ring v2 backward compatibility remains undecided. Until it is decided, do not claim per-thread rings are compatible with the current process-wide ring.
- Production release reproducibility remains separate from ABI compatibility.
Final ISO, manifest, and embedded ELF checksums are tracked in
docs/trusted-build-inputs.mdandREVIEW_FINDINGS.md.