CodeDocs Vault

NanoClaw Architecture Overview

What NanoClaw Does

NanoClaw is a personal AI assistant that runs Claude in isolated Linux containers and makes it accessible through messaging platforms (WhatsApp, Telegram, Slack, Discord, Gmail). You message your assistant from any chat app; it thinks inside a sandboxed container, and replies.

It solves three problems simultaneously:

  1. Access — Talk to Claude from WhatsApp or Telegram instead of a web UI
  2. Autonomy — The agent can browse the web, run bash, read/write files, and schedule recurring tasks
  3. Security — All of the above happens inside OS-level containers, not on your host machine

Target user: Individual developers/power users who want a personal Claude assistant they fully control and can customize by editing code.

Tech Stack

Layer Technology Why
Language TypeScript (ES2022, strict) Type safety, modern async/await, same language host and container
Runtime Node.js 20+ Single-process, event-loop fits the polling architecture
Database SQLite via better-sqlite3 Zero-config, single-file, synchronous API simplifies state management
AI Runtime @anthropic-ai/claude-agent-sdk Official SDK for running Claude as an autonomous agent with tool use
Container Docker / Apple Container OS-level isolation — agents can run bash safely because they're sandboxed
IPC Protocol @modelcontextprotocol/sdk (MCP) Standard protocol for exposing tools (scheduling, messaging) to the agent
Scheduling cron-parser Parse cron expressions for recurring tasks
Credentials @onecli-sh/sdk Gateway-based secret injection — containers never see real API keys
Validation zod (container only) Runtime type checking for MCP tool arguments

Notable non-dependencies: No Express, no Redis, no message queue, no ORM. The entire host process is ~3500 lines across 15 files with 3 runtime dependencies.

Directory Structure

nanoclaw/
├── src/                          # Host orchestrator (runs on your machine)
│   ├── index.ts                  # Main entry point, message loop, startup
│   ├── config.ts                 # Environment-based configuration
│   ├── db.ts                     # SQLite schema, queries, migrations
│   ├── container-runner.ts       # Spawns containers, parses output streams
│   ├── group-queue.ts            # Per-group concurrency and task queuing
│   ├── router.ts                 # Message formatting and outbound routing
│   ├── ipc.ts                    # Watches IPC directories for agent requests
│   ├── task-scheduler.ts         # Cron/interval/one-time task execution
│   ├── mount-security.ts         # Validates volume mounts against allowlist
│   ├── sender-allowlist.ts       # Per-group message sender filtering
│   ├── container-runtime.ts      # Docker/Apple Container abstraction
│   ├── remote-control.ts         # Remote Claude Code session support
│   ├── logger.ts                 # Structured logging
│   ├── types.ts                  # Shared type definitions
│   └── channels/                 # Channel plugins
│       ├── registry.ts           # Plugin registration (Map-based)
│       └── index.ts              # Barrel import that triggers self-registration
│
├── container/                    # Runs INSIDE the Linux container
│   ├── Dockerfile                # Node 22 + Chromium + agent tools
│   ├── build.sh                  # Container image build script
│   ├── agent-runner/
│   │   └── src/
│   │       ├── index.ts          # Agent lifecycle: stdin→SDK→stdout
│   │       └── ipc-mcp-stdio.ts  # MCP server exposing 8 tools to the agent
│   └── skills/                   # Skills loaded into agent containers
│       ├── agent-browser/        # Web browsing via Chromium
│       ├── capabilities/         # Self-report installed capabilities
│       ├── slack-formatting/     # Slack mrkdwn syntax reference
│       └── status/               # Health check and diagnostics
│
├── groups/                       # Per-group persistent storage
│   ├── main/CLAUDE.md            # Main channel agent instructions
│   └── global/CLAUDE.md          # Shared instructions for all groups
│
├── setup/                        # Interactive setup modules
├── scripts/                      # Migration runner
├── .claude/skills/               # 47+ installable skill definitions
├── config-examples/              # Example configuration files
└── docs/                         # Documentation

System Architecture Diagram

