OpenClaw Deep Dive

Full System Architecture & Code Walkthrough

What is OpenClaw?

MetricValue
GitHub Stars186,000
Contributors510
Age81 days
LicenseMIT

Origin Story

Nov 2025 "Clawdbot" open-sourced by Anthropic internal team
Jan 12, 2026 Reaches 3.2K stars, growing steadily
Jan 27 Anthropic trademark issue → renamed to "Moltbot"
Jan 28 Moltbook platform launches (macOS/iOS/Android)
Jan 29-30 Viral explosion: 34K stars in 48 hours
Jan 30 Community forks and renames to "OpenClaw"
Feb 12, 2026 186,000 stars — fastest-growing repo in GitHub history

Why It Blew Up

"This is genuinely the most incredible sci-fi takeoff-adjacent thing I've seen in my career"
— Andrej Karpathy

By the Numbers

186K
GitHub Stars
510
Contributors
9,358
Commits
913K
Lines of Code
48
Releases
17+
Channels
52
Skills
35
Extensions

High-Level Architecture

┌─────────────────────────────────────────────────────────┐
│  Channels (WhatsApp, Discord, Slack, Telegram, etc.)   │
└─────────────────────┬───────────────────────────────────┘
                      │
                      ▼
            ┌─────────────────┐
            │  Gateway :18789 │  ← WebSocket + HTTP
            └────────┬─────────┘
                     │
                     ▼
            ┌─────────────────┐
            │   Agent Core    │  ← Pi Agent, routing, sessions
            └────────┬─────────┘
                     │
                     ▼
        ┌────────────────────────┐
        │  Tools & LLM Providers │  ← Claude, GPT, Gemini
        └────────────────────────┘
  
OpenClaw (Gateway + Channels)
Pi Agent Core (Skills + Tools)
LLM APIs (Claude, GPT, Gemini)

Tech Stack

LayerTechnology
RuntimeNode.js 22
LanguageTypeScript
Build Systempnpm + tsdown
AI FrameworkPi Agent Core
WhatsAppBaileys (no official API)
TelegramgrammY
Discorddiscord.js
Slack@slack/bolt
BrowserPlaywright
DatabaseSQLite + sqlite-vec
ValidationZod 4 + TypeBox
TestingVitest 4
Lintingoxlint + oxfmt

Monorepo Structure

DirectoryPurposeSize
src/Core TypeScript codebase480K LOC
extensions/35 plugin modules86K LOC
apps/macos/Swift menu bar app12K LOC
apps/ios/Swift iOS client8K LOC
apps/android/Kotlin Android client11K LOC
ui/Lit web components24K LOC
skills/52 SKILL.md definitions—
scripts/~70 build/dev scripts—
docs/Mintlify documentation—

The Gateway

Responsibilities

  • Channel lifecycle management
  • Agent request routing
  • Session state tracking
  • Cron job scheduling
  • Plugin loading & hooks
  • mDNS discovery broadcast
export async function startGatewayServer(
  port: number,
  config: GatewayConfig
): Promise<GatewayServer> {
  // WebSocket + HTTP server
  // Channel managers
  // Agent dispatcher
}

Gateway: Server Startup

export async function startGatewayServer(port: number, config: GatewayConfig) {
  const httpServer = http.createServer(handleRequest);
  const wss = new WebSocketServer({ server: httpServer });

  wss.on('connection', async (ws, req) => {
    const session = await createSession(ws, req);
    attachFrameHandlers(session);
  });

  httpServer.listen(port);
  await startChannels(config.channels);
  await startDiscovery(port);

  return { httpServer, wss, config };
}
  

src/gateway/server.ts

WebSocket Protocol Frames

const RequestFrame = Type.Object({
  type: Type.Literal('request'),
  id: Type.String(),
  method: Type.String(),
  params: Type.Optional(Type.Any())
});

const ResponseFrame = Type.Object({
  type: Type.Literal('response'),
  id: Type.String(),
  result?: Type.Any(),
  error?: Type.String()
});

const EventFrame = Type.Object({
  type: Type.Literal('event'),
  event: Type.String(),
  data: Type.Any()
});
  

