Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overview

A harness is one statically linked RV64 ELF. berm pins it by hash, compiles it once, and instantiates it per invocation: arguments go in through host calls, the result comes back out of guest memory, and nothing survives the call.

let berm = Berm::load(&engine, &elf, &[])?;

match berm.call("echo", br#"{"query":"hello"}"#.to_vec())? {
    Ok(result) => println!("{result}"),
    Err(failure) => eprintln!("{failure}"),
}

Two levels of result, because two things can go wrong and they are not the same thing. The outer one is the host’s: no such tool, a trap, a broken image. The inner one is the harness reporting failure through the ABI — which is a result the model should see and react to, not an error the host should swallow.

Nothing survives the call

Every invocation gets a fresh Store, so guest memory is not carried between calls. A harness that wants something to outlive an invocation has to put it somewhere a host is holding.

This is the opposite of a container, and it is why there is no lifecycle to manage — nothing to start, stop, or restart. What is expensive is compilation, not instantiation, and compilation is paid once per image.

The linker is the boundary

A harness reaches the world only through the system harnesses it was given, and that list is the Linker it is instantiated with. A call to anything else traps because nothing is registered for the number the guest put in a7 — not because a check ran and said no. There is no check to write, and none to forget.

berm ships none. What a filesystem is bounded by, what shape a command’s result takes, where bytes persist — each is a decision about a host, and berm has no host. An embedder passes Harness values to Berm::load, and that list is the whole of what a guest can reach.

The same argument appears one layer down in Host Calls: rvtime ships no standard function set for the same reason.

Where rvtime stops and berm starts

rvtime loads an ELF, generates native code for every function in it, and calls it. It has no idea what a harness is — no tools, no manifest, no arguments, no JSON.

berm is the layer that decides those things: that a tool is an export named berm_tool_*, that an image describes itself in a .berm.abi section, that arguments arrive as a blob the guest pulls in rather than as registers. Those are conventions, and keeping them out of rvtime is what lets rvtime be used for something that is not a harness at all.