[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-74863":3},{"id":4,"name":5,"fullName":6,"owner":5,"repo":5,"description":7,"homepage":8,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":9,"rankLanguage":9,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":24,"createdAt":9,"pushedAt":9,"updatedAt":29,"readmeContent":30,"aiSummary":31,"trendingCount":15,"starSnapshotCount":15,"syncStatus":32,"lastSyncTime":33,"discoverSource":34},74863,"nullclaw","nullclaw\u002Fnullclaw","Fastest, smallest, and fully autonomous AI assistant infrastructure written in Zig","https:\u002F\u002Fnullclaw.io",null,"Zig",7678,902,56,53,0,20,42,144,60,39.87,"MIT License",false,"main",[25,26,27,28],"ai","assistant","personal","zig","2026-06-12 02:03:29","Want a simpler way to install and configure nullclaw with a UI? Try [nullhub](https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullhub)! (currently in beta)\n\n[nullhub](https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullhub) provides a UI layer for the Null ecosystem: simpler nullclaw setup and configuration, orchestration from [nullboiler](https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullboiler), observability from [nullwatch](https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullwatch), and task tracking from [nulltickets](https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnulltickets).\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"nullclaw.png\" alt=\"nullclaw\" width=\"200\" \u002F>\n\u003C\u002Fp>\n\n\u003Ch1 align=\"center\">NullClaw\u003C\u002Fh1>\n\n\u003Cp align=\"center\">\n  \u003Cstrong>Null overhead. Null compromise. 100% Zig. 100% Agnostic.\u003C\u002Fstrong>\u003Cbr>\n  \u003Cstrong>678 KB binary. ~1 MB RAM. Boots in \u003C2 ms. Runs on anything with a CPU.\u003C\u002Fstrong>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullclaw\u002Factions\u002Fworkflows\u002Fci.yml\">\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullclaw\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg\" alt=\"CI\" \u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullclaw\u002Factions\u002Fworkflows\u002Fnightly.yml\">\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullclaw\u002Factions\u002Fworkflows\u002Fnightly.yml\u002Fbadge.svg\" alt=\"Nightly\" \u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fnullclaw.github.io\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdocs-nullclaw.github.io-informational\" alt=\"Documentation\" \u002F>\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002FBfmdua22Ud\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdiscord-join%20community-5865F2?logo=discord&logoColor=white\" alt=\"Discord\" \u002F>\u003C\u002Fa>\n  \u003Ca href=\"LICENSE\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-blue.svg\" alt=\"License: MIT\" \u002F>\u003C\u002Fa>\n\u003C\u002Fp>\n\nThe smallest fully autonomous AI assistant infrastructure — a static Zig binary that fits on any $5 board, boots in milliseconds, and requires nothing but libc.\n\nDocs: [English](docs\u002Fen\u002FREADME.md) · [中文](docs\u002Fzh\u002FREADME.md) · [Contributing](CONTRIBUTING.md) · [Discord](https:\u002F\u002Fdiscord.gg\u002FBfmdua22Ud)\n\n```\n678 KB binary · \u003C2 ms startup · 5,300+ tests · 50+ providers · 19 channels · Pluggable everything\n```\n\n### Features\n\n- **Impossibly Small:** 678 KB static binary — no runtime, no VM, no framework overhead.\n- **Near-Zero Memory:** ~1 MB peak RSS. Runs comfortably on the cheapest ARM SBCs and microcontrollers.\n- **Instant Startup:** \u003C2 ms on Apple Silicon, \u003C8 ms on a 0.8 GHz edge core.\n- **True Portability:** Single self-contained binary across ARM, x86, and RISC-V. Drop it anywhere, it just runs.\n- **Feature-Complete:** 50+ providers, 19 channels, 35+ tools, 10 memory engines, multi-layer sandbox, tunnels, hardware peripherals, MCP, subagents, streaming, voice — the full stack.\n\n### Why nullclaw\n\n- **Lean by default:** Zig compiles to a tiny static binary. No allocator overhead, no garbage collector, no runtime.\n- **Secure by design:** pairing, strict sandboxing (landlock, firejail, bubblewrap, docker), explicit allowlists, workspace scoping, encrypted secrets.\n- **Fully swappable:** core systems are vtable interfaces (providers, channels, tools, memory, tunnels, peripherals, observers, runtimes).\n- **No lock-in:** OpenAI-compatible provider support + pluggable custom endpoints.\n\n## Benchmark Snapshot\n\nLocal machine benchmark (macOS arm64, Feb 2026), normalized for 0.8 GHz edge hardware.\n\n| | [OpenClaw](https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw) | [NanoBot](https:\u002F\u002Fgithub.com\u002FHKUDS\u002Fnanobot) | [PicoClaw](https:\u002F\u002Fgithub.com\u002Fsipeed\u002Fpicoclaw) | [ZeroClaw](https:\u002F\u002Fgithub.com\u002Fzeroclaw-labs\u002Fzeroclaw) | **[🦞 NullClaw](https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullclaw)** |\n|---|---|---|---|---|---|\n| **Language** | TypeScript | Python | Go | Rust | **Zig** |\n| **RAM** | > 1 GB | > 100 MB | \u003C 10 MB | \u003C 5 MB | **~1 MB** |\n| **Startup (0.8 GHz)** | > 500 s | > 30 s | \u003C 1 s | \u003C 10 ms | **\u003C 8 ms** |\n| **Binary Size** | ~28 MB (dist) | N\u002FA (Scripts) | ~8 MB | ~8.8 MB | **678 KB** |\n| **Tests** | — | — | — | 1,017 | **5,300+** |\n| **Source Files** | ~400+ | — | — | ~120 | **~230** |\n| **Cost** | Mac Mini $599 | Linux SBC ~$50 | Linux Board $10 | Any $10 hardware | **Any $5 hardware** |\n\n> Measured with `\u002Fusr\u002Fbin\u002Ftime -l` on ReleaseSmall builds. nullclaw is a static binary with zero runtime dependencies.\n\nReproduce locally:\n\n```bash\nzig build -Doptimize=ReleaseSmall\nls -lh zig-out\u002Fbin\u002Fnullclaw\n\n\u002Fusr\u002Fbin\u002Ftime -l zig-out\u002Fbin\u002Fnullclaw --help\n\u002Fusr\u002Fbin\u002Ftime -l zig-out\u002Fbin\u002Fnullclaw status\n```\n\n## Documentation\n\nStart here if you want the shortest path to install, configure, operate, or extend nullclaw.\n\nLocalized documentation lives under `docs\u002Fen\u002F` and `docs\u002Fzh\u002F`. Use the links below to jump straight to the page you need.\n\n| Need | English | 中文 |\n|---|---|---|\n| Start here | [`docs\u002Fen\u002FREADME.md`](docs\u002Fen\u002FREADME.md) | [`docs\u002Fzh\u002FREADME.md`](docs\u002Fzh\u002FREADME.md) |\n| Install | [`docs\u002Fen\u002Finstallation.md`](docs\u002Fen\u002Finstallation.md) | [`docs\u002Fzh\u002Finstallation.md`](docs\u002Fzh\u002Finstallation.md) |\n| Install Zig | [`docs\u002Fen\u002Fzig-installation.md`](docs\u002Fen\u002Fzig-installation.md) | [`docs\u002Fzh\u002Fzig-installation.md`](docs\u002Fzh\u002Fzig-installation.md) |\n| Configure | [`docs\u002Fen\u002Fconfiguration.md`](docs\u002Fen\u002Fconfiguration.md) | [`docs\u002Fzh\u002Fconfiguration.md`](docs\u002Fzh\u002Fconfiguration.md) |\n| Commands | [`docs\u002Fen\u002Fcommands.md`](docs\u002Fen\u002Fcommands.md) | [`docs\u002Fzh\u002Fcommands.md`](docs\u002Fzh\u002Fcommands.md) |\n| Development | [`docs\u002Fen\u002Fdevelopment.md`](docs\u002Fen\u002Fdevelopment.md) | [`docs\u002Fzh\u002Fdevelopment.md`](docs\u002Fzh\u002Fdevelopment.md) |\n| Operations | [`docs\u002Fen\u002Fusage.md`](docs\u002Fen\u002Fusage.md) | [`docs\u002Fzh\u002Fusage.md`](docs\u002Fzh\u002Fusage.md) |\n| Architecture | [`docs\u002Fen\u002Farchitecture.md`](docs\u002Fen\u002Farchitecture.md) | [`docs\u002Fzh\u002Farchitecture.md`](docs\u002Fzh\u002Farchitecture.md) |\n| Security | [`docs\u002Fen\u002Fsecurity.md`](docs\u002Fen\u002Fsecurity.md) | [`docs\u002Fzh\u002Fsecurity.md`](docs\u002Fzh\u002Fsecurity.md) |\n| Gateway API | [`docs\u002Fen\u002Fgateway-api.md`](docs\u002Fen\u002Fgateway-api.md) | [`docs\u002Fzh\u002Fgateway-api.md`](docs\u002Fzh\u002Fgateway-api.md) |\n\n- Specialized guides: [`CONTRIBUTING.md`](CONTRIBUTING.md), [`SECURITY.md`](SECURITY.md), [`SIGNAL.md`](SIGNAL.md)\n\n## Choose Your Path\n\n| Goal | Open this first | Then go to |\n|---|---|---|\n| First run in English | [`docs\u002Fen\u002FREADME.md`](docs\u002Fen\u002FREADME.md) | [`docs\u002Fen\u002Finstallation.md`](docs\u002Fen\u002Finstallation.md) → [`docs\u002Fen\u002Fconfiguration.md`](docs\u002Fen\u002Fconfiguration.md) → [`docs\u002Fen\u002Fusage.md`](docs\u002Fen\u002Fusage.md) |\n| Chinese Quick Start (中文快速上手) | [`docs\u002Fzh\u002FREADME.md`](docs\u002Fzh\u002FREADME.md) | [`docs\u002Fzh\u002Finstallation.md`](docs\u002Fzh\u002Finstallation.md) → [`docs\u002Fzh\u002Fconfiguration.md`](docs\u002Fzh\u002Fconfiguration.md) → [`docs\u002Fzh\u002Fusage.md`](docs\u002Fzh\u002Fusage.md) |\n| Find the right CLI command | [`docs\u002Fen\u002Fcommands.md`](docs\u002Fen\u002Fcommands.md) \u002F [`docs\u002Fzh\u002Fcommands.md`](docs\u002Fzh\u002Fcommands.md) | `nullclaw help` → task-specific subcommand page |\n| Contribute code or docs | [`CONTRIBUTING.md`](CONTRIBUTING.md) | [`docs\u002Fen\u002Fdevelopment.md`](docs\u002Fen\u002Fdevelopment.md) \u002F [`docs\u002Fzh\u002Fdevelopment.md`](docs\u002Fzh\u002Fdevelopment.md) → relevant architecture page |\n| Operate or secure a deployment | [`docs\u002Fen\u002Fusage.md`](docs\u002Fen\u002Fusage.md) \u002F [`docs\u002Fzh\u002Fusage.md`](docs\u002Fzh\u002Fusage.md) | [`docs\u002Fen\u002Fsecurity.md`](docs\u002Fen\u002Fsecurity.md) \u002F [`docs\u002Fzh\u002Fsecurity.md`](docs\u002Fzh\u002Fsecurity.md) → Gateway API |\n\n## After This README\n\n- New here: jump to [`docs\u002Fen\u002FREADME.md`](docs\u002Fen\u002FREADME.md) or [`docs\u002Fzh\u002FREADME.md`](docs\u002Fzh\u002FREADME.md) and follow the guided reading order.\n- Want commands fast: open [`docs\u002Fen\u002Fcommands.md`](docs\u002Fen\u002Fcommands.md) or [`docs\u002Fzh\u002Fcommands.md`](docs\u002Fzh\u002Fcommands.md).\n- Want to submit a PR: start with [`CONTRIBUTING.md`](CONTRIBUTING.md), then read [`docs\u002Fen\u002Fdevelopment.md`](docs\u002Fen\u002Fdevelopment.md) or [`docs\u002Fzh\u002Fdevelopment.md`](docs\u002Fzh\u002Fdevelopment.md).\n\n## Quick Start\n\n### 1) Recommended install (Homebrew)\n\nThe simplest path: install a ready-to-run binary with no extra runtime dependencies.\n\n```bash\nbrew install nullclaw\nnullclaw --help\n```\n\n### 2) Build from source\n\n> **Prerequisite:** use **Zig 0.16.0** (exact version).\n> Other Zig versions are currently unsupported and may fail to build.\n> Verify before building: `zig version` should print `0.16.0`.\n> Debian users who need Zig first can follow [`docs\u002Fen\u002Fzig-installation.md`](docs\u002Fen\u002Fzig-installation.md).\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fnullclaw\u002Fnullclaw.git\ncd nullclaw\nzig build -Doptimize=ReleaseSmall\nzig build test --summary all\n```\n\nMake `nullclaw` available on `PATH`:\n\nmacOS\u002FLinux (zsh\u002Fbash):\n\n```bash\nzig build -Doptimize=ReleaseSmall -p \"$HOME\u002F.local\"\necho 'export PATH=\"$HOME\u002F.local\u002Fbin:$PATH\"' >> ~\u002F.zshrc\n# or ~\u002F.bashrc\n```\n\nWindows (PowerShell):\n\n```powershell\nzig build -Doptimize=ReleaseSmall -p \"$HOME\\.local\"\n\n$bin = \"$HOME\\.local\\bin\"\n$user_path = [Environment]::GetEnvironmentVariable(\"Path\", \"User\")\nif (-not ($user_path -split \";\" | Where-Object { $_ -eq $bin })) {\n  [Environment]::SetEnvironmentVariable(\"Path\", \"$user_path;$bin\", \"User\")\n}\n$env:Path = \"$env:Path;$bin\"\n```\n\nThen:\n\n```bash\nnullclaw --help\n```\n\n### 3) Common commands\n\n```bash\n\n# Quick setup\nnullclaw onboard --api-key sk-... --provider openrouter\n\n# Or interactive wizard\nnullclaw onboard --interactive\n\n# Chat\nnullclaw agent -m \"Hello, nullclaw!\"\n\n# Interactive mode\nnullclaw agent\n\n# Start gateway runtime (gateway + all configured channels\u002Faccounts + heartbeat + scheduler)\nnullclaw gateway                # default: 127.0.0.1:3000\nnullclaw gateway --port 8080    # custom port\n\n# Check status\nnullclaw status\n\n# Run system diagnostics\nnullclaw doctor\n\n# Check channel health\nnullclaw channel status\n\n# Start specific channels\nnullclaw channel start telegram\nnullclaw channel start discord\nnullclaw channel start signal\n\n# Manage background service\n# Linux supports systemd user services and OpenRC\nnullclaw service install\nnullclaw service status\n\n# Optional secret injection hook for service mode:\n# if ~\u002F.nullclaw\u002Fservice-env is executable, the installed service launcher runs it\n# before starting `nullclaw gateway` (for example via dotenvx or sops)\n\n# Migrate memory from OpenClaw\nnullclaw migrate openclaw --dry-run\nnullclaw migrate openclaw\n```\n\n## Edge MVP (Hybrid Host + WASM Logic)\n\nIf you want edge deployment (Cloudflare Worker) with Telegram + OpenAI while keeping agent policy in WASM, see:\n\n`examples\u002Fedge\u002Fcloudflare-worker\u002F`\n\nThis pattern keeps networking\u002Fsecrets in the edge host and lets you swap\u002Fupdate logic by replacing a tiny Zig WASM module.\n\n## Architecture\n\nEvery subsystem is a **vtable interface** — swap implementations with a config change, zero code changes.\n\n| Subsystem | Interface | Ships with | Extend |\n|-----------|-----------|------------|--------|\n| **AI Models** | `Provider` | 50+ providers (OpenRouter, Anthropic, OpenAI, Azure OpenAI, Gemini, Vertex AI, Ollama, Venice, Groq, Mistral, xAI, DeepSeek, Together, Fireworks, Perplexity, Cohere, Bedrock, and many OpenAI-compatible endpoints) | `custom:https:\u002F\u002Fyour-api.com` — any OpenAI-compatible API |\n| **Channels** | `Channel` | CLI, Telegram, Signal, Discord, Slack, iMessage, Matrix, WhatsApp, Webhook, IRC, Lark\u002FFeishu, OneBot, Line, DingTalk, Email, Nostr, QQ, MaixCam, Mattermost | Any messaging API |\n| **Memory** | `Memory` | SQLite with hybrid search (FTS5 + vector cosine similarity), Markdown, ClickHouse, PostgreSQL, Redis, LanceDB, Lucid, LRU, API | Any persistence backend |\n| **Tools** | `Tool` | shell, file_read, file_write, file_edit, file_edit_hashed, file_read_hashed, file_append, memory_store, memory_recall, memory_forget, memory_list, browser_open, screenshot, composio, http_request, web_fetch, web_search, delegate, schedule, hardware_info, hardware_memory, pushover, message, spawn, git, image, i2c, spi, and more | Any capability |\n| **Observability** | `Observer` | Noop, Log, File, Multi | Prometheus, OTel |\n| **Runtime** | `RuntimeAdapter` | Native, Docker (sandboxed), WASM (wasmtime) | Any runtime |\n| **Security** | `Sandbox` | Landlock, Firejail, Bubblewrap, Docker, auto-detect | Any sandbox backend |\n| **Identity** | `IdentityConfig` | OpenClaw (markdown), AIEOS v1.1 (JSON) | Any identity format |\n| **Tunnel** | `Tunnel` | None, Cloudflare, Tailscale, ngrok, Custom | Any tunnel binary |\n| **Heartbeat** | Engine | HEARTBEAT.md periodic tasks | — |\n| **Skills** | Loader | TOML\u002FJSON manifests or YAML frontmatter in `SKILL.md` | Community skill packs |\n| **Peripherals** | `Peripheral` | Serial, Arduino, Raspberry Pi GPIO, STM32\u002FNucleo | Any hardware interface |\n| **Cron** | Scheduler | Cron expressions + one-shot timers with JSON persistence | — |\n\n### Memory System\n\nAll custom, zero external dependencies for the core path:\n\n| Layer | Implementation |\n|-------|---------------|\n| **Vector DB** | Embeddings stored as BLOB in SQLite, cosine similarity search |\n| **Keyword Search** | FTS5 virtual tables with BM25 scoring |\n| **Hybrid Merge** | Weighted merge (configurable vector\u002Fkeyword weights) |\n| **Embeddings** | `EmbeddingProvider` vtable — OpenAI, custom URL, or noop |\n| **Hygiene** | Automatic archival + purge of stale memories |\n| **Snapshots** | Export\u002Fimport full memory state for migration |\n| **Engines** | SQLite (default), Markdown, ClickHouse, PostgreSQL, Redis, LanceDB, Lucid, LRU, API, None |\n\n```json\n{\n  \"memory\": {\n    \"backend\": \"sqlite\",\n    \"auto_save\": true,\n    \"embedding_provider\": \"openai\",\n    \"vector_weight\": 0.7,\n    \"keyword_weight\": 0.3,\n    \"hygiene_enabled\": true,\n    \"snapshot_enabled\": false\n  }\n}\n```\n\n## Security\n\nnullclaw enforces security at **every layer**.\n\n| # | Item | Status | How |\n|---|------|--------|-----|\n| 1 | **Gateway not publicly exposed** | Done | Binds `127.0.0.1` by default. Refuses `0.0.0.0` without tunnel or explicit `allow_public_bind`. |\n| 2 | **Pairing required** | Done | 6-digit one-time code on startup. Exchange via `POST \u002Fpair` for bearer token. |\n| 3 | **Filesystem scoped** | Done | `workspace_only = true` by default. Null byte injection blocked. Symlink escape detection. |\n| 4 | **Access via tunnel only** | Done | Gateway refuses public bind without active tunnel. Supports Tailscale, Cloudflare, ngrok, or custom. |\n| 5 | **Sandbox isolation** | Done | Auto-detects best backend: Landlock, Firejail, Bubblewrap, or Docker. |\n| 6 | **Encrypted secrets** | Done | API keys encrypted with ChaCha20-Poly1305 using local key file. |\n| 7 | **Resource limits** | Done | Configurable memory, CPU, disk, and subprocess limits. |\n| 8 | **Audit logging** | Done | Signed event trail with configurable retention. |\n\n### Channel Allowlists\n\n- Empty allowlist = **deny all inbound messages**\n- `\"*\"` = **allow all** (explicit opt-in)\n- Otherwise = exact-match allowlist\n\nNostr additionally: the `owner_pubkey` is **always** allowed regardless of `dm_allowed_pubkeys`. Private keys are encrypted at rest via SecretStore (`enc2:` prefix) and only decrypted into memory while the channel is running; zeroed on channel stop.\n\n### Nostr Channel Setup\n\n`nullclaw` speaks Nostr natively via NIP-17 (gift-wrapped private DMs) and NIP-04 (legacy DMs), using [`nak`](https:\u002F\u002Fgithub.com\u002Ffiatjaf\u002Fnak).\n\n**Prerequisites:** Install `nak` and ensure it's in your `$PATH`.\n\n**Setup via onboarding wizard:**\n\n```bash\nnullclaw onboard --interactive   # Step 7 configures Nostr\n```\n\nThe wizard will:\n1. Generate a new keypair for your bot or import a key & encrypt it with ChaCha20-Poly1305\n2. Ask for your (owner) pubkey (npub or hex) — always allowed through DM policy\n3. Configure relays and DM relays (kind:10050 inbox)\n4. Display the bot's pubkey\n\nOr configure manually in the [config](#configuration).\n\n**How it works:** On startup, nullclaw announces its DM inbox relays (kind:10050), then listens for incoming NIP-17 gift wraps and NIP-04 encrypted DMs. Outbound messages mirror the sender's protocol. Multi-relay rumor deduplication prevents duplicate responses when the same message is delivered via multiple relays.\n\n## Configuration\n\nConfig: `~\u002F.nullclaw\u002Fconfig.json` (created by `onboard`)\n\n> **OpenClaw compatible:** nullclaw uses the same config structure as [OpenClaw](https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw) (snake_case). Providers live under `models.providers`, the default model under `agents.defaults.model.primary`, and channels use `accounts` wrappers.\n> Top-level `default_provider` \u002F `default_model` keys are not supported.\n>\n> **Vertex AI note:** `models.providers.vertex.api_key` supports either:\n> 1. a bearer token (`ya29...`), or\n> 2. a full Google service-account JSON object (same shape as Apps Script `GEMINI_KEY` with `project_id`, `client_email`, `private_key`).\n>\n> `models.providers.vertex.base_url` can be set explicitly (`...\u002Fprojects\u002F\u003Cid>\u002Flocations\u002F\u003Cloc>\u002Fpublishers\u002Fgoogle\u002Fmodels`), or omitted when service-account JSON is used (nullclaw will derive it from `project_id`, with `VERTEX_LOCATION` defaulting to `global`).\n> Service-account mode requires `openssl` available in `$PATH` for RS256 JWT signing.\n\n```json\n{\n  \"default_temperature\": 0.7,\n\n  \"models\": {\n    \"providers\": {\n      \"openrouter\": { \"api_key\": \"sk-or-...\" },\n      \"groq\": { \"api_key\": \"gsk_...\" },\n      \"vertex\": {\n        \"api_key\": {\n          \"type\": \"service_account\",\n          \"project_id\": \"your-project\",\n          \"client_email\": \"svc@your-project.iam.gserviceaccount.com\",\n          \"private_key\": \"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\"\n        },\n        \"base_url\": \"https:\u002F\u002Faiplatform.googleapis.com\u002Fv1\u002Fprojects\u002Fyour-project\u002Flocations\u002Fglobal\u002Fpublishers\u002Fgoogle\u002Fmodels\"\n      },\n      \"anthropic\": { \"api_key\": \"sk-ant-...\", \"base_url\": \"https:\u002F\u002Fapi.anthropic.com\" }\n    }\n  },\n\n  \"agents\": {\n    \"defaults\": {\n      \"model\": { \"primary\": \"openrouter\u002Fanthropic\u002Fclaude-sonnet-4\" },\n      \"heartbeat\": { \"every\": \"30m\" }\n    },\n    \"list\": [\n      { \"id\": \"researcher\", \"model\": { \"primary\": \"openrouter\u002Fanthropic\u002Fclaude-opus-4\" }, \"system_prompt\": \"...\" }\n    ]\n  },\n\n  \"channels\": {\n    \"telegram\": {\n      \"accounts\": {\n        \"main\": {\n          \"bot_token\": \"123:ABC\",\n          \"allow_from\": [\"user1\"],\n          \"reply_in_private\": true,\n          \"proxy\": \"socks5:\u002F\u002F...\"\n        }\n      }\n    },\n    \"discord\": {\n      \"accounts\": {\n        \"main\": {\n          \"token\": \"disc-token\",\n          \"guild_id\": \"12345\",\n          \"allow_from\": [\"user1\"],\n          \"allow_bots\": false\n        }\n      }\n    },\n    \"irc\": {\n      \"accounts\": {\n        \"main\": {\n          \"host\": \"irc.libera.chat\",\n          \"port\": 6697,\n          \"nick\": \"nullclaw\",\n          \"channel\": \"#nullclaw\",\n          \"tls\": true,\n          \"allow_from\": [\"user1\"]\n        },\n        \"meshrelay\": {\n          \"host\": \"irc.meshrelay.xyz\",\n          \"port\": 6697,\n          \"nick\": \"nullclaw\",\n          \"channels\": [\"#agents\"],\n          \"tls\": true,\n          \"nickserv_password\": \"YOUR_NICKSERV_PASSWORD\",\n          \"allow_from\": [\"*\"]\n        }\n      }\n    },\n    \"slack\": {\n      \"accounts\": {\n        \"main\": {\n          \"bot_token\": \"xoxb-...\",\n          \"app_token\": \"xapp-...\",\n          \"allow_from\": [\"user1\"]\n        }\n      }\n    },\n    \"nostr\": {\n      \"private_key\": \"enc2:...\",\n      \"owner_pubkey\": \"hex-pubkey-of-owner\",\n      \"relays\": [\"wss:\u002F\u002Frelay.damus.io\", \"wss:\u002F\u002Fnos.lol\", \"wss:\u002F\u002Frelay.nostr.band\"],\n      \"dm_allowed_pubkeys\": [\"*\"],\n      \"display_name\": \"NullClaw\",\n      \"about\": \"AI assistant on Nostr\",\n      \"nip05\": \"nullclaw@yourdomain.com\",\n      \"lnurl\": \"lnurl1...\"\n    }\n  },\n\n  \"tools\": {\n    \"media\": {\n      \"audio\": {\n        \"enabled\": true,\n        \"language\": \"ru\",\n        \"models\": [{ \"provider\": \"groq\", \"model\": \"whisper-large-v3\" }]\n      }\n    }\n  },\n\n  \"mcp_servers\": {\n    \"filesystem\": {\n      \"transport\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@modelcontextprotocol\u002Fserver-filesystem\"]\n    },\n    \"remote\": {\n      \"transport\": \"http\",\n      \"url\": \"https:\u002F\u002Fmcp.example.com\u002Frpc\",\n      \"timeout_ms\": 10000,\n      \"headers\": {\n        \"Authorization\": \"Bearer example-token\"\n      }\n    }\n  },\n\n  \"memory\": {\n    \"backend\": \"sqlite\",\n    \"auto_save\": true,\n    \"embedding_provider\": \"openai\",\n    \"vector_weight\": 0.7,\n    \"keyword_weight\": 0.3\n  },\n\n  \"gateway\": {\n    \"port\": 3000,\n    \"require_pairing\": true,\n    \"allow_public_bind\": false\n  },\n\n  \"autonomy\": {\n    \"level\": \"supervised\",\n    \"workspace_only\": true,\n    \"max_actions_per_hour\": 20\n  },\n\n  \"runtime\": {\n    \"kind\": \"native\",\n    \"docker\": {\n      \"image\": \"alpine:3.20\",\n      \"network\": \"none\",\n      \"memory_limit_mb\": 512,\n      \"read_only_rootfs\": true\n    }\n  },\n\n\n  \"tunnel\": { \"provider\": \"none\" },\n  \"secrets\": { \"encrypt\": true },\n  \"identity\": { \"format\": \"openclaw\" },\n\n  \"security\": {\n    \"sandbox\": { \"backend\": \"auto\" },\n    \"resources\": { \"max_memory_mb\": 512, \"max_cpu_percent\": 80 },\n    \"audit\": { \"enabled\": true, \"retention_days\": 90 }\n  }\n}\n```\n\nConfig values are literal. NullClaw does not expand `${VAR}` inside `config.json`\nstrings, including custom header values. If you need environment-based secrets,\nrender `config.json` ahead of time with your own deployment tooling.\n\nTelegram forum topics:\n\n- Topic session isolation is automatic. You do not add a `topic_id` field under `channels.telegram`.\n- The easiest operator flow is:\n  1. define named agent profiles under `agents.list`\n  2. open the target Telegram chat or forum topic\n  3. run `\u002Fbind \u003Cagent>`\n- To bind a specific Telegram forum topic to a specific agent, use `bindings[].match.peer.id` with the canonical thread form `\"\u003Cchat_id>:thread:\u003Ctopic_id>\"`.\n- To bind the whole Telegram group as a fallback for all other topics, keep a normal group binding with `\"\u003Cchat_id>\"`.\n- `\u002Fbind status` shows the current effective route and the available agent ids.\n- `\u002Fbind clear` removes only the exact binding for the current account\u002Fchat\u002Ftopic and falls back to the broader route.\n- `\u002Fbind` persists an exact `bindings[]` entry for the current Telegram account and peer.\n- `\u002Fbind status` distinguishes an exact local override from an inherited broader fallback.\n- Topic-specific bindings win over group fallback by route priority; the order in `bindings[]` does not matter.\n- Telegram menu visibility for `\u002Fbind` is controlled by `channels.telegram.accounts.\u003Cid>.binding_commands_enabled`.\n\nExample:\n\n```json\n{\n  \"bindings\": [\n    {\n      \"agent_id\": \"coder\",\n      \"match\": {\n        \"channel\": \"telegram\",\n        \"account_id\": \"main\",\n        \"peer\": { \"kind\": \"group\", \"id\": \"-1001234567890:thread:42\" }\n      }\n    },\n    {\n      \"agent_id\": \"orchestrator\",\n      \"match\": {\n        \"channel\": \"telegram\",\n        \"account_id\": \"main\",\n        \"peer\": { \"kind\": \"group\", \"id\": \"-1001234567890\" }\n      }\n    }\n  ]\n}\n```\n\nIn that setup, topic `42` routes to `coder`, while the rest of the forum falls back to `orchestrator`.\n\nNamed agent profiles are configured separately from bindings. Bindings only choose which named agent handles a given chat\u002Ftopic.\n\nIf a named agent should run from its own workspace, set `agents.list[].workspace_path`.\nRelative paths are resolved from the directory that contains `config.json`, the workspace is scaffolded on first use, and the agent gets a durable memory namespace `agent:\u003Cagent-id>`.\nSetting `workspace_path` does not disable `system_prompt`: when both are configured, the named profile prompt is still applied and the workspace bootstrap files are loaded from that dedicated workspace.\nThis applies to `nullclaw agent --agent \u003Cid>`, `\u002Fsubagents spawn --agent \u003Cid>`, and routed sessions resolved through `bindings`.\n\nMinimal end-to-end example:\n\n```json\n{\n  \"agents\": {\n    \"list\": [\n      {\n        \"id\": \"orchestrator\",\n        \"provider\": \"openrouter\",\n        \"model\": \"anthropic\u002Fclaude-sonnet-4\"\n      },\n      {\n        \"id\": \"coder\",\n        \"provider\": \"ollama\",\n        \"model\": \"qwen2.5-coder:14b\",\n        \"system_prompt\": \"You are the coding agent for this topic.\"\n      }\n    ]\n  },\n  \"channels\": {\n    \"telegram\": {\n      \"accounts\": {\n        \"main\": {\n          \"bot_token\": \"123456:ABCDEF\",\n          \"allow_from\": [\"YOUR_TELEGRAM_USER_ID\"],\n          \"binding_commands_enabled\": true,\n          \"topic_commands_enabled\": true,\n          \"topic_map_command_enabled\": true,\n          \"commands_menu_mode\": \"scoped\"\n        }\n      }\n    }\n  },\n  \"bindings\": [\n    {\n      \"agent_id\": \"orchestrator\",\n      \"match\": {\n        \"channel\": \"telegram\",\n        \"account_id\": \"main\",\n        \"peer\": { \"kind\": \"group\", \"id\": \"-1001234567890\" }\n      }\n    }\n  ]\n}\n```\n\nOperator flow:\n\n- Send `\u002Fbind coder` inside the target forum topic.\n- `nullclaw` writes a new exact `bindings[]` entry to `~\u002F.nullclaw\u002Fconfig.json` for that topic and Telegram account.\n- The next message in that topic uses the new routed agent profile.\n- `nullclaw` must have write access to `~\u002F.nullclaw\u002Fconfig.json` for `\u002Fbind` to persist changes.\n\nAbout `account_id`:\n\n- `account_id` identifies the configured Telegram account entry, not a topic and not an agent.\n- In the usual `channels.telegram.accounts` form, the object key becomes the account id. For example, `accounts.main` means `account_id = \"main\"`, and `accounts.backup` means `account_id = \"backup\"`.\n- In `bindings`, `match.account_id` limits that binding to one specific Telegram account.\n- If `match.account_id` is omitted, the binding can match any Telegram account for that channel.\n- Use different account ids only when you run multiple Telegram bot accounts\u002Ftokens in the same nullclaw instance.\n\nEffect on delivery:\n\n- Incoming Telegram updates are processed by the account that received them.\n- Routing uses that same `account_id`, so `match.account_id = \"main\"` matches only messages received by `channels.telegram.accounts.main`.\n- Replies go back out through the same Telegram account\u002Fruntime that handled the message.\n- Setting one binding to `account_id = \"main\"` and another to `account_id = \"sub\"` does not split one chat across two agents automatically; it scopes each binding to a different configured Telegram account.\n\n### Full Web Search + Shell Access\n\nUse this when you want full web-search provider control plus unrestricted shell command allowlist behavior:\n\n```json\n{\n  \"http_request\": {\n    \"enabled\": true,\n    \"search_base_url\": \"https:\u002F\u002Fsearx.example.com\",\n    \"search_provider\": \"auto\",\n    \"search_fallback_providers\": [\"jina\", \"duckduckgo\"]\n  },\n  \"autonomy\": {\n    \"level\": \"full\",\n    \"allowed_commands\": [\"*\"],\n    \"allowed_paths\": [\"*\"],\n    \"require_approval_for_medium_risk\": false,\n    \"block_medium_risk_commands\": false,\n    \"block_high_risk_commands\": false\n  }\n}\n```\n\n- `http_request.search_base_url` accepts either instance root (`https:\u002F\u002Fhost`) or explicit endpoint (`https:\u002F\u002Fhost\u002Fsearch`); local\u002Fprivate SearXNG instances may also use plain HTTP such as `http:\u002F\u002Flocalhost:8888` or `http:\u002F\u002F192.168.1.10:8888\u002Fsearch`.\n- Invalid `http_request.search_base_url` now fails config validation at startup (no automatic fallback for malformed URL).\n- `http_request.search_provider` supports: `auto`, `searxng`, `duckduckgo` (`ddg`), `brave`, `firecrawl`, `tavily`, `perplexity`, `exa`, `jina`.\n- `http_request.search_fallback_providers` is optional and is tried in order when the primary provider fails.\n- Provider env vars: `BRAVE_API_KEY`, `FIRECRAWL_API_KEY`, `TAVILY_API_KEY`, `PERPLEXITY_API_KEY`, `EXA_API_KEY`, `JINA_API_KEY` (or shared `WEB_SEARCH_API_KEY` where supported). DuckDuckGo and SearXNG do not require API keys.\n- `allowed_commands` entries support `\"cmd\"`, `\"cmd *\"`, and `\"*\"` formats.\n  - `\"cmd\"` and `\"cmd *\"` both allow that command family at the allowlist stage.\n  - `\"*\"` allows any command at the allowlist stage.\n- High-risk and medium-risk runtime gates still apply after the allowlist check; set `block_medium_risk_commands: false` when you intentionally allow network\u002Ftransfer commands such as `curl`\u002F`wget` or other medium-risk mutations.\n- `allowed_paths: [\"*\"]` allows access outside workspace, except system-protected paths.\n\n### Web UI \u002F Browser Relay\n\nUse `channels.web` for browser UI events (WebChannel v1):\n\n```json\n{\n  \"channels\": {\n    \"web\": {\n      \"accounts\": {\n        \"default\": {\n          \"transport\": \"local\",\n          \"listen\": \"127.0.0.1\",\n          \"port\": 32123,\n          \"path\": \"\u002Fws\",\n          \"auth_token\": \"replace-with-long-random-token\",\n          \"message_auth_mode\": \"pairing\",\n          \"allowed_origins\": [\"http:\u002F\u002Flocalhost:5173\", \"chrome-extension:\u002F\u002Fyour-extension-id\"]\n        }\n      }\n    }\n  }\n}\n```\n\n- Local: keep `\"listen\": \"127.0.0.1\"`.\n- `message_auth_mode` controls inbound `user_message` auth:\n  - `\"pairing\"` (default): send `pairing_request`, receive `pairing_result`, include UI `access_token` in every `user_message`.\n  - `\"token\"` (local transport only): include `auth_token` in each `user_message` payload (`access_token` is also accepted for compatibility).\n- `auth_token` hardens the WebSocket upgrade and becomes required when binding non-loopback addresses.\n- Unauthenticated WebSocket upgrade is loopback-only. Pairing-first local UX works on `127.0.0.1`, but a public\u002FLAN bind must authenticate the `\u002Fws` upgrade on the first hop with `?token=\u003Cauth_token>` or `Authorization: Bearer \u003Cauth_token>`.\n- Local loopback pairing no longer depends on a fixed shared code. `pairing_request` may omit `payload.pairing_code`, and legacy loopback clients that still send `123456` remain compatible.\n- `\u002Fws` is the WebSocket endpoint. `\u002Fpair` belongs to the HTTP gateway API and is not part of the web channel handshake.\n- Remote\u002Fheadless host: if you bind `\"listen\": \"0.0.0.0\"`, prefer a stable configured token plus `message_auth_mode: \"token\"` behind TLS\u002Freverse proxy, or keep loopback bind and expose it through SSH tunnel\u002Fproxy.\n- UI\u002Fextension should live in a separate repository and connect via this WebSocket endpoint.\n- For orchestration, use local token mode with a stable token from config or env (`NULLCLAW_WEB_TOKEN`, `NULLCLAW_GATEWAY_TOKEN`, `OPENCLAW_GATEWAY_TOKEN`).\n- Relay transport (outbound agent socket) is configured via:\n\n```json\n{\n  \"channels\": {\n    \"web\": {\n      \"accounts\": {\n        \"default\": {\n          \"transport\": \"relay\",\n          \"relay_url\": \"wss:\u002F\u002Frelay.nullclaw.io\u002Fws\u002Fagent\",\n          \"relay_agent_id\": \"default\",\n          \"relay_token\": \"replace-with-relay-token\",\n          \"relay_token_ttl_secs\": 2592000,\n          \"relay_pairing_code_ttl_secs\": 300,\n          \"relay_ui_token_ttl_secs\": 86400,\n          \"relay_e2e_required\": false\n        }\n      }\n    }\n  }\n}\n```\n\n- Relay token lifecycle (dedicated): `relay_token` (config) -> `NULLCLAW_RELAY_TOKEN` (env) -> persisted `web-relay-\u003Caccount_id>` credential -> generated token.\n- Relay UI handshake: send `pairing_request` with one-time `pairing_code`, receive `pairing_result` with UI `access_token` JWT (and optional `set_cookie` string for relay HTTP layer).\n- Relay `user_message` must include valid UI JWT in `access_token` (top-level or `payload.access_token`).\n- If E2E is enabled (`relay_e2e_required=true`), UI and agent exchange X25519 keys during pairing and send encrypted payloads in `payload.e2e`.\n- WebChannel event envelope is defined in [`spec\u002Fwebchannel_v1.json`](spec\u002Fwebchannel_v1.json).\n\n## Gateway API\n\n| Endpoint | Method | Auth | Description |\n|----------|--------|------|-------------|\n| `\u002Fhealth` | GET | None | Health check (always public) |\n| `\u002Fpair` | POST | `X-Pairing-Code` header | Exchange one-time code for bearer token |\n| `\u002Fwebhook` | POST | `Authorization: Bearer \u003Ctoken>` | Send message: `{\"message\": \"your prompt\"}` |\n| `\u002F.well-known\u002Fagent-card.json` | GET | None | A2A Agent Card discovery (public) |\n| `\u002Fa2a` | POST | `Authorization: Bearer \u003Ctoken>` | A2A JSON-RPC endpoint (canonical methods plus legacy slash aliases) |\n| `\u002Fwhatsapp` | GET | Query params | Meta webhook verification |\n| `\u002Fwhatsapp` | POST | None (Meta signature) | WhatsApp incoming message webhook |\n\n### A2A (Agent-to-Agent Protocol v0.3.0)\n\nNullClaw implements Google's [A2A protocol](https:\u002F\u002Fgithub.com\u002Fgoogle\u002FA2A) v0.3.0, allowing any A2A-compatible agent or client to discover, authenticate, and interact with your instance over JSON-RPC 2.0.\n\nEnable in `~\u002F.nullclaw\u002Fconfig.json`:\n\n```json\n{\n  \"a2a\": {\n    \"enabled\": true,\n    \"name\": \"nullclaw\",\n    \"description\": \"General-purpose AI assistant\",\n    \"url\": \"https:\u002F\u002Fexample.com\",\n    \"version\": \"1.0.0\"\n  }\n}\n```\n\n**Endpoints:**\n\n| Endpoint | Auth | Description |\n|----------|------|-------------|\n| `GET \u002F.well-known\u002Fagent-card.json` | None | Agent Card discovery (public) |\n| `POST \u002Fa2a` | Bearer token | JSON-RPC 2.0 dispatch |\n\n**Supported methods:** `message\u002Fsend`, `message\u002Fstream`, `tasks\u002Fget`, `tasks\u002Fcancel`, `tasks\u002Flist`, `tasks\u002Fresubscribe`.\n\n**Quick test:**\n\n```bash\n# 1. Get a bearer token\n# Set PAIRING_CODE to the one-time code for this gateway session.\nTOKEN=$(curl -s -X POST -H \"X-Pairing-Code: $PAIRING_CODE\" http:\u002F\u002Flocalhost:3000\u002Fpair | jq -r .token)\n\n# 2. Discover the agent\ncurl http:\u002F\u002Flocalhost:3000\u002F.well-known\u002Fagent-card.json\n\n# 3. Send a message\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"message\u002Fsend\",\"params\":{\"message\":{\"messageId\":\"msg-1\",\"role\":\"user\",\"parts\":[{\"kind\":\"text\",\"text\":\"Hello\"}]}}}' \\\n  http:\u002F\u002Flocalhost:3000\u002Fa2a\n```\n\nSee [Gateway API docs](docs\u002Fen\u002Fgateway-api.md) for full A2A reference including streaming, task lifecycle, and configuration details.\n\n## Commands\n\n| Command | Description |\n|---------|-------------|\n| `onboard --api-key sk-... --provider openrouter` | Quick setup with API key and provider |\n| `onboard --interactive` | Full interactive wizard |\n| `onboard --channels-only` | Reconfigure channels\u002Fallowlists only |\n| `agent -m \"...\"` | Single message mode |\n| `acp` | Start the Agent Client Protocol stdio adapter for ACP-compatible editors |\n| `agent` | Interactive chat mode |\n| `gateway` | Start long-running runtime (default: `127.0.0.1:3000`) |\n| `service install\\|start\\|stop\\|restart\\|status\\|uninstall` | Manage background service |\n| `doctor` | Diagnose system health |\n| `status [--json]` | Show full system status or emit the machine-readable runtime snapshot |\n| `channel list\\|info\\|start\\|status\\|add\\|remove` | Manage channels, including JSON account inventory for automation |\n| `cron list\\|status\\|add\\|add-agent\\|once\\|once-agent\\|remove\\|pause\\|resume\\|run\\|update\\|runs` | Manage scheduled tasks |\n| `skills list\\|install\\|remove\\|info` | Manage skill packs, including `install --name \u003Cquery>` registry search |\n| `history list\\|show` | View session conversation history |\n| `memory stats\\|count\\|reindex\\|search\\|get\\|list\\|drain-outbox\\|forget` | Inspect and maintain memory |\n| `hardware scan\\|flash\\|monitor` | Hardware device management |\n| `config show\\|get`, `models list\\|summary\\|info\\|benchmark\\|refresh` | Read config\u002Fmodel admin state and manage the model catalog |\n| `workspace edit\\|reset-md` | Maintain workspace markdown\u002Fbootstrap files |\n| `capabilities [--json]` | Show runtime capabilities manifest |\n| `auth login\\|status\\|logout` | Manage OAuth authentication |\n| `migrate openclaw [--dry-run] [--source PATH]` | Import memory + migrate config from OpenClaw |\n| `update [--check] [--yes]` | Check for and install updates |\n\n## Development\n\nBuild and tests are pinned to **Zig 0.16.0**.\n\n```bash\nzig build                          # Dev build\nzig build -Doptimize=ReleaseSmall  # Release build (678 KB)\nzig build test --summary all       # 5,300+ tests\n```\n\n### Channel Flow Coverage\n\nChannel CJM coverage (ingress parsing\u002Ffiltering, session key routing, account propagation, bus handoff) is validated by tests in:\n\n- `src\u002Fchannel_manager.zig` (runtime channel registration\u002Fstart semantics + listener mode wiring)\n- `src\u002Fconfig.zig` (OpenClaw-compatible `channels.*.accounts` parsing, multi-account selection\u002Fordering, aliases)\n- `src\u002Fgateway.zig` (Telegram\u002FWhatsApp\u002FLINE\u002FLark routed session keys from webhook payloads)\n- `src\u002Fdaemon.zig` (gateway-loop inbound route resolution for Discord\u002FQQ\u002FOneBot\u002FMattermost\u002FMaixCam)\n- `src\u002Fchannels\u002Fdiscord.zig`, `src\u002Fchannels\u002Fmattermost.zig`, `src\u002Fchannels\u002Fqq.zig`, `src\u002Fchannels\u002Fonebot.zig`, `src\u002Fchannels\u002Fsignal.zig`, `src\u002Fchannels\u002Fline.zig`, `src\u002Fchannels\u002Fwhatsapp.zig` (per-channel inbound\u002Foutbound contracts)\n\n### Project Stats\n\n```\nLanguage:     Zig 0.16.0\nSource files: ~250\nLines of code: ~249,000\nTests:        5,300+\nBinary:       678 KB (ReleaseSmall)\nPeak RSS:     ~1 MB\nStartup:      \u003C2 ms (Apple Silicon)\nDependencies: 0 (besides libc + optional SQLite)\n```\n\n### Source Layout\n\n```\nsrc\u002F\n  main.zig              CLI entry point + argument parsing\n  root.zig              Module hierarchy (public API)\n  config.zig            JSON config loader + 30 sub-config structs\n  agent.zig             Agent loop, auto-compaction, tool dispatch\n  daemon.zig            Daemon supervisor with exponential backoff\n  gateway.zig           HTTP gateway (rate limiting, idempotency, pairing)\n  channels\u002F             19 channel implementations (telegram, signal, discord, slack, nostr, matrix, whatsapp, line, lark, onebot, mattermost, qq, ...)\n  providers\u002F            50+ AI provider integrations\n  memory\u002F               SQLite backend, embeddings, vector search, hygiene, snapshots\n  tools\u002F                35+ tool implementations\n  security\u002F             Secrets (ChaCha20), sandbox backends (landlock, firejail, ...)\n  cron.zig              Cron scheduler with JSON persistence\n  health.zig            Component health registry\n  tunnel.zig            Tunnel vtable (cloudflare, ngrok, tailscale, custom)\n  peripherals.zig       Hardware peripheral vtable (serial, Arduino, RPi, Nucleo)\n  runtime.zig           Runtime vtable (native, docker, WASM)\n  skillforge.zig        Skill discovery (GitHub), evaluation, integration\n  ...\n```\n\n## Versioning\n\nnullclaw uses **CalVer** (`YYYY.M.D`) for releases — e.g. `v2026.2.20`.\n\n- **Tag format:** `vYYYY.M.D` (one release per day max; patch suffix `vYYYY.M.D.N` if needed)\n- **Release binaries derive their embedded version from the git tag** (`v...` -> `nullclaw --version`)\n- **Non-release builds default to `dev`** unless you override with `zig build -Dversion=...`\n- **No stability guarantees yet** — the project is pre-1.0, config and CLI may change between releases\n- **`nullclaw --version`** prints the current version\n\n## Contributing\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md) for development environment setup, workflow, validation commands, and the PR checklist.\n\nImplement a vtable interface, submit a PR:\n\n- New `Provider` -> `src\u002Fproviders\u002F`\n- New `Channel` -> `src\u002Fchannels\u002F`\n- New `Tool` -> `src\u002Ftools\u002F`\n- New `Memory` backend -> `src\u002Fmemory\u002F`\n- New `Tunnel` -> `src\u002Ftunnel.zig`\n- New `Sandbox` backend -> `src\u002Fsecurity\u002F`\n- New `Peripheral` -> `src\u002Fperipherals.zig`\n- New `Skill` -> `~\u002F.nullclaw\u002Fworkspace\u002Fskills\u002F\u003Cname>\u002F` or `~\u002F.nullclaw\u002Fworkspace\u002Fskills\u002F\u003Ccategory>\u002F\u003Cname>\u002F`\n\n## Chinese Docs (中文文档)\n\n- [Chinese docs overview (中文文档总览)](docs\u002Fzh\u002FREADME.md)\n- [Installation guide (安装指南)](docs\u002Fzh\u002Finstallation.md)\n- [Zig installation (Zig 安装指南)](docs\u002Fzh\u002Fzig-installation.md)\n- [Configuration guide (配置指南)](docs\u002Fzh\u002Fconfiguration.md)\n- [Usage and operations (使用与运维)](docs\u002Fzh\u002Fusage.md)\n- [Architecture overview (架构总览)](docs\u002Fzh\u002Farchitecture.md)\n- [Security model (安全机制)](docs\u002Fzh\u002Fsecurity.md)\n- [Gateway API (中文)](docs\u002Fzh\u002Fgateway-api.md)\n- [Commands reference (命令参考)](docs\u002Fzh\u002Fcommands.md)\n- [Development guide (开发指南)](docs\u002Fzh\u002Fdevelopment.md)\n\n## English Docs\n\n- [English docs overview](docs\u002Fen\u002FREADME.md)\n- [Installation](docs\u002Fen\u002Finstallation.md)\n- [Zig Installation](docs\u002Fen\u002Fzig-installation.md)\n- [Configuration](docs\u002Fen\u002Fconfiguration.md)\n- [Usage and operations](docs\u002Fen\u002Fusage.md)\n- [Architecture](docs\u002Fen\u002Farchitecture.md)\n- [Security](docs\u002Fen\u002Fsecurity.md)\n- [Gateway API](docs\u002Fen\u002Fgateway-api.md)\n- [Commands](docs\u002Fen\u002Fcommands.md)\n- [Development](docs\u002Fen\u002Fdevelopment.md)\n\n## Disclaimer\n\nnullclaw is a pure open-source software project. It has **no token, no cryptocurrency, no blockchain component, and no financial instrument** of any kind. This project is not affiliated with any token or financial product.\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n\n---\n\n**nullclaw** — Null overhead. Null compromise. Deploy anywhere. Swap anything.\n\n## Star History\n\n[![Star History Chart](https:\u002F\u002Fapi.star-history.com\u002Fsvg?repos=nullclaw\u002Fnullclaw&type=date&legend=top-left)](https:\u002F\u002Fwww.star-history.com\u002F#nullclaw\u002Fnullclaw&type=date&legend=top-left)\n","nullclaw是一个用Zig编写的最小且完全自主的AI助手基础设施。其核心功能包括678 KB的静态二进制文件、小于1 MB的内存占用以及小于2毫秒的启动时间，支持ARM、x86和RISC-V架构。技术特点上，它不依赖运行时环境或虚拟机，具有极高的安全性和可移植性，内置50多个服务提供商、19个通信通道及多种工具和存储引擎。适用于对资源消耗有严格限制但又需要强大AI助手能力的场景，如低成本单板计算机、微控制器以及其他边缘计算设备。",2,"2026-06-11 03:51:10","high_star"]