Architecture

Architecture

LordClaw is a Next.js 16 application that communicates with an OpenClaw gateway over WebSocket.

Core Principles

  • Local-first — all data stays on your machine or private network
  • Real-time — WebSocket connection for live updates
  • Zero telemetry — no analytics, tracking, or external calls

System Overview

┌─────────────────┐     WebSocket      ┌──────────────────┐
│    LordClaw     │ ◄──────────────► │  OpenClaw Gateway  │
│   (Next.js 16)   │   ws://localhost  │   (Node.js)        │
└────────┬────────┘    :18789          └──────────────────┘
         │                                      │
         ▼                                      ▼
  ┌──────────────┐                    ┌──────────────────┐
  │  PostgreSQL   │                    │     Agents       │
  │  (port 5555)  │                    │  (autonomous)    │
  └──────────────┘                    └──────────────────┘

Frontend Stack

LayerTechnology
FrameworkNext.js 16 with TypeScript (strict, no any)
UI Componentsshadcn/ui + Tailwind CSS
ChartsRecharts
Drag & Drop@dnd-kit/core + @dnd-kit/sortable
StateReact hooks + context
ThemesCSS variables with light/dark/system modes

Gateway Communication

All real-time data flows through a single GatewayClient class (src/lib/gateway/client.ts):

  • Protocol: WebSocket with v3 handshake (responds to connect.challenge)
  • Client ID: gateway-client
  • Authentication: Optional token-based auth
  • Reconnection: Automatic with exponential backoff

The client exposes a request/response API for commands and an event system for real-time updates:

// Request-response
const status = await client.request("status");
const history = await client.request("chat.history", { sessionKey, limit: 50 });
 
// Event listening
client.on("agent", handleAgentEvent);
client.on("chat", handleChatEvent);

The client uses Math.random().toString(36) instead of crypto.randomUUID() for ID generation, since the dashboard may run in a non-secure context (HTTP, not HTTPS) during local development.

Data Storage

PostgreSQL (via Prisma)

Usage data is stored in a local PostgreSQL database:

  • Token snapshots — per-minute records of token consumption per agent/model
  • Hourly aggregates — rolled up from minute-level data for long-range queries
  • Chat messages — persisted for offline access and pagination
  • Chat drafts — auto-saved in-progress messages
  • User preferences — custom agent ordering, etc.

Connection: postgresql://LordClaw_user:LordClaw_password@localhost:5555/LordClaw

localStorage

Client-side preferences stored in the browser:

  • Widget layout and order
  • Sidebar width and navigation order
  • Theme preference
  • Gateway URL and token
  • Editor settings (font size, line wrap)
  • Workspace tabs and starred items
  • Onboarding completion state

API Routes

Server-side API routes handle database operations and external service calls:

RoutePurpose
/api/usage/*Token usage CRUD, plan usage (Claude, MiniMax, ZAI)
/api/messages/*Chat message persistence, sync, drafts
/api/workspaceFile listing, reading, writing
/api/agents/*Agent photos, transcripts, workspace browsing
/api/preferencesUser preference key-value store
/api/pricingModel pricing data
/api/eventsServer-sent events (alternative to WS)

Usage Collection Flow

Browser (every 10s)

    ├── Poll gateway for session token counts
    ├── Compute deltas since last poll
    └── POST /api/usage/snapshot

            ├── Insert per-minute record
            └── Aggregate into per-hour record