┌─────────────────────────────────────────────────────────────────────┐
│                         HOST PROCESS (Node.js)                      │
│                                                                     │
│  ┌───────────┐   ┌───────────┐   ┌───────────┐   ┌───────────┐    │
│  │ WhatsApp  │   │ Telegram  │   │   Slack   │   │  Discord  │    │
│  │  Channel  │   │  Channel  │   │  Channel  │   │  Channel  │    │
│  └─────┬─────┘   └─────┬─────┘   └─────┬─────┘   └─────┬─────┘    │
│        │               │               │               │           │
│        └───────────────┼───────────────┼───────────────┘           │
│                        ▼                                            │
│              ┌──────────────────┐                                   │
│              │  Channel Registry │  (self-registration at startup)  │
│              └────────┬─────────┘                                   │
│                       ▼                                             │
│  ┌─────────────────────────────────────────────────────────┐       │
│  │                    src/index.ts                          │       │
│  │  ┌──────────┐  ┌──────────┐  ┌───────────┐             │       │
│  │  │ Message  │  │  State   │  │  Session   │             │       │
│  │  │  Loop    │──│  (SQLite)│──│  Manager   │             │       │
│  │  └────┬─────┘  └──────────┘  └───────────┘             │       │
│  └───────┼─────────────────────────────────────────────────┘       │
│          │                                                          │
│          ▼                                                          │
│  ┌──────────────┐    ┌────────────────┐    ┌──────────────────┐    │
│  │  GroupQueue   │    │ Task Scheduler │    │   IPC Watcher    │    │
│  │  (per-group   │    │ (cron/interval │    │ (polls IPC dirs  │    │
│  │   concurrency)│    │  /one-time)    │    │  for agent cmds) │    │
│  └──────┬───────┘    └───────┬────────┘    └────────┬─────────┘    │
│         │                    │                      │              │
│         └────────────────────┼──────────────────────┘              │
│                              ▼                                      │
│                   ┌─────────────────────┐                          │
│                   │  Container Runner   │                          │
│                   │  (spawn, stream,    │                          │
│                   │   timeout, logging) │                          │
│                   └─────────┬───────────┘                          │
│                             │                                      │
└─────────────────────────────┼──────────────────────────────────────┘
                              │  stdin (JSON) / stdout (markers)
                              │  + volume mounts + IPC files
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    LINUX CONTAINER (Docker/Apple)                    │
│                                                                     │
│  ┌──────────────────────────────────────────────────────────┐      │
│  │                   agent-runner/index.ts                   │      │
│  │                                                          │      │
│  │  stdin ──► Parse JSON ──► Claude Agent SDK query() ──►   │      │
│  │            ▲                      │                       │      │
│  │            │              ┌───────┼────────┐              │      │
│  │     IPC input/            │       │        │              │      │
│  │     (follow-up    ┌──────┴──┐ ┌──┴───┐ ┌──┴────┐        │      │
│  │      messages)    │  Bash   │ │ Read │ │ Write │ ...     │      │
│  │                   └─────────┘ └──────┘ └───────┘         │      │
│  │                                                          │      │
│  │  ┌──────────────────────────────────┐                    │      │
│  │  │   MCP Server (ipc-mcp-stdio.ts)  │                    │      │
│  │  │  ┌──────────────┬────────────┐   │                    │      │
│  │  │  │ send_message │ schedule_  │   │                    │      │
│  │  │  │              │ task       │   │                    │      │
│  │  │  ├──────────────┼────────────┤   │                    │      │
│  │  │  │ list_tasks   │ pause_task │   │                    │      │
│  │  │  ├──────────────┼────────────┤   │                    │      │
│  │  │  │ resume_task  │cancel_task │   │                    │      │
│  │  │  ├──────────────┼────────────┤   │                    │      │
│  │  │  │ update_task  │register_   │   │                    │      │
│  │  │  │              │group       │   │                    │      │
│  │  │  └──────────────┴────────────┘   │                    │      │
│  │  └──────────────────────────────────┘                    │      │
│  │           │                                              │      │
│  │           ▼  writes JSON files                           │      │
│  │  /workspace/ipc/{messages,tasks}/                        │      │
│  └──────────────────────────────────────────────────────────┘      │
│                                                                     │
│  /workspace/group/    ← group's persistent storage (read-write)    │
│  /workspace/global/   ← shared memory (read-only for non-main)    │
│  /workspace/extra/*   ← additional validated mounts                │
│  /workspace/project/  ← project root (main only, read-only)       │
│  Chromium + agent-browser  ← web automation                        │
└─────────────────────────────────────────────────────────────────────┘

Core Data Flow (Happy Path)

User sends "@Andy what's the weather?"
         │
         ▼
Channel SDK (e.g. baileys) receives message
         │
         ▼
onMessage callback → storeMessage() → SQLite messages table
         │
         ▼
Message loop (2s poll) → getNewMessages() → trigger pattern match
         │
         ▼
GroupQueue.enqueueMessageCheck() or GroupQueue.sendMessage()
         │                                    │
         │  (no active container)             │ (container already running)
         ▼                                    ▼
processGroupMessages()                   Write IPC file to input/
         │                                    │
         ▼                                    ▼
runContainerAgent()                      Agent picks up via drainIpcInput()
  │ spawn container
  │ write JSON to stdin
  │ stream stdout markers
  │
  ▼
Agent SDK query() → Claude thinks → tool calls → result
         │
         ▼
writeOutput() → ---NANOCLAW_OUTPUT_START--- JSON ---NANOCLAW_OUTPUT_END---
         │
         ▼
Host parses marker → onOutput callback → channel.sendMessage()
         │
         ▼
User receives response in chat

Key Design Principles

  1. Single process, no microservices — One Node.js process manages everything on the host
  2. Security through OS isolation — Agents run in containers; bash is safe because it's sandboxed
  3. File-based IPC — Agent communicates with host via JSON files, not network calls
  4. Polling over webhooks — Simple, reliable, no exposed ports needed
  5. Skills over features — New capabilities are added via git branch merges, not configuration
  6. AI-native development — Claude Code handles setup, debugging, and customization