src/gateway/protocol.ts

Connect Handshake

ConnectParams

  • protocol: version negotiation
  • client: app name/version
  • deviceId: Ed25519 identity
  • auth: API key (optional)

HelloOk Response

  • protocol: "openclaw/1.0"
  • server: version info
  • features: capability flags
  • snapshot: current state
  • policy: auth requirements
Ed25519 Device Identity: Each client generates a keypair. The public key becomes the deviceId, used for message signing and session resumption.

RPC Methods & Authorization

const READ_METHODS = new Set([
  'getChannels', 'getConversations', 'getMessages', 'getConfig'
]);

const WRITE_METHODS = new Set([
  'sendMessage', 'updateConfig', 'startChannel', 'stopChannel'
]);

function authorizeGatewayMethod(method: string, session: Session) {
  if (WRITE_METHODS.has(method) && !session.isAuthenticated) {
    throw new Error('Write methods require authentication');
  }
  return true;
}
  

src/gateway/auth.ts — 50+ RPC methods total

Gateway HTTP Layer

async function handleRequest(req: IncomingMessage, res: ServerResponse) {
  // Execute plugin hooks
  const hookResult = await runHttpHooks(req);
  if (hookResult) return sendResponse(res, hookResult);

  // Tool HTTP endpoints
  if (req.url?.startsWith('/tools/')) return handleToolRequest(req, res);

  // OpenAI-compatible API
  if (req.url === '/v1/chat/completions') return handleChatCompletion(req, res);

  // Control UI
  if (req.url === '/') return serveControlUI(res);

  res.writeHead(404); res.end();
}
  

src/gateway/http.ts

Channel Manager

async function startChannel(channelId: string, config: ChannelConfig) {
  const abortController = new AbortController();
  const adapter = await createAdapter(config.type, config);

  adapter.on('message', async (msg) => {
    const conversation = await getOrCreateConversation(channelId, msg.from);
    await dispatchToAgent(conversation, msg);
  });

  await adapter.connect(abortController.signal);
  channelRegistry.set(channelId, { adapter, abortController });

  broadcastEvent('channelStarted', { channelId });
}
  

src/gateway/channels.ts

Discovery & mDNS

Zero-Config Pairing: Native apps (macOS/iOS/Android) automatically discover local gateway instances on the same network. No manual IP entry required.

Channel Abstraction

16 interfaces for one unified messaging abstraction

Outbound Adapter Pattern

const telegramOutbound: OutboundAdapter = {
  async sendText(conversationId, text, options) {
    const chunks = chunkMessage(text, 4096);
    for (const chunk of chunks) {
      await bot.api.sendMessage(conversationId, chunk, {
        parse_mode: 'Markdown',
        disable_web_page_preview: options?.noPreview
      });
    }
  },

  async sendMedia(conversationId, attachment) {
    const file = new InputFile(attachment.data);
    await bot.api.sendDocument(conversationId, file);
  }
};
  

src/channels/telegram/outbound.ts

Discord & WhatsApp

Discord

// No chunking needed
chunker: null,
maxLength: 2000,

async sendText(channelId, text) {
  const channel = await
    client.channels.fetch(channelId);
  await channel.send(text);
}
      

src/channels/discord/outbound.ts

WhatsApp

  • Baileys library (no official API)
  • QR code pairing flow
  • JID normalization (@s.whatsapp.net)
  • 4000 char message limit
  • Media via uploadMedia
  • Most complex adapter (~8K LOC)

Plugin System

async function loadOpenClawPlugins(pluginDir: string) {
  const manifests = await glob('**/openclaw.plugin.json', { cwd: pluginDir });

  for (const manifestPath of manifests) {
    const manifest = await readJSON(manifestPath);
    const plugin = await import(manifest.entry);
    await plugin.initialize(gatewayContext);
    pluginRegistry.set(manifest.id, plugin);
  }
}
  
{
  "id": "spotify-player",
  "version": "1.2.0",
  "entry": "./dist/index.js",
  "hooks": ["beforeMessage", "afterMessage"],
  "capabilities": ["audio-playback"]
}