surma 8d3c163992 sausage-plugin part 2: import! with typed clients (sausage_interface::client_rs), TCP endpoints with rpc.endpoint, examples
Part 1 fixes: complete stdout writes, buffered stdin, Rust type names
in schemas, enum variant docs, -32603 for serde errors in handlers,
crate docs and modules. End-to-end check of a generated consumer through
a fake core.
2026-10-23 00:35:45 +01:00

Sausage

You can see how the sausage is made.

Sausage is a world of small WASI programs ("plugins") that you grow by talking to it. A plugin is a .wasm file with JSON-RPC methods. Each call to a plugin starts a new process. The agent is a plugin too: it writes new plugins in Rust, builds them, tests them, activates them, and can change its own code.

Start

You need Nix with flakes, and an API token for an LLM (OpenAI Responses or Anthropic Messages API). No clone needed:

nix run git+https://gitea.surma.technology/surma/sausage

This creates a world in ./world the first time (it builds the agent, which takes a few minutes), then starts it with a console. Later runs start the existing world. The two steps on their own:

nix run git+https://gitea.surma.technology/surma/sausage -- init   # creates ./world and builds the agent
nix run git+https://gitea.surma.technology/surma/sausage -- run    # starts the world and a console

init asks for the API shape (openai-responses or anthropic-messages), the base URL, the model, and the token. It reads SAUSAGE_API, SAUSAGE_BASE_URL, SAUSAGE_MODEL, and SAUSAGE_API_TOKEN first. The values go to world/config/agent.json. Both commands take another folder as an argument, for example ... -- run my-world.

To keep a world on the newest version, run it in a loop:

nix run git+https://gitea.surma.technology/surma/sausage#loop               # ./world, created if needed
nix run git+https://gitea.surma.technology/surma/sausage#loop -- my-world   # an existing world

When the host crashes or a plugin calls core.exit, the loop fetches the newest commit and starts it again. quit ends the loop. Set SAUSAGE_FLAKE to run another flake URL instead, for example your fork.

From a clone (git clone https://gitea.surma.technology/surma/sausage), the same commands are nix run, nix run -- init, nix run -- run, and nix run .#loop.

Headless

--headless (or SAUSAGE_HEADLESS=1) turns the console input off. The host runs until SIGTERM or SIGINT (exit code 0) or until a plugin calls core.exit (exit code 75, so the loop starts the newest version):

nix run git+https://gitea.surma.technology/surma/sausage#loop -- --headless my-world

The console

Input Effect
text a message to the agent (agent.chat)
<plugin>.<method> {json} a call. The console prints the result.
ls [path], cat <path> the filesystem as a process sees it, with /proc and /run
methods <plugin> the methods of a plugin, with params and docs
ps the running processes
log [plugin or pid] [-n N] the journal
activate <plugin> <wasm>, revert <plugin> activate a new version, or go back
stop <pid>, kill <pid> stop a process
help, quit

The world folder

plugins/<name>/plugin.wasm   the current version of a plugin
plugins/<name>/source/       its source
state/<plugin>/              the state of a plugin, by convention
state/core/journal.sqlite    the journal: stderr of all processes, and core events
config/boot.json             the services that start with the world
config/agent.json            the configuration of the agent

Each process also sees /proc (the processes and the state of the core), /self (its own folder in /proc), and /run (shared files in memory).

To replace a plugin by hand, copy a .wasm file to its plugin.wasm. That skips the interface checks. revert <plugin> goes back to the version before the last activation.

More

  • PROTOCOL.md: calls, processes, the filesystem, locks, interfaces, and cargo. Read this to write a plugin in any language.
  • DECISIONS.md: why Sausage is the way it is.
  • sdk/: sausage-plugin, the SDK for Rust plugins.
  • examples/: small plugins.
  • AGENTS.md: how to work on Sausage itself.
S
Description
No description provided
Readme
1.4 MiB
Languages
Rust 98.9%
Nix 1.1%