The OS for agent harnesses.
Claude Code, Codex and OpenCode reach berm over MCP. What they install runs as a process, under a syscall table that is readable before it runs and fixed once it does.
A program is one WebAssembly module. berm pins it by hash, compiles it once, and instantiates it per invocation — arguments go in through syscalls, the result comes back out through one, and nothing survives the call.
The same source also builds for RISC-V, which berm runs under rvtime instead of wasmtime. That backend is experimental; which one a deploy reaches is read off the image's first four bytes, so a program is deployed the same way either way.
let berm = Berm::new(&engine, call::DEFAULT_CALL_DEPTH, vec![]);
berm.deploy("example", &wasm)?;
// The outer result is the host's — a missing tool, a trap. The inner one is
// the program reporting failure, which is a result the model should see.
match berm.call("example", "echo", br#"{"query":"hello"}"#.to_vec())? {
Ok(result) => println!("{result}"),
Err(failure) => eprintln!("{failure}"),
}What is deployed is reachable by name from anything deployed beside it. Beyond that a program reaches the world only through the syscalls it was given, and that table is the linker it is instantiated with — a call to anything else traps because nothing is registered for it, not because a check said no. berm ships none: what a filesystem is bounded by, and where bytes persist, are decisions about a host, and berm has no host.
Manifest::from_image(bytes) reads what an image claims to be — its tools,
their schemas, when to reach for them, and what it will reach for itself —
without compiling or running it.
bermd is a long-running service that deploys programs and serves every one of
them on a single MCP endpoint, with tools named {program}.{tool}.
bermd &
berm deploy example ./program.wasm
berm lsSee apps/service for the control API and what a deployed
program can reach.
A program is one file, so it travels as one OCI layer with no tarball around it
— and because the layer is the image and nothing else, the digest a registry
addresses it by is the digest berm ls prints.
berm push ghcr.io/org/example:v1 ./program.wasm
berm deploy example ghcr.io/org/example:v1
berm search "read a file"deploy takes a file or a reference. Finding one is a separate question, since
no registry will tell you who published a program: the list is a git repository,
so search reads a clone of it with no service and no credential. See
Publishing a Program.
- Guide and design notes — how it works and why, with worked examples.
- API reference —
cargo doc --open.
cargo test --workspaceRust 2024 edition, POSIX only. Building a program additionally needs the guest target:
rustup target add wasm32-unknown-unknownFor the experimental RISC-V backend, rustup target add riscv64imac-unknown-none-elf and build berm with --features riscv. Guests
there must be linked with --emit-relocs, which is the first thing to check
when one fails to load; berm new --target riscv writes that flag for you.
Licensed under the Apache License, Version 2.0.