OpenClaw Architecture Overview
This is the top-level map. All other architecture documents branch from here.
Based on OpenClaw version 2026.2.23 (commit aceb17a).
High-level architecture
Message flow (inbound → outbound)
A message from Telegram to a reply, step by step:
Module index
Each module has its own detailed document. Status indicates writing progress.
Cross-module patterns
Recurring design patterns that span multiple modules. See patterns index.
Deep dives
Code-level walkthroughs of high-impact subsystems. See deep-dives index.
Key architectural decisions (verified from source)
-
Single process: The Gateway (
src/gateway/server.impl.ts) runs everything in one Node.js process — WebSocket server, HTTP API, channel plugins, agent runtime, cron. Default port18789, with Canvas Host onport+1and Browser Control onport+2. -
Local-first persistence: Sessions as append-only JSONL files (
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl). Memory as SQLite with vector extensions (memory.db). Config as JSON5 with env var substitution (openclaw.json). No cloud dependency. -
BYOM (Bring Your Own Model): The Pi Agent SDK (
@mariozechner/pi-agent-core) abstracts the LLM provider. Supports Anthropic, OpenAI, Google Gemini, xAI Grok, DeepSeek, Mistral, Ollama, and more. Model selection per agent, per session, or per turn. -
Markdown-as-Config: Skills are
SKILL.mdfiles with YAML frontmatter. Memory notes are Markdown. Agent workspace files are Markdown. All human-readable, Git-friendly, and natively parseable by LLMs. -
Channel-agnostic plugin architecture: Each messaging platform is an extension package (
extensions/*/) that implements theChannelPlugininterface fromsrc/channels/plugins/types.plugin.ts. 38 extensions currently, each with ~15 adapter types (config, security, outbound, status, gateway, threading, etc.). -
Monorepo with pnpm workspaces: Root package +
ui/+extensions/*as workspace members. Build viatsdown. TypeScript 5.9, Node.js 22+, strict mode.
Source directory structure
Config structure (openclaw.json)
Verified from src/config/types.openclaw.ts and Zod schema:
My blind spots
Honest record of what I don't yet understand. Updated as knowledge grows.
- How does the tool policy pipeline compose its filtering layers? — Resolved: 7-step cascading allowlist pipeline, each step progressively filters tools. No explicit deny; tools not in allowlist are excluded. See Tool Policy.
- The compaction flow — how does it decide what to summarize vs keep? — Resolved:
BASE_CHUNK_RATIO=0.4adaptive chunking,splitMessagesByTokenShare()distributes messages across N chunks targeting equal token share, oldest chunks dropped first,repairToolUseResultPairing()fixes orphaned tool_results after pruning. See Pi Agent Runtime and Compaction Internals. - Memory hybrid search ranking — Resolved:
score = vectorWeight * vectorScore + textWeight * textScore, with optional MMR re-ranking (lambda=0.7: 70% relevance, 30% diversity penalty via Jaccard similarity). See Memory (RAG). - Device pairing protocol — Resolved: Ed25519 challenge-response, 8-char alphanumeric pairing code (no ambiguous 0O1I), 60-min TTL, max 3 pending, SHA-256 device ID derived from public key. See Native Apps and Ed25519 Device Auth.
- Extension hot-reload — Resolved: Extensions do NOT hot-reload (require gateway restart). Only config-level hooks and cron support hot-reload via
chokidarfile watcher with 300ms debounce. Seesrc/gateway/config-reload.ts. - Subagent registry — Resolved: No explicit depth limit. Tracks all levels via
rootSessionKeyrecursive queries inlistDescendantRunsForRequester(). Orphan detection viaresolveSubagentRunOrphanReason(), announce retry max 3 times with 5-min expiry. Seesrc/agents/subagent-registry.ts(1,081 lines). - WebSocket reconnection strategy — Resolved: Server does not initiate reconnects (stateless accept). Web UI (
ui/src/ui/gateway.ts) uses exponential backoff: initial 800ms, multiplier 1.7x, max 15s. iOS app retries on foreground resume only (attemptAutoReconnectIfNeeded()). See WebSocket Disconnects. - Config hot-reload failure handling — Resolved: On reload failure, system logs the error and skips the update; old config stays in memory. No rollback mechanism. Missing file retries 2x with 150ms delay then skips. Invalid config logs validation errors and skips. See
src/gateway/config-reload.ts:370-388. - JSONL transcript corruption recovery — Resolved: Two-layer recovery. File-level:
repairSessionFileIfNeeded()(src/agents/session-file-repair.ts) drops unparseable JSON lines and creates.bak-{pid}-{timestamp}backups. Transcript-level:repairToolUseResultPairing()+repairToolCallInputs()+sanitizeToolCallInputs()fix orphaned/duplicate tool results. See Session Transcript Corruption. - Memory index cache eviction —
maxEntriesconfig exists insrc/memory/manager.tsbut NO pruning/eviction is implemented.INDEX_CACHEis an unboundedMapwith no LRU, size check, or TTL. Appears to be a missing feature. - Multi-agent session key collision — Session keys are deterministic (
agent:{agentId}:{mainKey}insrc/routing/session-key.ts). No UUID generation, collision detection, or locking. System relies on external uniqueness (distinct agent IDs + peer IDs per channel) rather than built-in safeguards.