C API and bindings architecture
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Read from modules/c-api (abi.h, the detail header, CMakeLists.txt), the ABI documents and the module probes; nothing was built. The external repositories are evidence for their own pinned revisions, and the C library was not built or tested for this page.
The CNA repository contains exactly one binding foundation: an explicitly experimental C API, modules/c-api, whose ABI version at this snapshot is 0.29.0. The C#, Java, TypeScript, Python, Rust, Swift, Go and Ruby projects are separate repositories that consume that ABI; they are not generated from CNA, they pin their own ABI generation (0.21.x), and they must not be described as automatically compatible with this snapshot. This page states the boundary rules a C-ABI change has to respect and what a maintainer owes the bindings when it moves; the routes, handle registry and per-layer policies are in C API internals.
Native C API
modules/c-api supplies 61 public C headers under CNA/C/, a shared library cna_c_api (alias CNA::CApi; a static library under Emscripten, which is linked into the cna_c_api_wasm module target), an optional relocatable static archive and a WebAssembly adapter path. It is an opt-in target: CNA_BUILD_C_API defaults to OFF and requires CNA_ENABLE_NET=ON because the library links the GamerServices and Net modules (see the module graph). On ELF a linker version script exports cna_* and nothing else, and the export list for the WebAssembly module is generated from the headers rather than maintained by hand.
- Opaque, generation-checked handles keep C++ types out of the ABI. A
CNA_Handleis a 64-bit value holding a slot index and a generation; a zero, stale, wrong-kind or foreign handle answersCNA_RESULT_INVALID_HANDLE. The registry records the object kind and the creating thread. - No exception crosses the boundary. Entry points run inside
CallWithExceptionBarrier(CnaCApiDetail.hpp), which maps C++ exceptions to one of the fixedCNA_Resultcodes (0 success through 14 buffer too small) and records a per-thread last-error message that query routes never overwrite. Callbacks run synchronously on the game's thread with borrowed handles, and a callback that fails stops the game withCNA_RESULT_CALLBACK. - The ABI version is exact, not stable.
CNA_ABI_VERSION(0.29.0, inabi.h) is unrelated to the product version, and while the ABI is 0.x an incompatible change takes a minor increment, release notes and a regenerated baseline. Two such steps (0.28.0 and 0.29.0) came after the last commit of every external binding, so consumers must validate the exact exported version instead of assuming compatibility. The history is inABI_VERSIONING.mdand summarized on the C API guide. - Build status is source-level. No CNA CI workflow builds the C library at this snapshot: the C-API workflows are header, JSON and documentation gates, and CNA's own release gate reports the surface as not ready. Whether the library builds and passes at exactly this commit was not established for this page.
Boundary checklist
- State who creates and destroys every handle, and in what order. Owned children are released before their parent, on the creating thread;
cna_game_destroyanswersCNA_RESULT_INVALID_STATEwhile owned graphics resources, content managers, audio resources or components exist, and refuses from inside a lifecycle callback. A wrong-thread call answersCNA_RESULT_THREAD, so a foreign garbage collector on another thread cannot be relied on for teardown order. - Define whether every string and buffer is borrowed, copied or transferred, and for how long. The conventions are UTF-8
CNA_StringViewin, count-then-copy out (a too-small buffer is refused with no partial write),CNA_Boolrestricted to 0 and 1, and versioned input structs that start withstruct_sizeandstruct_version. - Convert every failure to the C error contract before returning; an exception that escapes the barrier is a defect.
- Keep the public headers valid C: they are compiled alone and through the umbrella at a C99 floor (with C11 and C17, and C++11 to C++17 as required cells), so do not leak C++ syntax, namespaces, templates or C++-only types into them.
- Test double-destroy, null and wrong-type handles, callback re-entry, wrong-thread calls and shutdown order.
The tests live under modules/c-api/tests: 85 pure-C programs, five C++ tests (including HandleRegistryTest.cpp and BoundaryDetailTest.cpp, registered as CApi_HandleRegistry and CApi_BoundaryDetail) and two libFuzzer sources. They are built and registered only when -DCNA_BUILD_C_API=ON, and none was executed for this page. The lifetime rules in more detail are CNA's own OWNERSHIP.md, HANDLES.md and CALLBACKS_AND_THREADING.md, and the ownership map places handles beside the C++ lifetimes.
External language repositories
| Binding | Repository | ABI generation targeted | What this page supports |
|---|---|---|---|
| C# / .NET | libcna/cna-cs | 0.21.x | Separate versioning; its README and compatibility data are authoritative |
| Java | libcna/cna-java | 0.21.x | Separate generated and handwritten boundary; verify the native ABI revision |
| TypeScript | libcna/cna-ts | 0.21.x | Separate project with its own native-bridge and WebAssembly decisions |
| Python | libcna/cna-python | 0.21.x | Separate release cadence; do not infer parity from repository existence |
| Rust | libcna/cna-rust | 0.21.x | Separate crate and repository contract; verify the ownership mapping |
| Swift | libcna/cna-swift | 0.21.x | Separate package; its loader policy is its own |
| Go | libcna/cna-go | 0.21.x | Separate module; its loader policy is its own |
| Ruby | libcna/cna-ruby | 0.21.x (also admits 0.7.0) | Separate gem; its loader admits only its own version set |
Compatibility is not established by this source revision. Every public binding targets ABI 0.21.x and this snapshot exports 0.29.0; five of them refuse a 0.29.0 library by an exact-version rule, and none has been qualified against it. Consumer projects are evidence for their own pinned revision, not for this one. Before changing the native API, read each repository's current compatibility matrix and CI; never claim that the latest binding works with the latest CNA. The per-binding pinned commits and loader behavior are tabulated on the C API guide.
Propagating a native API change
- Change and test the owning C++ module first.
- Decide whether the operation belongs in the experimental C ABI at all. The generated coverage inventory (
generate_coverage_inventory.py, output inthe C API coverage document) maps every public C++ symbol to implemented, partial, planned or not applicable; the C API guide gives the counts. - Update the C header, the implementation, the symbol and export rules and the C API tests together. The header half of the ABI baseline gate (
CApiAbiHeaderBaseline, fromgenerate_abi_baseline.py--checkagainstabi_baseline.json) runs in an ordinary build and names a moved field, a changed constant or a vanished export as a break;CApiAbiBaselineandCApiDeclaredExportsadditionally need the built library on ELF, andCApiReleaseGatereports the release verdict. Additive changes are re-recorded; an incompatible one requires the version increment above. - For each maintained external binding, pin a compatible native artifact, update its generated declarations and its handwritten ownership and marshaling code, and let its own compatibility policy decide what it accepts. Removing a route (as 0.29.0 did for one) breaks a binding at compile or link time, not at run time.
- Run binding tests under sanitizers where the foreign runtime permits it, and treat the C library's own smoke and sanitizer configurations as the C-side evidence.
The step-by-step recipe is update the C API. The C# binding, Java and Python pages describe how those layers apply the ownership rules.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- C API evidence, coverage inventory and release gate — How far the evidence for CNA's C ABI 0.29.0 reaches, what each release-gate criterion really checks, why the gate measures two unmet criteria at 009d40f5, and how the coverage inventory classifies modules.
- Framework services and ecosystem quick reference — A map of CNA's input, audio, media, device, network, gamer-services, storage and sharp-runtime types with their current boundaries and owner pages, plus the bindings and showcase applications around CNA.
- Native C API contract: admission, buffers, retention and route families — What CNA's C ABI 0.29.0 version checks admit, the exact count-then-copy protocol, callback and registration lifetimes, resources that retain others, caller-created devices and all 61 headers by family.
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- User guide
- C API: programming model · C API: ABI version policy and history · C API: bindings boundary · C API: gates and CI
- Architecture
- Architecture overview · Physical module dependency map
- Maintainer workflow
- I need to update the C API · Ownership: foreign handles
- Tests and validation
- Test architecture
- Reference
- Public header index: c-api · CMake option index