Files
sausage/AGENTS.md
T

4.8 KiB

Working on Sausage (v3)

Sausage is a world of WASI programs and durable agents. It is meant to be read and changed by agents, including its own. Keep it that way.

README.md describes the current build and layout.

The boundary

  • The world is the sandbox. Everything inside the world sees and changes the whole world. Do not add inner boundaries, permissions or per-agent views. The VFS is a safety net against mistakes, not a wall against attackers.
  • Host calls pierce the sandbox. The only host import beyond standard WASI is the process interface (spawn, wait, kill). Do not add another one. Everything else uses standard WASI 0.2 interfaces: files through the VFS, wasi:http (the host API on its reserved host name, credential injection, signing), and wasi:sockets.
  • The credentials manager in the host is the one real security boundary. Its managed secrets never enter the world. Approvals need a passkey. The exceptions are provider API keys in /etc/sausage/config.yaml. The user keeps these keys inside the world so agents can change their own setup.
  • Native code is the exception. The only native work for the world is the Rust builder, and it runs in user, mount and network namespaces.

Priorities

  1. Correct. Every behavior has a test.
  2. Simple and short. Prefer the obvious code. No abstraction for one caller. No configuration that nobody asked for.
  3. Readable. A new reader must understand a file without the others. Each module starts with a doc comment that says what it owns.
  4. Efficient on the paths that run per command and per model turn.

Layout

  • host/: the host crate (package and binary sausage): the world, the process table, the harness. host/src/exec.rs is the contract between the harness and the process table.
  • vfs/: sausage-vfs, the virtual filesystem for WASI guests and for host code. It knows nothing about Sausage. Host code reads and writes the world only through it.
  • userland/: the default CLIs of a world, built for wasm32-wasip2 in the root Cargo workspace. The patched Brush crates live in userland/brush/source/ and share the root lock file.

Programs in the world

  • Agents write CLIs and servers. That they are WASI binaries is a detail, not a concept to explain.
  • The platform is WASI 0.2 components. Rust programs build for wasm32-wasip2. WASI 0.1 programs run through an adapter.

Checks

nix develop -c cargo test -p sausage -p sausage-vfs
nix develop -c cargo clippy -p sausage -p sausage-vfs --all-targets -- -D warnings
nix develop -c nu userland/bundle.nu
# For one tool, select its package and WASI target explicitly:
nix develop -c cargo build -p git --target wasm32-wasip2 --release

nix build uses separate Crane pipelines for the native host and WASI userland. Go tools and QuickJS build separately. Nix assembles the bundle without bundle.nu. Both Nix and the incremental development bundle read the command names from userland/tools.json. Coreutils, diffutils, procps, and brush use relative symlinks to one binary each.

nix flake check checks the packaged host, its bundle, and the whole host/VFS test suite. Sandbox-incompatible tests print an explicit skip reason instead of a flake-level filter. The check sets SAUSAGE_NIX_CHECK=1 for tests that need network access or unvendored fixture dependencies.

Real end-to-end check

Before you report packaging, config, or model changes as complete, run this check outside the Nix sandbox. Use the required build slot wrapper and approved real provider keys.

nu --no-config-file host/tests/e2e-real.nu /path/to/config.yaml

The script builds the package and tests a fresh world, real models, a shell tool, and browser chat. It copies only the supplied config's providers and never changes the source world. It uses a free loopback port, not port 8080. A failure retains a private temporary folder with provider keys. Treat that folder as sensitive. The result table reports times in seconds.

Tests

Host integration tests share one executable:

cargo test -p sausage --test it
cargo test -p sausage --test it -- chain::

Use the required Nix environment and build slot wrapper for these commands. Add test modules under host/tests/it/ and declare them in it/main.rs. Use fully qualified child test filters with --exact. See host/tests/README.md for fixture and isolation details.

Tests must stay fast:

  • Only a few tests may wait for a timing (grace periods, deadlines).
  • Poll for a condition with a deadline. Do not sleep.
  • Do not test unrealistic load, such as many fast cycles.
  • #[ignore] only disables a test for a short time, with the reason in the attribute.
  • Tests that build or compile guests use a persistent cache (for example under env!("CARGO_TARGET_TMPDIR")), so a warm run does not compile again.