OMW
OMW = OpenAI + MCP + WASM.
omw is an agent runtime. You declare agents in a TOML configuration, each
wiring a provider (an OpenAI-family chat service), tooling (MCP tool
servers), and a brain — either a compiled WASM component, a rhai script, or
a JavaScript script. omw then drives each agent through an actor model: every
agent owns a single inbox, and chat streams, tool results, timers, and messages
from other agents all arrive there as tagged events the brain consumes.
Who it is for
omw is for people who want a small, local agent runtime that is genuine about
its inputs and outputs: the brain is real WASM, tooling speaks MCP, and the
configuration is plain TOML. It is not a framework — there is no DSL to learn
and no orchestration layer. You bring a provider key, a couple of MCP servers,
and a brain, and omw runs it for one iteration (run) or keeps it going
(loop).
How it works
- Providers are OpenAI-family chat services.
provider.chat-streamopens a streaming response whose deltas arrive as events in the agent's inbox; the in-bandprovider.chatblocks for the full result. - Tooling is MCP tool servers. They expose callable tools and readable resources; resource subscriptions deliver change events.
- Brains are runtimes. The
wasmruntime loads an agent as a compiled component; therhairuntime evaluates a script on an interpreter that ships as an opt-in variant, as does thejsruntime. The defaultomwpackage/binary ships with theruntime-wasm,provider-openai,tooling-mcp, andendpoint-openaiback ends but without either script runtime, whereas theomw-rhaipackage /omw-rhai-<arch>.tar.gzbinary (--features runtime-rhai) includes the rhai interpreter and theomw-jspackage /omw-js-<arch>.tar.gzbinary (--features runtime-js) includes the js interpreter. All three see the sameomwhost interface (rhai in snake_case, js in camelCase). To write a pure Rust brain, depend on theomw-wasm-rustguest SDK crate instead of runningwit-bindgenyourself; see Rust brains. - Agents are actors. They subscribe to each other explicitly, so a message only ever reaches an agent that chose to listen.
- Endpoint is an optional OpenAI-compatible HTTP server. Set
[endpoint]withkind = "openai"plus alistenaddress and agents can subscribe themselves under model names: inbound chat requests arrive in the agent's inbox as events, and the agent streams its reply back (SSE or buffered JSON). Any OpenAI-compatible client can then drive an agent. - Hot reload is
--watchonrun/loop. When a brain script changes, the agent's run restarts on the new script while inboxes, subscriptions, and sessions survive. The new script is validated before the live run ends, so a bad edit never kills a good run — and a broken script never starts.
Installation
omw is packaged as a Nix flake. Run it directly without installing:
or build the omw binary with:
Releases
Prebuilt binaries for x86_64-linux and aarch64-linux are attached to each
GitHub release as tarballs containing the omw binary. The default
omw-<arch>.tar.gz ships no rhai runtime; grab the omw-rhai-<arch>.tar.gz
tarball (or the rhai Nix package) when your brains are rhai scripts, or the
omw-js-<arch>.tar.gz tarball (or the js Nix package) when your brains are
JavaScript scripts:
The rhai variant is the same shape, with the -rhai name:
The js variant is the same shape, with the -js name:
Usage
Configuration lives in a TOML file (default omw.toml in the current directory,
overridable with --config). It declares named providers, tooling, and runtimes
plus a list of agents:
[]
= "openai"
= "sk-…"
= "gpt-4o"
[]
= "mcp"
= "stdio"
= "npx"
= ["-y", "@modelcontextprotocol/server-everything"]
[]
= "rhai"
[[]]
= "alice"
= "rhai"
= "brain.rhai"
Then drive it:
Serve agents over HTTP with the optional endpoint: set kind plus a listen
address, have a brain subscribe itself under a model name, then any
OpenAI-compatible client can call it:
[]
= "openai"
= "127.0.0.1:8080"
let sub = omw::host::subscribe_endpoint("gpt-4o");
Inbound requests arrive in the agent's inbox as endpoint-message events; the
brain streams its reply back with stream_endpoint (SSE for stream: true, one
buffered JSON completion otherwise). GET /v1/models lists subscribed models.
Edit brains live with --watch on either mode: when a brain file changes, the
agent's current run ends and restarts on the new script, while inboxes,
subscriptions, and sessions survive on the shared bus. The new script is
validated before the live run ends, so a bad edit keeps the good run alive
(plus an error event if the brain subscribed to lifecycle events) — and a
broken script never starts (parks under --watch, fails fast without it).
Brains opt in to reload / shutdown notices with subscribe_lifecycle.
See the endpoint and hot reload pages for the full reference.
To write a pure Rust brain, depend on the omw-wasm-rust guest SDK crate
instead of running wit-bindgen yourself; see Rust brains.
Configuration can also be layered from the environment (OMW__ prefix) or
generated as a JSON schema:
See the docs for the full reference.
NixOS
The flake ships a NixOS module exposing services.omw — a hardened systemd unit
that runs omw from a config file, layering OMW__-prefixed environment
variables over it so secrets never live in the Nix store. Plain-systemd and
Docker deployments are covered too — see Deployment in the documentation:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
omw.url = "github:haras-unicorn/omw";
};
nixosConfigurations.my-machine = nixpkgs.lib.nixosSystem {
modules = [
omw.nixosModules.default
{
services.omw = {
enable = true;
settingsFile = "/etc/omw.toml";
environmentFile = "/var/lib/omw/env";
};
}
];
};
}
See The NixOS module in the documentation for the full option set, including
settings vs settingsFile, mode, user/group, and stateDir.
Library
omw can be used as a library in your own crate by adding omw to dependencies
and enabling the runtime features you want. See the library page for details.
Examples and testing
The examples are runnable agents that exercise the whole stack — provider,
tooling, endpoint, agents — with no keys, no network, and no external services,
each in rhai, js, and wasm. The omw-test binary runs them deterministically
against in-process scripted doubles and checks what every agent saw and did
against an [assertions] section; the testing pages cover the binary, the
assertion language, and each mock. See examples for the tour.
Binary cache
Builds are cached on the haras cachix cache. When the flake is used directly
(for example with nix run github:haras-unicorn/omw), the cache is configured
automatically through the flake's nixConfig. To use it when the package comes
from an overlay, add the following to your nix configuration:
{
nix.settings = {
substituters = [ "https://haras.cachix.org" ];
trusted-public-keys = [
"haras.cachix.org-1:/HIo1JYqOIH1Nwk1EGXhuPPvDW0WekxIbY5CiXUZbYw="
];
};
}
Documentation
The documentation is available at https://haras-unicorn.github.io/omw/.