Kore ABI
Tasks and bootstrap
One kernel object for threads and processes, a role-tagged handle table in place of environment strings, and the page a launcher shares with its child.
Source: task.rsOne kernel object
The kernel knows only tasks. Thread and process are a userland convenience, chosen when a task is founded:
| Spawn flag | Value | Meaning |
|---|---|---|
SHARE_SPACE | 1 << 0 | Share the spawner's address space: a thread. Clear: a fresh address space with delegated regions, a process |
DETACHED | 1 << 1 | Nobody joins this task |
SPMD_WORKER | 1 << 2 | A data-parallel gang worker; only with SHARE_SPACE |
A task is founded by a derive on a staged address space, with a FoundReq that carries a SpawnReq (24 bytes: entry, stack, bootstrap and flags), the arena size, the executable authority to run under, and a delivery map of up to 78 capabilities to place in the child's arena. A session can found up to 12 tasks, and must end with its founding run.
The founder gets back a Task handle with all four rights (none for a DETACHED spawn). A founded process is dormant until a later session runs TASK_START on that handle, which gives the parent a window to register a crash buffer, name the process and finish delegating memory without racing its first instruction. A SHARE_SPACE thread is the exception: it is dispatched as soon as its founding commits, so prepare anything it needs before that session. The child can receive its own SELF handle, with ACCESS and MODIFY but not REVOKE.
The boot page
A new task enters with a pointer to its boot page in the first argument register (rdi, x0 or a0). The page is 4096 bytes of frozen blocks:
| Offset | Block | Size | Contents |
|---|---|---|---|
| 0 | BootBlock | 712 | Role-tagged handles |
| 712 | LdBlock | 560 | Loader state for the startup linker |
| 1272 | GeomBlock | 296 | The address-space windows |
| 1568 | ClockPage | 64 | The published clock |
| 1632 | FoundingBlock | 1552 | The boot chain's testimony |
| 3184 | MachineBlock | 20 | Machine facts |
The runtime's entry stub validates the page, then calls your #[kore::main].
Capabilities by role, not by name
BootBlock holds up to 80 HandleEntry records (role, index, slot) and 8 DatumEntry records (role, index, value). A handle names an arena slot the founder delegated; a datum is plain data and never authority. Read it through Bootstrap::of, a validated view that checks the ABI stamp and counts and rejects duplicate keys before any lookup:
use kore::abi::session::SlotRef;
use kore::abi::task::role;
use kore::prelude::*;
/// The slot of the doorbell channel this task was founded with, if any.
fn doorbell(rt: &Rt) -> Option<SlotRef> {
rt.bootstrap().handle(role::DOORBELL, 0)
}There are no environment strings, by design. A KEY=VALUE string can be copied, so a disclosed one is a transferred one; a capability can't. Configuration that is genuinely data arrives over the parent channel after the first rendezvous. Nor is there a name server: a task reaches exactly the services whose endpoints its founder handed it.
The well-known roles (47 in this export, SELF = 0 through STOR_TARGET = 46) include:
| Roles | Grant |
|---|---|
SELF, ADDRESS_SPACE, PARENT_SEND, PARENT_RECV | The task itself and its parent channel |
MEM_SUPPLY, HEAP, VIRT_MEMORY, MEM_REGION | Memory budgets |
DEVICE, IRQ_ROUTE, DMA_POOL, ROOT_COMPLEX | A driver's device, interrupts and DMA supply |
DOORBELL, SVC_SEND, SVC_RING, SVC_GRANT, SVC_RETURN | Service endpoints and rings |
LOADER, IMAGE, EXECUTABLE, EXEC_AUTHORITY | Loading and launching code |
FS_VIEW_BIND, FS_GRANT_TARGET, STOR_RING, STOR_TARGET | Filesystem and storage |
TIMER, POWER, DISPLAY, HYPERVISOR, SYSTEM_OBSERVE | Platform services |
Applications define their own roles from USER_BASE = 0x8000. #[derive(kore::Bundle)] declares the roles a program needs as a founding profile, and claims them fail-closed at startup.
Address-space geometry
GeomBlock lists the task's windows, each (role, index, va, len):
| Window | Value | Holds |
|---|---|---|
SCRATCH | 0 | The session descriptor scratch |
SLOTS | 1 | The read-only arena alias and execution-advice page |
MAP | 2 | The allocation window for mappings |
THREAD_BLOCK | 3 | Per-thread blocks |
APERTURE, LINK | 4, 5 | Device apertures and service links |
TRANSIENT | 6 | Image validation |
SLOTS_EXT | 7 | Arena growth |
SURFACES, DEVICE, HEAP | 8–10 | Display surfaces, device mappings and the heap |
The process page
A launcher and its child share one 4096-byte process page (PROCESS_IO_ABI = 13). The launcher writes the arguments (a 272-byte tail) and an optional launcher context; the child replaces that context with its bounded output record. An optional second page carries the live foreground stream as two single-producer rings: input from the launcher, and ordered stdout, stderr and input-ready events from the child.
A Closed output record ends the stream. An Interrupt input record, the terminal's Ctrl-C, asks the child to stop at its next safe point; a child that never reads its input meets the launcher's revoke after a grace period. No byte copied through these pages confers authority.
Identity and names
Each task has a TaskId: an opaque 128-bit value, scoped to one boot, with a quality tag for its entropy source. It identifies; it never authorizes. A parent may give a dormant task a TaskName of up to 63 bytes of UTF-8, once.