the C ABI, the buses, the handle

The seams: how the pieces talk

The pieces meet at a few deliberate seams. A C ABI lets any language drive the model. The buses carry events and edits. One invariant lets a document handle cross every layer untouched.

Where the pieces meet

A system stays comprehensible when its parts touch at a few named seams instead of everywhere. Kinogaki has three: a C ABI for foreign languages, the buses for events and edits, and one invariant that lets a document handle cross every layer untouched.

The C ABI: the foreign-language seam

Kinogaki Core exposes a flat C ABI, <kinogaki/C.h>, with functions named kinogaki_*. It is the one door any non-C++ language uses to drive the model. A kinogaki_document_t* is an opaque handle; functions take it plus plain C arguments and return plain C values. No C++ types cross the boundary, so the binding is small and the ABI is stable.

The Python package is the proof: pip install kinogaki is a ctypes binding over this ABI (see the Python boundary). A Document in Python is a handle to a C++ Document; doc.append(path, type) and doc.eval(slot, time) each call straight through kinogaki_*. No model logic is reimplemented above the ABI. Rust, Swift, or any other language drives the same door the same way.

The buses: events and edits

Inside the runtime, two buses carry intent, both in Kinogaki Platform:

The buses are why the UI never reaches into the OS and the application never mutates the Document from scattered call sites.

The handle invariant

The transport (SDUI) lives in Platform but operates on Documents owned by Core. They connect through one deliberate invariant: a kinogaki_document_t* is the same address as the C++ Document* it wraps. Platform's C ABI casts between them directly. So a Document handle created in one layer is usable in another with no copy and no marshalling. It is a small promise, written down, that keeps the layers cheap to cross.

Why seams, not glue

Each seam is narrow and explicit: a C header, a bus interface, a one-line invariant. Nothing reaches across a layer except through one of them. That is what lets Core be tested with no Platform, Platform with no UI, and a Python program with no C++ compiler. The seams are the reason adding a backend (see platforms) or a language binding does not ripple upward. Next: the loop those seams carry, the server-driven-UI loop.