The operator side: the engine.toml reference, building module artefacts, and local runs. For the production deploy (systemd, backup, observability) see docs/production.md; for containers see docs/deployment/docker.md; for multiple chains see docs/deployment/multi-chain.md. Module-author topics are in the SDK overview and the first-module tutorial.
A deployment is one or more engine processes, each pointed at:
- an
engine.tomldescribing the local environment (chain RPCs, resource caps, state directory); [[modules]]entries listing.wasmartefacts and theirmodule.tomlmanifests;[[adapters]]entries listing venue-adapter components (the bundledcow-venueadapter);- a
state_dirthe engine creates and owns (the redb local-store).
CoW order submission needs the venue platform, so run the shepherd binary, not the bare nexum. Modules and adapters are declared statically; the engine does not pull them from a registry today.
engine.example.toml at the repo root is the annotated template. The shape:
[engine]
# Local-store redb directory, created at boot.
state_dir = "./data"
# `tracing_subscriber::EnvFilter` directive; `RUST_LOG` overrides at start.
log_level = "info"
[engine.metrics]
# Prometheus exporter. Disabled unless enabled = true (bare `nexum` never binds it).
enabled = true
bind_addr = "127.0.0.1:9100"
# Per-module wasmtime resource caps. Every field is optional; omitted
# values resolve to built-in defaults. Applies uniformly to every module.
[limits]
fuel_per_event = 1_000_000_000 # ~1s of pure compute; wasmtime traps on exhaustion
event_deadline_secs = 120 # wall-clock backstop for unmetered host-call time (min 1)
memory_bytes = 67_108_864 # 64 MiB linear-memory ceiling
state_bytes = 52_428_800 # 50 MiB local-store quota
# One [chains.<id>] per chain, keyed by EVM decimal id. `ws://`/`wss://`
# engage the pubsub transport (blocks push via eth_subscribe); `http://`/
# `https://` poll (blocks via eth_getBlockByNumber, logs via eth_getLogs).
# Both work; prefer wss:// where the provider offers it.
[chains.1]
rpc_url = "https://ethereum-rpc.publicnode.com"
# request_timeout_secs = 30 # per-request JSON-RPC timeout (default 30, 0 rejected at boot)
[chains.11155111]
rpc_url = "wss://ethereum-sepolia-rpc.publicnode.com"The full [limits.*] subtables (http, chain, logs, poison, dispatch, quota, watch) are documented inline in engine.example.toml.
[[modules]]
path = "modules/twap-monitor.wasm"
manifest = "shepherd/modules/twap-monitor/module.toml" # defaults to module.toml beside `path`
[[adapters]]
path = "target/wasm32-wasip2/release/cow_venue.wasm"
manifest = "shepherd/crates/cow-venue/module.toml"
http_allow = ["api.cow.fi"] # outbound wasi:http allowlist the operator grantsThe orderbook base URL is not an engine setting: the cow-venue adapter reads it from its own module.toml [config] (chain, and optional orderbook-url to point at a barn or mock). See docs/deployment/multi-chain.md.
Modules compile to wasm32-wasip2. Add the target once:
rustup target add wasm32-wasip2Build release artefacts from the workspace root:
cargo build --target wasm32-wasip2 --release \
-p twap-monitor -p ethflow-watcher
cargo build --target wasm32-wasip2 --release -p cow-venue --features adapterArtefacts land in target/wasm32-wasip2/release/*.wasm. Copy them to wherever engine.toml points. CI guards a size regression:
ls -lh target/wasm32-wasip2/release/*.wasmBuild and run the shepherd binary against an engine.toml:
cargo run -p shepherd -- --engine-config engine.tomlThe single-module shortcut takes positional paths and synthesizes a one-module config:
cargo run -p shepherd -- \
target/wasm32-wasip2/release/twap_monitor.wasm \
shepherd/modules/twap-monitor/module.tomlAt boot the engine creates state_dir/local-store.redb, opens RPC providers, loads components, calls init, and begins dispatching. Console output is tracing JSON, or pretty with --pretty-logs. For systemd runs see docs/production.md.
| Symptom | Likely cause | Fix |
|---|---|---|
init failed: unsupported |
Module needs a chain RPC not configured. | Add the missing [chains.<id>] entry. |
unknown chain ... (no engine.toml RPC entry) |
chain::request for a chain not in engine.toml. |
Add the chain. |
OutOfFuel trap, restart loop |
on_event exceeds [limits] fuel_per_event. |
Bump fuel_per_event, or audit the module's loop bounds. |
MemoryOutOfBounds trap |
Linear-memory growth exceeds [limits] memory_bytes. |
Bump memory_bytes; profile the module. |
| dispatch exceeded its wall-clock deadline, module marked dead | A host call blocked past [limits] event_deadline_secs. |
Tighten the module's host-call timeouts, or bump event_deadline_secs. |
- SDK overview
- First-module tutorial
- ADR-0001 (
docs/adr/0001-engine-toml-separate-from-nexum-toml.md): whyengine.tomlandmodule.tomlare split. - ADR-0003 (
docs/adr/0003-local-store-namespacing.md): howstate_dir/local-store.redbpartitions across modules.