# Concepts

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`.

```mermaid
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:

| Module           | Config section     | Purpose                              |
| ---------------- | ------------------ | ------------------------------------ |
| `config/gateway` | `gateway.*`        | Auth mode, trustedProxies, controlUi |
| `config/session` | `session.dmScope`  | Per-channel-peer session isolation   |
| `config/tools`   | `tools.toolSearch` | Enable tool search in "tools" mode   |
| `config/logging` | `logging.*`        | Production logging with redaction    |

### Feature-gated config modules

These modules only run when their corresponding flag is enabled:

| Module                  | Flag                                                   | Config section                      |
| ----------------------- | ------------------------------------------------------ | ----------------------------------- |
| `agents/`               | (always-on)                                            | `agents.defaults`, `agents.entries` |
| `config/channels`       | `channelsTelegram`, `channelsDiscord`, `channelsSlack` | `channels.*`                        |
| `config/memory`         | `knowledgeGraph`                                       | `memory.*`, `plugins.*`             |
| `config/plugins`        | `unifiedBilling`                                       | `plugins.*`                         |
| `config/microsoftGraph` | `microsoftGraph`                                       | `plugins.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.

```mermaid
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`:

```ts
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:

| Method           | Upstream RPC   | Params                               |
| ---------------- | -------------- | ------------------------------------ |
| `config.get()`   | `config.get`   | none                                 |
| `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:

```ts
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`.
