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 message bus carries events. Raw OS input becomes a normalized
Event(viaEventSynth), and widgets and the application read from one stream rather than each polling the OS. - The command bus is a generic, domain-agnostic channel an application edits its Document through. Edits go through one surface, which is what makes undo (snapshot, mutate, compare) and a single audited mutation path possible.
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.