DocsGetting started

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 README

The packages

PackageUse
kore-stdThe Rust facade: filesystem, time, random, collections, tasks, threads, SPMD, process and optional driver APIs
kore-entryAn executable-only adapter so a program can use an ordinary returning fn main()
kore-ffiC headers and a Zig module over a Rust-founded host
kore-luaAn embeddable Lua 5.5 engine with a kore.* standard library
kore-wasiA 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

ToolVersionNeeded for
Rustnightly-2026-07-13, with rust-srcEverything
Zig0.16.0 (official release)The Zig module and the full C/Zig test matrix
cbindgen0.29.4Regenerating 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:

TOML
[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.

FeatureEffect
fs (default)The native DagFS client and allocation-free transfer helpers
heapExposes the runtime heap engine; installs no allocator
crt0The native process entry stub on Kore targets
driverThe 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.

TOML
[dependencies.std]
package = "kore-entry"
git = "https://github.com/MicroPerceptron/libkore"
rev = "4d873c581b77f8228627c98a5301797d7417b09e"
Rust
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:

TOML
[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:

Rust
#![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:

TOML
# .cargo/config.toml
[unstable]
json-target-spec = true
Shell
cargo build \
  --target targets/x86_64-unknown-kore-user.json \
  -Z build-std=core,alloc,compiler_builtins \
  -Z build-std-features=compiler-builtins-mem

The 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:

Shell
cargo fetch --locked
cargo test --locked -p kore-std
cargo test --locked -p kore-lua

The full matrix, with Zig 0.16.0 and cbindgen 0.29.4 installed, is:

Shell
make fmt test check docs owners cross
make miri bindings-check

Host tests exercise Rust logic and the linked foreign and Lua harnesses. They do not prove founded service behavior or physical-device operation.