kore-ffi provides a Rust-founded embedding ABI. cbindgen 0.29.4 generates
extern/c/include/kore.h from its actual extern "C" functions and repr(C) /
integer-representation types. The namespace generator groups those exported
names into extern/zig/src/root.zig. Zig 0.16.0 translates the C header for the
consumer's target at build time; generated host-specific layouts are not checked
in. build.zig exports the kore module, with filesystem, graph, time, random, task, thread and SPMD namespaces, and the
complete declarations/constants under kore.abi.
Generation is inexpensive once the boundary exists. Rust lifetimes, affine
View ownership, futures, slices and native error enums are not themselves a C
ABI. The adapter below establishes those semantics; neither cbindgen nor Zig's
translator invents them. See cbindgen's supported API shapes
and Zig C translation.
A Rust host attaches Fs using its real founding face, then moves it into
kore_ffi::Session, after closing any external views opened before handoff.
The host awaits Session::run(step, user) on the existing
Kore executor. No second executor, global allocator or global filesystem root is
installed. The C/Zig callback is linked into that same application's image.
This is not yet an independent C main/CRT, registry-published static SDK, or a
POSIX syscall veneer.
Each callback receives an opaque context and the preceding completion. Initially
that completion is KORE_OP_START. Queue one operation and return. Submission
returning KORE_STATUS_QUEUED means accepted by the adapter, not successful
I/O. The next callback reports the native completion. Returning without queuing
anything ends the program and starts acknowledged cleanup of its views.
Implemented operations are open, create file/directory, read, write, refreshed
stat, resize, sync, checked close, compatibility unlink, and paged directory
listing. Native kore_dag_link and kore_dag_detach preserve DAG projection
and nonempty-directory edge removal separately from compatibility unlink.
Open resolves only within the task's endowed namespace; an empty path
names its root. Names and rights follow DagFS, including rejection of ..,
unknown right bits and the native name/depth limits. A parent handle is required
for creation and unlink; no ambient POSIX descriptor table is manufactured.
The adapter retains at most eight native views, leaving room for bounded native
path walks within the service's handle ceiling. A foreign handle carries owner,
slot and generation. Copying its bits does not clone a View. Successful close
invalidates all copies; refused close retains the original view. Foreign handles
cannot be reused in another owner, and exhausted generations are never recycled.
Native service authority is still revalidated on every operation.
The owner copies write input before returning from the submission call. Reads
copy acknowledged bytes into its bounded scratch buffer, then kore_fs_read_data
copies them into foreign storage during the following callback. Input and output
buffers must be disjoint from opaque context storage. No foreign buffer is held
across an await. One transfer is capped at 4096 bytes and the native window may
complete less; completion.count is the acknowledged count. Loops must inspect
it. Writes neither truncate nor sync implicitly. After a new request is queued,
previous result-buffer access is refused rather than observing overwritten data.
The fixed owner storage is an intentional copy/custody tradeoff for this ABI; Rust's existing native window operations remain available for zero-copy use. There is no hot-path allocation or new lock; each turn makes one foreign function-pointer call followed by a statically typed native operation. Every eight completed turns, the driver yields through the existing native executor, even if requests (such as zero-byte reads) complete immediately. C/Zig callbacks must return promptly: the adapter does not preempt arbitrary foreign code inside a callback.
Filesystem and provider errors have explicit generated foreign enums. Monotonic and wall time retain distinct functions and explicit calibration/domain errors; an unavailable clock is never reported as a successful zero timestamp.
An indeterminate transport outcome may follow a committed write. Its final
callback receives KORE_STATUS_INDETERMINATE; the context rejects further work,
and the driver returns RunError::Quarantined. Nothing is automatically replayed.
Dropping a started driver while it is pending likewise prevents owner reuse
(an unpolled future has not changed the owner). The native ring/data
mappings are task-lifetime mappings; dropping the adapter's scratch buffers does
not free memory still visible to the service. Retire the enclosing task to
reclaim a quarantined owner. Cancellation is not successful close or DMA-style
completion. A normal exit closes each retained view; a refused cleanup close
returns the native error while retaining that view in the Rust owner.
The context and completion pointers exist only for one callback. They may not be retained, accessed by another thread, used for re-entry, or crossed by unwinding/longjmp. C/Zig callers must supply valid, aligned, non-overlapping buffers. This is the usual memory-unsafe FFI boundary, not an intra-process sandbox; the process's Kore capabilities remain the security boundary.
kore-lua already owns this lifecycle rather than exposing these pointers to a
script: the VM runs on a worker thread and reaches every service through one
request cell served on thread 0, so a script holds a handle — an index and a
generation — and never a native view. A Python adapter can do the same.
Normal Rust consumers need neither generation tool. To develop this repository:
cargo install cbindgen --version 0.29.4 --locked
# Or use an existing official Zig 0.16.0 installation.
python3 tools/install-zig.py /tmp/libkore-tools
export ZIG=/path/printed/by/the/installer
make bindings
make fmt test check docs owners cross miri bindings-checkThe installer supports the CI Linux x86_64 and local macOS arm64 hosts and verifies
pinned official archive checksums. Other hosts can supply an official Zig 0.16.0
binary through ZIG. CBINDGEN and CLANG also accept explicit tool paths.
test-foreign on kore-ffi links compiled C and translated Zig consumers into
host tests. It is disabled for native target checks and Miri; Rust pointer,
handle, buffer and scheduling tests still run under Miri. bindings-check checks
drift, compiles C and Zig consumers for x86_64, aarch64 and riscv64, and exercises
the Zig build-system module. Native Rust target checks use Kore's actual JSON
user targets, not merely stock bare-metal targets.
Local macOS package validation with Zig 0.16.0 uses the host-selected SDK; no global SDK selection change or per-invocation SDK override is needed.
These checks prove declarations, layouts, linked foreign calls, and admission
contracts. They do not claim that a C/Zig-founded standalone process has booted
on hardware. The concrete Rust-owned driver calls the existing native Fs
implementation; its founding/entry integration remains explicit in the host.
The generated API also includes native sleep, async task preparation and joined worker scopes, plus automatically seeded random values. Async callbacks return to the existing executor; synchronous worker scopes retain the native join owner. No foreign macro translation, second scheduler, TLS RNG, or global entropy lock is required.