F FourIA GitHub ↗

Concepts

On this page

Moltlazy has two systems that work together: the Config Module (startup-time) and the SDK Module (runtime).

Config Module (startup)

The Config Module runs once at container startup via moltlazy patch. It generates an immutable moltlazy.json file and injects a $include directive into openclaw.json.

flowchart LR
    ENV["Env vars\n(secrets)"]
    FLAGS["Feature flags\n(MOLTLAZY_FEATURE_FLAGS)"]

    ENV --> PATCH["moltlazy patch"]
    FLAGS --> PATCH

    PATCH --> JSON["/home/openclaw/.openclaw/moltlazy.json"]
    PATCH --> INCLUDE["$include injection\ninto openclaw.json"]

    INCLUDE --> GW["OpenClaw loads\nboth files"]
    JSON --> GW

How it works

  1. start-openclaw.sh runs moltlazy patch
  2. moltlazy patch reads env vars and MOLTLAZY_FEATURE_FLAGS JSON
  3. Assembles all mandatory config sections (gateway, session, tools, logging)
  4. Assembles feature-gated sections (agents, channels, skills, memory, plugins)
  5. Writes /home/openclaw/.openclaw/moltlazy.json (regenerated from scratch each startup)
  6. Injects $include: "./moltlazy.json" into openclaw.json if not present
  7. OpenClaw starts, loads both files, merges them natively

Always-on config modules

These modules always run regardless of feature flags:

ModuleConfig sectionPurpose
config/gatewaygateway.*Auth mode, trustedProxies, controlUi
config/sessionsession.dmScopePer-channel-peer session isolation
config/toolstools.toolSearchEnable tool search in “tools” mode
config/logginglogging.*Production logging with redaction

Feature-gated config modules

These modules only run when their corresponding flag is enabled:

ModuleFlagConfig section
agents/(always-on)agents.defaults, agents.entries
config/channelschannelsTelegram, channelsDiscord, channelsSlackchannels.*
config/memoryknowledgeGraphmemory.*, plugins.*
config/pluginsunifiedBillingplugins.*
config/microsoftGraphmicrosoftGraphplugins.entries.microsoft-graph

Secrets are never in moltlazy.json

Tokens, API keys, and bot credentials are excluded from moltlazy.json via an IGNORE_KEYS filter. They come from environment variables that the gateway reads directly.

SDK Module (runtime)

The SDK Module provides a typed RPC client for interacting with a running gateway via the Admin HTTP RPC API at POST /api/v1/admin/rpc. No filesystem writes — pure typed RPC calls.

RPC Client

RpcClient is the low-level HTTP client that handles authentication, retries, and timeouts for all Admin HTTP RPC calls.

sequenceDiagram
    participant App as Your Code
    participant Client as RpcClient
    participant GW as OpenClaw Gateway

    App->>Client: client.config.get()
    Client->>GW: POST /api/v1/admin/rpc<br/>Authorization: Bearer<br/>{"method": "config.get", "id": "moltlazy-..."}
    alt Success
        GW-->>Client: {"id": "...", "ok": true, "payload": {...}}
        Client-->>App: RpcSuccess
    else Error
        GW-->>Client: {"id": "...", "ok": false, "error": {...}}
        Client-->>App: RpcError
    end

Key properties:

  • Auth — Bearer token sent in the Authorization header, never in request body
  • Retry — Automatic retry on HTTP 503 with configurable maxRetries and retryDelay
  • Timeout — Configurable request timeout (default: 10 seconds)
  • Request IDs — Auto-generated with moltlazy- prefix for tracing

Domain modules

createClient() returns a MoltlazyClient that attaches 7 domain modules to the RpcClient:

const client = createClient({ baseUrl: '...', token: '...' });

client.config; // ConfigModule   — config.get, config.set, config.apply
client.agents; // AgentsModule   — agents.list, agents.create, agents.update, agents.delete
client.channels; // ChannelsModule — channels.status, channels.start, channels.stop
client.devices; // DevicesModule  — device.pair.list, device.pair.approve, device.pair.reject, device.pair.remove
client.cron; // CronModule     — cron.list, cron.get, cron.add, cron.update, cron.remove, cron.run
client.models; // ModelsModule   — models.list, models.authStatus
client.gateway; // GatewayModule  — health, status, gateway.restart.request, logs.tail

Each module method maps 1:1 to an Admin HTTP RPC method. Modules are thin wrappers that:

  1. Build the params object from typed arguments
  2. Call client.call(method, params)
  3. Return the typed RpcResponse<T>

Config methods

The config module exposes three methods matching the upstream protocol contract:

MethodUpstream RPCParams
config.get()config.getnone
config.set()config.set{ raw: string, baseHash?: string }
config.apply()config.apply{ raw: string, baseHash?: string }

raw is a serialised JSON string of the config fragment to apply. baseHash is an optional optimistic concurrency token.

Response shape

Every call returns RpcResponse<T>, a discriminated union:

type RpcResponse<T> =
  | { ok: true; id: string; payload: T }
  | {
      ok: false;
      id: string;
      error: {
        code: number;
        message: string;
        details?: unknown;
        retryable?: boolean;
        retryAfterMs?: number;
      };
    };

Check result.ok before accessing result.payload.