Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt
Use this file to discover all available pages before exploring further.
NullClaw supports edge deployment with a hybrid architecture that separates network I/O from core agent logic.
Architecture
Edge deployment uses a two-layer pattern:
- Edge Host (JavaScript/TypeScript): Handles HTTP, secrets, API calls, webhook parsing
- WASM Core (Zig): Contains agent decision logic, policy selection, feature extraction
This keeps secrets and network operations in the trusted edge host while the agent logic remains portable and swappable.
┌─────────────────────────────────────┐
│ Cloudflare Worker (Edge Host) │
│ │
│ • HTTP request handling │
│ • Telegram/Discord webhooks │
│ • OpenAI/Anthropic API calls │
│ • Secret management │
│ • KV storage (dedup) │
│ │
│ ┌───────────────────────────────┐ │
│ │ agent_core.wasm (< 10 KB) │ │
│ │ │ │
│ │ • choose_policy() │ │
│ │ • extract_features() │ │
│ │ • decision logic │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
Cloudflare Worker Example
The examples/edge/cloudflare-worker/ directory demonstrates a complete edge deployment.
What It Does
Receive webhook
Cloudflare Worker receives Telegram webhook update
Extract features
JavaScript host extracts text features (length, question marks, keywords)
Call WASM policy
WASM choose_policy() returns response policy (concise/detailed/urgent)
Build prompt
Host builds system prompt based on selected policy
Call LLM
Send request to OpenAI Chat Completions API
Send response
Reply sent back to Telegram chat
WASM Core
The agent logic is a tiny Zig module compiled to WASM:
const std = @import("std");
pub const Policy = enum(u32) {
concise = 0,
detailed = 1,
urgent = 2,
};
pub export fn choose_policy(
text_len: u32,
has_question: u32,
has_urgent_keyword: u32,
has_code_hint: u32,
) u32 {
const urgent_bonus: u32 = if (text_len > 900) 1 else 0;
const detailed_bonus: u32 = if (text_len > 260) 1 else 0;
const urgent_score: u32 = has_urgent_keyword * 3 + urgent_bonus;
const detailed_score: u32 = has_question * 2 + has_code_hint * 2 + detailed_bonus;
if (urgent_score >= 3) return @intFromEnum(Policy.urgent);
if (detailed_score >= 3) return @intFromEnum(Policy.detailed);
return @intFromEnum(Policy.concise);
}
Build WASM
Compile the Zig module to WASM:
zig build-lib examples/edge/cloudflare-worker/agent_core.zig \
-target wasm32-freestanding \
-fno-entry \
-rdynamic \
-O ReleaseSmall \
-femit-bin=examples/edge/cloudflare-worker/dist/agent_core.wasm
Or use the build script:
cd examples/edge/cloudflare-worker
./build_wasm.sh
The resulting WASM module is < 10 KB.
Worker Implementation
The edge host loads the WASM module and calls it for each request:
let wasm_instance_promise;
const POLICY_CONCISE = 0;
const POLICY_DETAILED = 1;
const POLICY_URGENT = 2;
async function get_wasm_instance(env) {
if (!wasm_instance_promise) {
wasm_instance_promise = WebAssembly.instantiate(env.AGENT_CORE, {});
}
return wasm_instance_promise;
}
async function choose_policy_from_wasm(env, text) {
const inst = await get_wasm_instance(env);
const features = extract_text_features(text);
const choose_policy = inst.instance.exports.choose_policy;
return choose_policy(
features.text_len,
features.has_question,
features.has_urgent_keyword,
features.has_code_hint,
) >>> 0;
}
Wrangler Configuration
name = "nullclaw-edge-telegram"
main = "src/worker.mjs"
compatibility_date = "2026-02-26"
[vars]
OPENAI_MODEL = "gpt-4o-mini"
DEDUP_TTL_SECONDS = "86400"
[wasm_modules]
AGENT_CORE = "dist/agent_core.wasm"
Prerequisites
- Cloudflare account
- Wrangler CLI
- Zig 0.15.2
- Telegram bot token (or other webhook provider)
- OpenAI API key
Deployment
Build WASM core
cd examples/edge/cloudflare-worker
./build_wasm.sh
Configure secrets
wrangler secret put TELEGRAM_BOT_TOKEN
wrangler secret put OPENAI_API_KEY
wrangler secret put TELEGRAM_WEBHOOK_SECRET
Create KV namespace (optional dedup)
wrangler kv namespace create TELEGRAM_DEDUP
wrangler kv namespace create TELEGRAM_DEDUP --preview
Add to wrangler.toml:[[kv_namespaces]]
binding = "TELEGRAM_DEDUP"
id = "your_prod_namespace_id"
preview_id = "your_preview_namespace_id"
Set webhook
curl -X POST "https://your-worker.workers.dev/telegram/set-webhook"
Why WASM?
Separation of Concerns
- Host owns secrets: API keys, tokens, credentials stay in edge environment variables
- Host owns network: HTTP calls, webhooks, rate limiting handled by Worker
- WASM owns logic: Decision trees, policy selection, feature extraction
Portability
The same WASM module can run on:
- Cloudflare Workers
- Deno Deploy
- AWS Lambda@Edge
- Fastly Compute@Edge
- Any WASI-compatible runtime
Security
WASM provides sandboxing:
- No filesystem access
- No network access
- No system calls
- Pure function execution
Size
Zig’s ReleaseSmall optimization produces tiny WASM modules:
- agent_core.wasm: < 10 KB
- Faster cold starts on edge platforms
- Fits in Cloudflare’s 1 MB Worker script limit with room to spare
The host extracts simple features from text:
function extract_text_features(text) {
const lower = text.toLowerCase();
return {
text_len: text.length,
has_question: text.includes("?") ? 1 : 0,
has_urgent_keyword: /\b(urgent|asap|immediately|critical)\b/.test(lower) ? 1 : 0,
has_code_hint: /```|\b(code|bug|error|stack|trace)\b/.test(lower) ? 1 : 0,
};
}
These features are passed to WASM as integers (no string marshalling overhead).
Policy-Based Prompts
The worker selects system prompts based on WASM policy:
function policy_system_prompt(policy) {
if (policy === POLICY_URGENT) {
return "You are an incident-response assistant. Be concise, prioritize safety and immediate next steps.";
}
if (policy === POLICY_DETAILED) {
return "You are a technical assistant. Give concrete, step-by-step guidance with explicit commands when useful.";
}
return "You are a concise assistant. Answer directly and avoid unnecessary detail.";
}
Deduplication with KV
Telegram webhooks retry on failure. Use Cloudflare KV to deduplicate:
async function is_duplicate_update(env, update_id) {
const kv = env.TELEGRAM_DEDUP;
const dedup_key = `tg:update:${update_id}`;
const existing = await kv.get(dedup_key);
if (existing !== null) {
return true; // Already processed
}
await kv.put(dedup_key, "1", {
expirationTtl: 86400, // 24 hours
});
return false;
}
KV has eventual consistency. This is best-effort dedup, not a strict guarantee.
Monitoring
Cloudflare Workers provide built-in analytics:
- Request rate
- Error rate
- CPU time
- Edge location distribution
Access in Cloudflare dashboard under Workers > Analytics.
Evolution Strategy
To update agent behavior:
- Edit
agent_core.zig
- Run
./build_wasm.sh
- Run
wrangler deploy
No changes to the Worker host required if the exported function signature stays the same.
Limitations
Edge deployments are stateless. They cannot:
- Maintain long-running agent sessions
- Execute local tools (filesystem, shell)
- Store large context windows
Use edge deployment for:
- Webhook handlers
- Request routing
- Policy selection
- Simple Q&A bots
Use full runtime deployment for autonomous agents.