libkore
Getting started with libkore
Add libkore to a Rust project, build for Kore's user targets, and write a first program: toolchain, targets, features and checks.
Source: libkore READMEThe packages
| Package | Use |
|---|---|
kore-std | The Rust facade: filesystem, time, random, collections, tasks, threads, SPMD, process and optional driver APIs |
kore-entry | An executable-only adapter so a program can use an ordinary returning fn main() |
kore-ffi | C headers and a Zig module over a Rust-founded host |
kore-lua | An embeddable Lua 5.5 engine with a kore.* standard library |
kore-wasi | A WASI 0.2 component host, composed by an embedder with its Wasmtime engine |
All five are version 0.1.0-alpha.1 and set publish = false. None starts a second executor, and only kore-entry installs a global allocator: the native heap, in the executable it is linked into. Selecting a feature grants no authority: filesystem, clock, process and device APIs work only through capabilities the process was actually endowed with.
Toolchain
| Tool | Version | Needed for |
|---|---|---|
| Rust | nightly-2026-07-13, with rust-src | Everything |
| Zig | 0.16.0 (official release) | The Zig module and the full C/Zig test matrix |
| cbindgen | 0.29.4 | Regenerating the C header |
An ordinary Rust dependency build needs only the pinned nightly. Kore programs build with -Z build-std, so rust-src is required.
Add the dependency
Name the dependency kore, which keeps it distinct from Kore's own kernel package, and pin a reviewed commit on the alpha branch:
[dependencies.kore]
package = "kore-std"
git = "https://github.com/MicroPerceptron/libkore"
rev = "4d873c581b77f8228627c98a5301797d7417b09e"A pinned revision is a source dependency, not an ABI stability promise. Choose a newer reviewed commit when you update.
| Feature | Effect |
|---|---|
fs (default) | The native DagFS client and allocation-free transfer helpers |
heap | Exposes the runtime heap engine; installs no allocator |
crt0 | The native process entry stub on Kore targets |
driver | The native device host, register, DMA and ring APIs |
A first program
The simplest executable uses kore-entry, renamed to std so rustc picks it up with its edition prelude. It belongs only in the final executable; libraries keep depending on kore-std.
[dependencies.std]
package = "kore-entry"
git = "https://github.com/MicroPerceptron/libkore"
rev = "4d873c581b77f8228627c98a5301797d7417b09e"fn main() -> Result<std::ExitCode, std::ExitCode> {
let values = vec![1, 2, 3];
assert_eq!(values.iter().sum::<i32>(), 6);
Ok(std::ExitCode::OK)
}There is no no_main, handwritten _start or entry attribute. The adapter installs the native process heap, which still draws from the process's founded heap supply. After main returns, the runtime asks the kernel for the process shutdown barrier: it stops every peer task in the process and reaps them before the process exits. The alias does not claim full compatibility with upstream Rust std; Kore's implemented APIs and authority requirements still apply.
The native entry
A program that runs its own executor uses #[kore::main], an async fn that never returns. It needs the crt0 and heap features:
[dependencies.kore]
package = "kore-std"
git = "https://github.com/MicroPerceptron/libkore"
rev = "4d873c581b77f8228627c98a5301797d7417b09e"
features = ["crt0", "heap"]This one starts a background task, attaches to the filesystem face its founding granted, and writes a file. It installs the native heap as its allocator and supplies its own panic handler, which ends the whole process with a typed panic outcome:
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use kore::fs::{self, Rights, TransferError};
use kore::prelude::*;
use kore::runtime::heap::{Heap, HeapSupplySource};
use kore::runtime::transport::TrapTransport;
#[global_allocator]
static HEAP: Heap<HeapSupplySource> = Heap::new_in(HeapSupplySource);
static TRANSPORT: TrapTransport = TrapTransport::new();
#[kore::task]
async fn heartbeat() {
while kore::time::sleep(Duration::from_secs(1)).await.is_ok() {}
}
#[kore::main(tasks(heartbeat()))]
async fn main(rt: Rt) -> ! {
// The filesystem face exists only if this program's founding granted one.
let Some(mut fs) = Fs::attach(&rt, &TRANSPORT).await else {
kore::thread::exit(ExitCode::UNAVAILABLE)
};
// Paths resolve inside the view this task was endowed with; there is no
// global root to escape to.
let Ok(file) = fs.open(b"notes/today.txt", Rights::READ.union(Rights::WRITE)).await else {
kore::thread::exit(ExitCode::NOT_FOUND)
};
let code = match fs::write_all_at(&mut fs, &file, 0, b"hello from Kore\n").await {
// Writes never sync implicitly.
Ok(()) if fs.sync(&file).await.is_ok() => ExitCode::OK,
Ok(()) => ExitCode::IO,
// `completed` is the acknowledged prefix, not a safe replay offset.
Err(TransferError { completed: _, .. }) => ExitCode::IO,
};
let _ = fs.close_checked(file).await;
kore::thread::exit(code)
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
// Abort the whole process with a typed PANIC outcome; nothing unwinds.
kore::crash::panic(info)
}#[kore::task] gives each declaration its own static storage; calling it returns a spawn token, and a second live instance is refused. tasks(...) admits a bounded startup set before the first poll. Programs that use #[kore::task] need #![feature(type_alias_impl_trait)].
Build for Kore
Kore executables use libkore's architecture-specific user targets in targets/, one each for x86-64, AArch64 and RISC-V. Copy the one you need into your project, enable JSON target specs, and build core, alloc and compiler_builtins from source:
# .cargo/config.toml
[unstable]
json-target-spec = truecargo build \
--target targets/x86_64-unknown-kore-user.json \
-Z build-std=core,alloc,compiler_builtins \
-Z build-std-features=compiler-builtins-memThe first build resolves the Git dependency and writes Cargo.lock. Commit it, and add --locked to later builds so they use exactly the pinned graph.
The targets produce position-independent executables with full RELRO, a non-executable stack, initial-exec TLS, panic = abort and frame pointers kept, so a crash report can carry a bounded stack trace.
Check libkore itself
From a libkore checkout, the host-facing checks need nothing beyond the pinned nightly:
cargo fetch --locked
cargo test --locked -p kore-std
cargo test --locked -p kore-luaThe full matrix, with Zig 0.16.0 and cbindgen 0.29.4 installed, is:
make fmt test check docs owners cross
make miri bindings-checkHost tests exercise Rust logic and the linked foreign and Lua harnesses. They do not prove founded service behavior or physical-device operation.