Embed · the runtime in your own program
The tutorial runs programs from a terminal. An application — a desktop shell, a phone, a browser tab, a server — wants the same guarantees inside: its own models, its own files, its own clock, its own way of stopping a run. This page is how that works, and what stays the same no matter who hosts it.
§1 · the shape of it
Kimiya is not reimplemented per language. There is one front end — lexer, parser,
the discipline checker (K1–K15), the type checker — and it is the Python one. It emits a
checked program as a portable document, kir/1: a JSON tree with every name already
resolved, every judged relation's task key fixed, every prompt template hashed, every
irreversible act flagged, and a requires block saying what the program
needs from a host. A runtime executes that document; it never parses source and never
re-checks discipline — it may refuse a document, never reinterpret it.
python3 -m kimiya ir prog.kim -- prog.kir.json: checked, resolved, hashed kimiya-rt validate prog.kir.json -- a runtime's own consumer validation kimiya-rt run prog.kir.json name=value -- the same certificate as `kimiya run`
docs/ir.md in the download — document shape, node set, static resolution rules, the validation a consumer must perform, and the decisions that pin number rendering, faults and line splitting so two runtimes cannot drift on them.docs/host_abi.md.kimiya-rt in Rust, which is also the WebAssembly module, the C library and the Android library (§3).Why not a second compiler
Because the certificate must mean one thing. If two implementations assembled prompts differently, or rounded θ differently, or ordered a panel differently, a certificate would be a statement about an implementation rather than about a run. One front end and a normative list of runtime-owned behaviour keep the semantics in one place; the runtimes are held to it by conformance (§4), not by good intentions.
§2 · the boundary
Every import is a small set of named operations; a host may omit a whole import (no screen
world on a phone) and the runtime refuses, before running, any program whose
requires needs it. The imports:
complete(agent, request) → reply. The runtime builds the request — prompt, system text, temperature, token cap — and hashes it; the host talks to the model. A request's error is classed transient (the runtime's silence rules apply), refused (the run stops) or cancelled. A reply may carry a structured verdict instead of words: that is how a small local classifier sits on a judge panel next to a chat model, and the certificate keeps the two distinct, seat by seat.pyfn / use python table, or whatever the host binds under the same names: a Rust twin, a worker process, a stub. Trusted like check; the certificate's kernel block says who bound each one.file, screen, dom, and the image codec — observe and act, with the arity and effect class of each action fixed by the ABI, not by the host.print goes, and how a named secret is resolved. A credential crosses the boundary inward only: into a request the host itself sends, never into a message, a trace or a reply.The lifetime of a run
prepare(kir_json) -- load + validate, once per document; refused with the problems named execute(prepared, params, limits, host) -- any number of times; each with its own params, -- limits, cancel token and host state close(prepared) -- releases the document and everything a run owned: -- prompts, replies, θ, memo keys (freed, not zeroed)
Every execution ends in exactly one outcome, a certificate whose status is one of
COMMITTED, ENDED, ABSTAINED or CANCELLED. The fourth is the host's:
a cancel token tripped from any thread, or a limit on model calls, tokens or host-clock
seconds, stops the run at the next statement boundary or before the next model call, and
the certificate's reason names what fired — cancelled by the host,
limit: 40 model call(s) reached. A run is never stopped between a
model call and the recording of its reply. What the program did (abstain) and what was done
to it (cancel) are never confused.
§3 · the runtimes
kimiya-rt is a Rust crate that performs no I/O outside the traits of
the boundary. Everything below is that one crate, compiled or wrapped differently; the
conformance suite (§4) runs the same goldens through each.
Oracle, Kernel, FileWorld, …), build a Host, run. The crate ships an in-memory host, a replay oracle, the live model backends (Ollama, OpenRouter and OpenAI-style over HTTP behind a cargo feature; the claude CLI with none), the live xdotool screen driver, a recorder that writes the conformance recording, and the threaded asynchronous session. A Tauri desktop application embeds it this way.wasm32-unknown-unknown with every import crossing one imported function, host_call(op, payload), as JSON in linear memory. A second build rewritten by Binaryen's Asyncify lets a host operation return a Promise; the module sleeps inside the import and wakes with the reply. A Node reference host reproduces the goldens byte for byte and offers a live oracle.kimiya_prepare, kimiya_execute, kimiya_close, a cancel token, and the asynchronous session as kimiya_execute_async / _session_next / _session_reply; the host is one callback, call(ctx, op, payload), JSON both ways. Built as a shared and a static library.dev.kimiya.rt.Kimiya, built for arm64-v8a and x86_64. Kotlin bindings wrap it as KimiyaProgram with execute and a coroutine executeSuspending whose host is a suspend fun call(op, payload); cancelling the coroutine or the token ends the run CANCELLED even while a model call is outstanding. A Gradle demo app and eleven instrumented tests are in the download.Host(store=…, oracle=…, clock=…, kernel=…) replaces one import at a time; the interpreter and the compiler consult nothing else. An embedding in Python needs no other runtime.Rust, in six lines
// a run with no disk: files in, certificate out let doc = kimiya_rt::ir::load(&kir_json)?; let (mut host, artifacts) = kimiya_rt::memory::memory_host(oracle, kernel, files, secrets); host.oracle = Box::new(oracle::Live::new(credentials, LiveOpts { cancel: Some(token.clone()), ..Default::default() })); let cert = Runtime::new(doc, host, ¶ms)?.with_limits(limits).with_cancel(token).run(); let receipt = kimiya_rt::receipt::from_certificate(&cert); // keep this; drop the trace
Node, with the WebAssembly module
node host/js/kimiya-rt.mjs run prog.kir.json --oracle live --max-calls 20
-- the asyncified module when present; a live provider awaited behind one import
Kotlin
KimiyaProgram(kir).use { program ->
val out = program.executeSuspending(files = mapOf("transcript.txt" to text), host = myHost, cancel = token)
} // out: {"status":"done","certificate":{…},"receipt":{…}} — no trace unless asked
§4 · the proof it is one language
Two runtimes agreeing is tested without a model. The reference records every model request — hashed exactly as the runtime assembled it: model, system text, prompt, temperature, token cap, image hashes — and every reply; a runtime under test is handed the recording as its oracle. A request the recording lacks is not a cache miss, it is a conformance failure: the runtime under test assembled a prompt the reference never made. Host-function calls are recorded and replayed the same way, so a program whose kernel exists only in Python still runs everywhere.
| What is compared | How |
|---|---|
| The trace | Every row, in order — judgments, votes, acts, observations, cache decisions — after dropping host-clock readings and the host's own paths. |
| The certificate | Every field, after dropping the three that legitimately differ: the runtime's version, the compiler's, and who bound the kernel. |
| The suite | Twelve goldens — text, image, screen, dom and batched instruments, one under a call cap — on four runtimes: the Python interpreter, the Python compiler, the Rust runtime, the WebAssembly module in Node. Forty-eight runs, byte-identical. A live run recorded on one runtime replays on the others. |
Conformance is graded in levels — L0 text, L1 images, L2 screen, L3 dom, L4 the batched instruments — so a host can say precisely which part of the language it carries. The WebAssembly build is the text level by design: no worlds beyond files. The Rust runtime is L4, the whole language, with the screen and dom worlds under recording drivers and a live xdotool driver bound only on request.
Verified outside this repository
A desktop application that moved its contracts onto the Rust runtime recorded four of them live on Python — judge panels across a CLI-backed and an API-backed model, a schema generation, a vision locate with a screenshot judge — and replayed them on its embedded runtime: identical traces, identical certificates, no replay miss. That is what the boundary is for.
where to find it
Everything on this page ships inside kimiya-lang.zip:
docs/ir.md and docs/host_abi.md (the two
contracts), kimiya-rt/ (the crate, with its embedding, leak, screen
and oracle tests), kimiya-rt-wasm/ and host/js/,
kimiya-rt-ffi/ with the Android build script,
android/kimiya-demo/, and tests/conformance/
with the goldens and the harness. The Rust crate is tagged on its own
(kimiya-rt-v0.3.0 at this release) so an application can pin it.
The language itself
← The reference
Grammar, the discipline rules, the commands, and what every line of a certificate means.