The Guest ABI
The fixed binary contract between WASM guest programs and the Samoza runtime.
The Guest ABI is the binary interface between WASM guest programs and the Samoza runtime. It defines the set of host-imported WASM functions and guest-exported entry points that constitute the fixed contract.
Every non-UI space (:IO, :DATA, :CHAT, :CALL) executes a WASM guest compiled with TinyGo. The Guest ABI is the syscall table — minimal, stable, and the only way guest code reaches the runtime.
What the ABI covers
The Guest ABI specifies only the WASM host-imported functions and guest-exported entry points. It does not cover:
- Construction-time concerns (space creation, path wiring, agent registration) — those belong to the MEX format.
- Domain-specific convenience wrappers (car control, vision typed structs) — those belong to the Guest Library.
See the boundaries reference for the full discussion.
Modules
The ABI is organised as modules. A guest can only use modules its declared capabilities permit.
Core — universal, every WASM space
| Module | Purpose |
|---|---|
| Lifecycle | on_init, on_process, on_close, memory model |
| System | Logging, sleep, yield, self-emit |
| Network | Message passing between spaces |
| Capabilities | Capability model — declaration and enforcement |
Extended — software services, cross-cutting
| Module | Purpose |
|---|---|
| Data | Key/value storage |
| Agents | DF, AF, LLM agent invocation |
| Events | Computed events from recognizers |
| Intelligence Cache | LLM response caching, semantic lookup |
Device — physical hardware I/O
| Module | Purpose |
|---|---|
| Sensors | Unified subscribe/read for all inputs |
| Actuators | Unified lock/command/release for all outputs |
| Audio | Microphone input and speaker output streaming |
Sensors is generic: subscribe/read interface, sensor types are extensible strings, no built-in device knowledge in the ABI itself, all data is JSON.
Actuators is generic: lock/command/release interface, actuator types are extensible strings, all actions are JSON.
Audio has its own module because it’s a continuous byte stream that doesn’t fit the sensor read/poll pattern or the actuator lock/command pattern.
UI spaces are not covered by the Guest ABI. They run in the browser, in zdesk iframes, against a separate JavaScript surface.
Space types and the ABI modules they use
| Space type | Execution | Key ABI modules |
|---|---|---|
:UI | Browser (HTML/JS) | (not in Guest ABI) |
:IO | WASM | sensors, actuators, events |
:DATA | WASM | data |
:CHAT | WASM | intelligence-cache, agents |
:CALL | WASM | agents, events |
How a guest interacts with the ABI
A guest program does three things:
- Exports the lifecycle entry points:
on_init,on_process,on_close. The runtime calls these. - Imports the host functions it needs:
sys.Log,net.SendMessage,data.Get, etc. The guest calls these. - Declares its capabilities in the topology —
sys.log,net.emit_to,data.read, etc. The runtime enforces the declaration at call time.
A capability the guest doesn’t declare cannot be called — zrun rejects the import at instantiation.
Error conventions
ABI functions use a single pattern:
Return code (int32):
0= success, negative = error, positive = bytes/count.
Host ABI functions uniformly use int32 return codes. Higher-level (value, error) pairs are a Guest Library concern, not an ABI one.
Import paths
All ABI packages live under:
github.com/anrl/samoza/components/zrun/guest/abi/
Sub-packages:
abi/net — Network messaging
abi/data — Key/value storage
abi/node — Node primitives
abi/sys — System functions
abi/ic — Intelligence cache
abi/agent — Agent invocation
abi/mem — Memory management
abi/z — Low-level primitives (rarely used directly)
abi/sys/io — Stream I/O (audio capture/playback)
Compilation
Guest programs compile with TinyGo targeting WASI:
tinygo build -target wasi -scheduler=none -opt=z -o core.wasm .
Constraints:
- No
fmtpackage (bloats binary significantly). - Use
encoding/jsonfor serialization. - Use
strconvfor number-to-string conversion. sys.Log()takes exactly one string argument.main()must exist but should be empty.
Why an ABI
Three reasons:
Stability. The set of imports/exports is fixed. A guest compiled today runs against a runtime built tomorrow without recompilation.
Auditability. Every external action a guest can take is a single import call. The capability list is the audit surface.
Substitutability. Anything that compiles to the ABI runs. TinyGo today, Rust tomorrow, an Anglish-generated guest the day after — the runtime doesn’t care.
See also
- ABI reference index — the per-module specifications.
- MEX format — how an ABI-conforming WASM gets packaged.
- Develop overview — the two-layer SDK story.