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 c1c316b9; 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.46.0. Two language bindings are actively maintained: C (this ABI itself) and C# (CNA.NET), a separate repository that consumes it. Eight bindings are archived: Common Lisp, Go, Java, Python, Ruby, Rust, Swift and TypeScript (which includes JavaScript). They are no longer maintained, not part of the current support policy and without any forward compatibility guarantee, and their source and Git history are kept in CNA Lab. None of the external bindings is generated from CNA, each pins its own ABI generation, and none must 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 60 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 (on Linux-like hosts; under Emscripten it is merged from the wasm module's closure) 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. A host that owns its loop, such as a browser page, can drive a run one frame per call withcna_game_run_frame_ext(0.38.0; C++Game::RunFrameEXT), which delivers the same initialize, begin-run, exiting and end-run sequence ascna_game_run. - The ABI version is exact, not stable.
CNA_ABI_VERSION(0.46.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. Of the seventeen minors from 0.30.0 to 0.46.0, the six from 0.30.0 to 0.35.0 were incompatible, 0.36.0 to 0.39.0 and 0.41.0 were additive, 0.40.0, 0.42.0, 0.43.0 and 0.44.0 changed a rule only (the export set stayed the same, but a call that was refused before is now accepted), 0.45.0 removed enumeration constants and reserved their values, and 0.46.0 appended one native window-system value,CNA_NATIVE_WINDOW_SYSTEM_UIKIT, without changing a route, structure or export. Consumers must therefore 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. - State which thread every call is made on. The creating-thread rule has three documented exceptions, all opt-in or narrow: with
cna_game_set_foreign_thread_calls_ext(0.39.0, off by default) a call refused only because another thread created the handle is queued, the caller blocks, and the game thread runs it at the start of its next update or draw (cna_game_run_foreign_thread_calls_ext, 0.41.0, drains the queue while the game thread waits for such a thread);cna_sound_effect_playandcna_sound_effect_play_with_settingsanswer on any thread (0.43.0); and standalone devices may be created and destroyed on several threads at once. Driving or destroying the game is still refused off its thread, and a game thread that waits for a thread whose call it must serve deadlocks unless it drains the queue. - 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: 93 pure-C programs, four C++ tests (including HandleRegistryTest.cpp and BoundaryDetailTest.cpp, registered as CApi_HandleRegistry and CApi_BoundaryDetail) and one libFuzzer source. 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
Maintained. Together with C, which is the in-tree ABI above, this is CNA's actively maintained language binding.
| Binding | Repository | ABI generation targeted | What this page supports |
|---|---|---|---|
| C# / .NET (CNA.NET) | libcna/cna-dotnet (8219a6c, as recorded by CNA) | Exactly 0.46.0 | Beta and source-first, with separate versioning; it admits one reviewed ABI point, not a range, and its README and compatibility data are authoritative |
Archived — not maintained. These eight were developed to an advanced state and archived at their final revisions. Seven of them were requalified against C ABI 0.35.0 on 2026-09-30 before they moved into CNA Lab; Common Lisp had stopped earlier, at the 0.21–0.23 generation. They are not part of the current support policy, a native API change is not propagated to them, and there is no forward compatibility guarantee. Their source and Git history are kept under bindings/ in CNA Lab (libcna/cna-lab); where each final revision can be read is listed under archived bindings in the C API guide.
| Archived binding | Location in CNA Lab | ABI generation targeted | What this page supports |
|---|---|---|---|
| Common Lisp | bindings/cna-common-lisp | 0.21.0, 0.22.0 or 0.23.0 | Record of its final revision only; a selected subset, qualified on SBCL/Linux only |
| Go | bindings/cna-go | Minor 35 or later (requalified against 0.35.0) | Record of its final revision only; its loader policy is its own |
| Java | bindings/cna-java | Exactly 0.35.0 | Record of its final revision only; its internals page is kept for study |
| Python | bindings/cna-python | Exactly minor 35 | Record of its final revision only; its internals page is kept for study |
| Ruby | bindings/cna-ruby | Exactly 0.35.0 | Record of its final revision only; its loader admits only its own version set |
| Rust | bindings/cna-rust | Exactly minor 35 | Record of its final revision only; its crate and ownership mapping are not updated |
| Swift | bindings/cna-swift | Minor 35 or later (requalified against 0.35.0) | Record of its final revision only; its loader policy is its own |
| TypeScript and JavaScript | bindings/cna-ts | Exactly minor 35 | Record of its final revision only; its Node-API bridge and WebAssembly decisions are its own |
Compatibility is not established by this source revision. This snapshot exports 0.46.0. The maintained C# binding admits exactly 0.46.0: CNA's Apple campaign records that cna-dotnet 8219a6c moved its policy, constants, tests and CI pins from 0.44.0 to 0.46.0 after an export-by-export comparison, and that 0.45.0 was never admitted (AM4-217 in plan_apple_m4.md). That revision was not read for this page, so the admission is CNA's record, not a run. The archived bindings target earlier generations: Common Lisp, Java, Python, Ruby, Rust and TypeScript refuse a 0.46.0 library by an exact-version rule, and Go and Swift would admit it by a minimum-minor rule but were qualified only against 0.35.0. Only the maintained C# binding is expected to migrate. 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, maintained and archived, 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 the maintained external binding (C#), 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 several of the 0.30.0 to 0.35.0 steps did) breaks a binding at compile or link time, not at run time; a rule-only step (0.40.0, 0.42.0 to 0.44.0) passes every link check but changes which calls succeed, so it needs a review of the binding's own refusals and threading.
- 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 eight archived bindings are not updated: a native change is not propagated to them, and nothing in CNA's release process waits for them.
The step-by-step recipe is update the C API. The C# binding page and the archived 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.46.0 reaches, what each release-gate criterion really checks, what the gate reports for the 631 public C++ declarations still planned for C mapping, 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.46.0 version checks admit, the exact count-then-copy protocol, callback, registration and thread rules, resources that retain others, caller-created devices and all 60 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
- Internals
- C API internals · C# binding internals · Java binding internals (archived) · Python binding internals (archived)
- 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