> ## 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.

# Edge Deployment

> Deploy NullClaw to edge platforms like Cloudflare Workers

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:

1. **Edge Host** (JavaScript/TypeScript): Handles HTTP, secrets, API calls, webhook parsing
2. **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

<Steps>
  <Step title="Receive webhook">
    Cloudflare Worker receives Telegram webhook update
  </Step>

  <Step title="Extract features">
    JavaScript host extracts text features (length, question marks, keywords)
  </Step>

  <Step title="Call WASM policy">
    WASM `choose_policy()` returns response policy (concise/detailed/urgent)
  </Step>

  <Step title="Build prompt">
    Host builds system prompt based on selected policy
  </Step>

  <Step title="Call LLM">
    Send request to OpenAI Chat Completions API
  </Step>

  <Step title="Send response">
    Reply sent back to Telegram chat
  </Step>
</Steps>

### WASM Core

The agent logic is a tiny Zig module compiled to WASM:

```zig agent_core.zig theme={null}
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:

```bash theme={null}
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:

```bash theme={null}
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:

```javascript worker.mjs theme={null}
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

```toml wrangler.toml theme={null}
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](https://developers.cloudflare.com/workers/wrangler/)
* Zig 0.15.2
* Telegram bot token (or other webhook provider)
* OpenAI API key

## Deployment

<Steps>
  <Step title="Build WASM core">
    ```bash theme={null}
    cd examples/edge/cloudflare-worker
    ./build_wasm.sh
    ```
  </Step>

  <Step title="Configure secrets">
    ```bash theme={null}
    wrangler secret put TELEGRAM_BOT_TOKEN
    wrangler secret put OPENAI_API_KEY
    wrangler secret put TELEGRAM_WEBHOOK_SECRET
    ```
  </Step>

  <Step title="Create KV namespace (optional dedup)">
    ```bash theme={null}
    wrangler kv namespace create TELEGRAM_DEDUP
    wrangler kv namespace create TELEGRAM_DEDUP --preview
    ```

    Add to `wrangler.toml`:

    ```toml theme={null}
    [[kv_namespaces]]
    binding = "TELEGRAM_DEDUP"
    id = "your_prod_namespace_id"
    preview_id = "your_preview_namespace_id"
    ```
  </Step>

  <Step title="Deploy">
    ```bash theme={null}
    wrangler deploy
    ```
  </Step>

  <Step title="Set webhook">
    ```bash theme={null}
    curl -X POST "https://your-worker.workers.dev/telegram/set-webhook"
    ```
  </Step>
</Steps>

## 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

## Feature Extraction

The host extracts simple features from text:

````javascript theme={null}
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:

```javascript theme={null}
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:

```javascript theme={null}
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;
}
```

<Warning>
  KV has eventual consistency. This is best-effort dedup, not a strict guarantee.
</Warning>

## 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:

1. Edit `agent_core.zig`
2. Run `./build_wasm.sh`
3. Run `wrangler deploy`

No changes to the Worker host required if the exported function signature stays the same.

## Limitations

<Warning>
  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.
</Warning>
