OpenClaw Architecture Overview

OpenClaw Architecture Overview

OpenClaw is a self-hosted AI agent platform — an operating system for AI agents. It wraps the pi-agent-core runtime inside a central Gateway process and exposes agents to humans and other systems via pluggable channel adapters.

System Shape

graph LR
    subgraph Adapters["Channel Adapters"]
        WA[WhatsApp]
        TG[Telegram]
        DC[Discord]
        SL[Slack]
        IM[iMessage]
    end

    subgraph Control["Control Interfaces"]
        WEB[Web UI]
        CLI[CLI]
        APP[macOS App]
        MOB[Mobile]
    end

    subgraph GW["Gateway (127.0.0.1:18789)"]
        direction TB
        ROUTER[Session Router]
        CRON[Cron Scheduler]
        HOOKS[Webhook Endpoints]
        TASKS[Task Ledger]
    end

    subgraph RT["Agent Runtime"]
        CTX[Context Assembly]
        LLM[Model Invocation]
        TOOLS[Tool Execution]
    end

    Adapters --> GW
    Control --> GW
    GW --> RT
    RT --> GW
    GW --> Adapters

The Gateway is the single choke point for all traffic. Adapters and control interfaces never speak to the runtime directly.


Critical Components

1. Gateway (Control Plane)

Node.js 22+ process binding to 127.0.0.1:18789 by default.

Responsibilities:

The loopback bind is the first security layer — no external network access without an explicit tunnel (SSH or Tailscale).

2. Channel Adapters

Platform-specific connectors plugged into the Gateway's hub:

Supported: WhatsApp, Telegram, Discord, Slack, iMessage, and others.

3. Session Manager

The Gateway owns all session state. Each session maps to:

Session assignment is determined by message origin (DM → main, group/room → isolated, cron → fresh per run). The session.dmScope config controls isolation granularity for DMs (from shared main to per-channel-peer).

Sessions expire via daily reset (4 AM), idle timeout, or manual /reset.

4. Agent Runtime (pi-agent-core)

The inner execution engine wrapping pi-agent-core. Runs serialized within a session lane — one active turn per session at a time.

Six-phase execution per message:

sequenceDiagram
    participant A as Channel Adapter
    participant G as Gateway
    participant S as Session Manager
    participant R as Agent Runtime
    participant L as LLM API

    A->>G: Inbound message (Phase 1: Ingestion)
    G->>G: Auth check + allowlist (Phase 2: Access Control)
    G->>S: Assign session lane
    S-->>G: Session context (.jsonl transcript)
    G->>R: Dispatch to runtime

    rect rgb(40, 60, 80)
        note over R: Phase 3 – Context Assembly
        R->>R: Load SOUL.md / AGENTS.md / TOOLS.md
        R->>R: Hybrid memory search
        R->>R: Inject session history
    end

    R->>L: Assembled context (Phase 4: Model Invocation)
    L-->>R: Response + tool calls

    rect rgb(40, 80, 60)
        note over R: Phase 5 – Tool Execution
        R->>R: Dispatch tool calls
        R->>L: Tool results → follow-up if needed
        L-->>R: Final response
    end

    R->>G: Completed turn output
    G->>S: Append to .jsonl transcript
    G->>A: Formatted reply (Phase 6: Response Delivery)

The Gateway owns phases 1–2 and 6. The Agent Runtime owns phases 3–5.

5. Context Assembly

Phase 3 — builds the full context window before each model call:

graph TD
    MSG[Current Message]

    subgraph SYS["System Prompt"]
        SOUL[SOUL.md
persona / values / guidelines] AGENTS[AGENTS.md
sub-agents + Standing Orders] TOOLS[TOOLS.md
tool definitions] end subgraph MEM["Memory"] HYBRID[Hybrid Search
vector + keyword] end subgraph HIST["Session History"] JSONL[.jsonl transcript
recent turns] end SYS --> CTX[Context Window] MEM --> CTX HIST --> CTX MSG --> CTX CTX --> LLM[Model Invocation]

6. Hook System

Two layers of extension points:

Type Registration Scope
Internal gateway hooks Shell scripts, auto-discovered Session lifecycle (/new, /reset, /stop), bootstrap
Plugin hooks Plugin API Full pipeline: model resolve → prompt build → tool call → compaction → message in/out

Key plugin hook order: before_model_resolveagent:bootstrapbefore_prompt_buildbefore_agent_replybefore_tool_call / after_tool_calltool_result_persistagent_end.

Block/cancel semantics are terminal: a block: true on before_tool_call stops all lower-priority handlers.

7. Memory & Compaction

8. Automation Subsystems

All hosted inside the Gateway process:

Mechanism Timing model Record created?
Cron Precise: --at, --every, --cron Yes — Background Task
Heartbeat Fuzzy: default 30-min interval No
Background Tasks Passive ledger (no scheduling)
Task Flow Multi-step workflow orchestration Yes
Hooks Lifecycle event-driven No
Standing Orders Injected every session via context assembly No
graph TD
    CRON["Cron
precise schedule"] HB["Heartbeat
fuzzy 30-min tick"] HOOKS["Hooks
lifecycle events"] SO["Standing Orders
every session"] CRON -->|"--session main → enqueues into"| HB CRON -->|"--wake → triggers early"| HB CRON -->|"--session isolated"| TURN HB --> TURN["Agent Turn"] HOOKS --> TURN SO -->|"injected via context assembly"| TURN TURN -->|"spawns detached work"| BG["Background Tasks
audit ledger"] TURN -->|"multi-step work"| TF["Task Flow
orchestration"]

9. Security Model (Layered)

Outer → inner:

graph TD
    NET["① Network Isolation
loopback bind · SSH tunnel / Tailscale for remote"] AUTH["② Authentication
token / password for all clients"] PAIR["③ Device Pairing
explicit pairing step for new nodes"] ALLOW["④ Channel Allowlists
unknown senders rejected at Gateway"] SAND["⑤ Tool Sandboxing
Docker containers for untrusted sessions"] INJ["⑥ Prompt Injection Defenses
untrusted content isolated from system prompt"] NET --> AUTH --> PAIR --> ALLOW --> SAND --> INJ

10. Plugin System

The Gateway exposes extension points for:

Skills are loaded as a snapshot and injected into the environment and system prompt.


Deployment Topologies

Mode How
Local dev Gateway on localhost; control via CLI/Web UI
macOS background LaunchAgent keeps Gateway alive across reboots
Linux/VPS SSH tunnel or Tailscale exposes the loopback port securely
Cloud container Fly.io (or similar); same loopback isolation inside the container
graph LR
    subgraph LOCAL["Local Dev"]
        L_GW["Gateway
127.0.0.1:18789"] L_CLI["CLI / Web UI"] L_CLI --> L_GW end subgraph MACOS["macOS Background"] M_LA["LaunchAgent"] M_GW["Gateway"] M_LA -->|"keeps alive"| M_GW end subgraph VPS["Linux / VPS"] V_SSH["SSH Tunnel
or Tailscale"] V_GW["Gateway
127.0.0.1:18789"] V_SSH --> V_GW end subgraph CLOUD["Cloud Container (Fly.io)"] C_GW["Gateway
loopback inside container"] C_PROXY["Reverse Proxy / Fly proxy"] C_PROXY --> C_GW end ADAPTERS["Channel Adapters
(Slack, Telegram…)"] ADAPTERS --> V_SSH ADAPTERS --> C_PROXY ADAPTERS --> L_GW ADAPTERS --> M_GW

Key References