nullclaw follows a vtable interface pattern for every major subsystem. Each core abstraction is a struct with a pointer to an opaque implementation and a vtable of function pointers. This gives runtime polymorphism without dynamic dispatch overhead or allocator pressure from trait objects.
pub const Interface = struct {
ptr: *anyopaque,
vtable: *const VTable,
pub const VTable = struct {
method: *const fn (ptr: *anyopaque, ...) anyerror!ReturnType,
};
pub fn method(self: Interface, ...) !ReturnType {
return self.vtable.method(self.ptr, ...);
}
};
Callers interact with the interface; implementations provide the vtable. Swap any implementation via config — zero code changes.
Important: callers must own the struct backing the vtable (local variable or heap-allocated). Never return a vtable interface pointing to a temporary.
| Subsystem | Interface | Implementations | Location |
|---|---|---|---|
| AI Models | Provider |
Anthropic, OpenAI, OpenRouter, Ollama, Gemini, Compatible, Reliable, Router | src/providers/ |
| Channels | Channel |
CLI, Telegram, Discord, Slack, WhatsApp, Matrix, IRC, iMessage, Email, Lark, DingTalk | src/channels/ |
| Memory | Memory |
SQLite (FTS5 + vector), Markdown, None | src/memory/ |
| Tools | Tool |
Shell, FileRead, FileWrite, FileEdit, HTTP, Git, Memory, Browser, Image, Schedule, Delegate, HardwareInfo | src/tools/ |
| Sandbox | Sandbox |
Landlock, Firejail, Bubblewrap, Docker, auto-detect | src/security/ |
| Runtime | RuntimeAdapter |
Native, Docker, WASM | src/runtime.zig |
| Tunnel | Tunnel |
None, Cloudflare, Tailscale, ngrok, Custom | src/tunnel.zig |
| Observer | Observer |
Noop, Log, File, Multi | src/observability.zig |
| Peripheral | Peripheral |
Serial, Arduino, RPi GPIO, STM32/Nucleo | src/peripherals.zig |
src/
main.zig CLI entry point + argument parsing
root.zig Module hierarchy (public API)
config.zig JSON config loader + 30 sub-config structs
agent/ Agent loop, tool dispatch, prompt construction
channels/ 11 channel implementations
providers/ 22+ AI provider implementations
memory/ SQLite backend, embeddings, vector search, hygiene, snapshots
tools/ 17 tool implementations
security/ Policy, audit, secrets, sandbox backends
daemon.zig Daemon supervisor with exponential backoff
gateway.zig HTTP gateway (rate limiting, pairing, webhooks)
cron.zig Cron scheduler with JSON persistence
tunnel.zig Tunnel vtable
runtime.zig Runtime vtable
peripherals.zig Hardware peripheral vtable
skillforge.zig Skill discovery and evaluation
observability.zig Logging, metrics, events
health.zig Component health registry
onboard.zig Interactive setup wizard
deinit() methodanyerror!ReturnType for flexible error propagationToolResult with .success, .output, .error_msg fields! operator forces callers to handle errors~/.nullclaw/config.json into Config structTelegramConfig, ProviderConfig, etc.)History auto-compacts at 50 messages. Tool call iteration limit: 10 per turn.
User query
↓
┌─────────────┐ ┌──────────────┐
│ FTS5 Search │ │ Vector Search│
│ (BM25) │ │ (cosine sim) │
└──────┬──────┘ └──────┬───────┘
└──────┬───────────┘
↓
Hybrid Merge
(weighted combine)
↓
Ranked Results
vector_weight / keyword_weightzig build # Debug
zig build -Doptimize=ReleaseSmall # Release (639 KB)
zig build test --summary all # 1,639 tests
Dependencies: zero Zig packages. Only libc + optional SQLite (via Homebrew on macOS).