Rust SDK facade and staged C, Swift and Kotlin binding boundary
Document status:
verified. Canonical source.
Summary
The safe common denominator for every integration is not a Rust struct graph or an MCP server. It is a bounded byte-oriented SDK façade over the canonical codecs, deterministic package, semantic operations and diagnostics. Native Rust callers use it directly; WASM, CLI, MCP and later foreign-language bindings translate only their transport and ownership conventions.
Rust’s native ABI is explicitly unstable. extern "C" uses the target’s C
calling convention, but a usable library must still define representations,
allocation ownership, destructors, errors, panic behavior, threads and symbols.
cbindgen generates C/C++ headers from an existing public C API; it does not
design or prove that API. UniFFI generates a shared-library FFI layer and
high-level Swift/Kotlin/Python/Ruby bindings from an object model and is used in
Firefox, but its own guide says shipping platform artifacts remains the user’s
responsibility. UniFFI is production-used yet pre-1.0, and 0.31 changed its
generator command and binding checksums.
NUIF should therefore stabilize in layers. First, make
nuif-api::NuifDocument authoritative for explicit text/CBOR load, validation,
typed operations, canonical hashes, export and verified package/resource
retention. Make WASM delegate to it and compare surfaces. Only after the
semantic API and error classes have a compatibility baseline should a small
separate unsafe C ABI be reviewed. cbindgen is appropriate for C/C++ headers;
UniFFI is the preferred Swift/Kotlin generator. Their generated packages and
versions remain separate from the editor.
Evidence
- The Rust Reference says the Rust ABI offers no stability guarantees and
defines
unsafe extern "C"as matching the dominant C compiler’s ABI for the target. Locator: The Rust Reference, External blocks, “ABI”, Rust 1.98, retrieved 2026-08-31: https://doc.rust-lang.org/reference/items/external-blocks.html#abi. - Rust 1.98 documents exported C symbols with
#[unsafe(no_mangle)] pub extern "C" fnand describes foreign interfaces as inherently unsafe, normally wrapped by safe Rust code. Locator: standard-libraryexternkeyword documentation, retrieved 2026-08-31: https://doc.rust-lang.org/stable/core/keyword.extern.html. - The Embedded Rust Book specifies
cdylib/staticlib, explicitextern "C"and generated or handwritten headers as the normal Rust-to-C/C++ path; C is used because neither Rust nor C++ supplies the needed cross-language stable ABI. Locator: A little Rust with your C, retrieved 2026-08-31: https://doc.rust-lang.org/stable/embedded-book/interoperability/rust-with-c.html. - cbindgen generates C and C++11 headers from Rust crates that already expose a
public C API. Its README says generation reflects Rust layout/ABI guarantees,
while also warning that project support is ad hoc and particular constructs
may be unsupported. Locator: cbindgen README,
master, retrieved 2026-08-31: https://github.com/mozilla/cbindgen. - UniFFI compiles Rust components into shared libraries and generates bindings to load them. Its first-party languages are Kotlin, Swift, Python and Ruby; Mozilla reports extensive Firefox mobile and desktop use. The project calls itself production-ready but far from 1.0. Locator: UniFFI README and user guide overview, retrieved 2026-08-31: https://github.com/mozilla/uniffi-rs and https://mozilla.github.io/uniffi-rs/latest/.
- The UniFFI guide explicitly says it generates bindings but does not help ship the Rust library to target platforms. Swift output includes a C header and module map around the shared library. Locator: guide overview, binding generation and Swift overview, retrieved 2026-08-31: https://mozilla.github.io/uniffi-rs/latest/bindings.html and https://mozilla.github.io/uniffi-rs/latest/swift/overview.html.
- UniFFI 0.31.0 removed prior generator types, changed command usage and changed
method checksums incompatibly with 0.30-generated bindings; 0.31.2 fixed
Kotlin ARM32 return conversion and Swift boundary defects. This supports
pinning generator/runtime pairs and testing generated consumers rather than
treating generated source as timeless. Locator: UniFFI
CHANGELOG.md, 0.31.0–0.31.2, retrieved 2026-08-31: https://github.com/mozilla/uniffi-rs/blob/main/CHANGELOG.md. - Executable NUIF evidence:
nuif-apitests load text and CBOR, apply the same typed transaction, compare canonical hashes, undo/redo and retain package metadata through a byte fixpoint.nuif-wasmdelegates those semantics to the façade, andcargo xtask gate-wasmcross-checks native and generated browser/Node output. Criterion addssdk/direct_documentload/export surfaces. Locator: linked code at revision containing this record.
Mechanism
The direct SDK owns a Session and an optional decoded NuifPackage. Bare
inputs enter through an explicit DocumentEncoding and remain diagnosable even
when structurally invalid. Package inputs enter through the package decoder,
then hand shared digest-verified embedded buffers to the session. Semantic
operations mutate only the session document. Package export clones the retained
package envelope, replaces its document and requested mode, and revalidates the
complete manifest/resource policy before writing bytes.
The WASM object stores this SDK object. Its remaining work is bounded JSON patch decoding and JavaScript error translation. Stateless MCP tools load the same SDK object per request while retaining their protocol framing and JSON Schema boundary. Direct API tests require matching hashes across text and CBOR, replayable operation preconditions, exact undo/redo and a package write/read/write fixpoint after editing. The generated Node/browser packages and live MCP subprocess remain checked against native canonical output.
The future foreign ABI remains one layer farther out:
C / C++ / Swift / Kotlin
│ generated wrapper and owned byte buffers
▼
separately reviewed nuif-ffi
│ NuifDocument byte records and typed error classes
▼
nuif-api
No internal Document, Entity, Rust enum layout, allocator pointer or panic
may cross that ABI by accident.
NUIF relevance
Borrow the compiler-style single façade, explicit C calling convention, cbindgen header generation and UniFFI Swift/Kotlin generation. Adapt them to NUIF’s byte records, typed errors, bounded inputs and independent profile versioning. Reject direct exposure of internal model structs, duplicated business logic in wrappers, a hand-written Swift/Kotlin ownership layer before UniFFI is evaluated, and any claim that generated bindings alone constitute a shippable SDK.
Promotion checklist
- semantic API and stable error-code registry leave
0.0.x; - separate
nuif-ffiunsafe-code review and panic-containment proof; - opaque handles plus allocator-matched bytes and destructors;
- cbindgen header/symbol compatibility diff;
- native C consumer under AddressSanitizer and UndefinedBehaviorSanitizer;
- pinned UniFFI generator/runtime with Swift and Kotlin consumer tests;
- target-specific XCFramework/Swift package and AAR artifacts with manifests, checksums, SBOMs and attestations;
- independent versions from the editor, WASM module and MCP binary.
Open questions
- Which semantic-API milestone is strong enough to register stable foreign error numbers and start ABI compatibility checks?
- Whether the first native package target should be an Apple XCFramework or an Android AAR; demand and a maintained live consumer should decide ordering.
- Whether a plain C ABI plus cbindgen is needed independently from UniFFI’s generated low-level C layer, or whether C/C++ adoption can wait for a named host requirement.
- Whether a measured Node workload ever justifies a native Node-API addon over the already conforming WebAssembly package.