Blog

  • First Month With OpenClaw: What Surprised Me Most (Honest Review)

    If you’ve just started running OpenClaw on a Hetzner VPS, or any other cloud provider, and you’re seeing unexpected resource spikes or even crashes, especially during off-peak hours, you’re not alone. I encountered this frequently in my first month, leading to a lot of head-scratching. The problem often isn’t the OpenClaw core itself, but how underlying dependencies and resource management interact with the burstable nature of most cloud VMs. Specifically, I found that the default log rotation and temporary file cleanup mechanisms on my Ubuntu 22.04 LTS Hetzner instance were clashing with OpenClaw’s internal state management, leading to temporary file system exhaustion and subsequent application failure.

    Affiliate Disclosure: As an Amazon Associate, we earn from qualifying purchases. This means we may earn a small commission when you click our links and make a purchase on Amazon. This comes at no extra cost to you and helps support our site.

    Understanding the “Phantom” Resource Spikes

    My initial deployment was a standard OpenClaw setup: a small Python script invoking the OpenClaw API for daily summarization tasks, running within a Docker container on a CX21 (2 vCPU, 4GB RAM) Hetzner VPS. For the first few days, everything seemed fine. Then, I started noticing that my monitoring alerts for disk I/O and CPU utilization would spike erratically, usually between 2 AM and 4 AM UTC, often coinciding with OpenClaw failing to complete its tasks or, worse, the entire Docker daemon restarting. The docker logs openclaw-container command would often show truncated output or outright connection errors to the LLM provider, followed by messages like OSError: [Errno 28] No space left on device, even though df -h / showed plenty of free space.

    The non-obvious insight here was the interaction between logrotate and OpenClaw’s default logging level. By default, OpenClaw can be quite verbose, especially during model inference or when encountering API errors. This verbosity, when combined with Docker’s default logging driver (usually json-file) and the host system’s logrotate, created a specific scenario. When logrotate would compress or move large Docker log files, it would briefly consume significant I/O and CPU, sometimes bottlenecking the already busy OpenClaw container trying to write its own logs or temporary inference data. The “no space left” error wasn’t about persistent disk space, but rather about temporary inode exhaustion or transient buffer space within the kernel during high I/O operations.

    The Fix: Taming Logs and Temporary Files

    To mitigate this, I made two critical adjustments. First, I configured Docker’s logging to be more conservative for the OpenClaw container. This prevents Docker itself from generating massive log files that need frequent rotation and compression. Add this to your docker-compose.yml for the OpenClaw service:

    
    services:
      openclaw:
        image: openclaw/openclaw:latest
        # ... other configurations ...
        logging:
          driver: "json-file"
          options:
            max-size: "10m"
            max-file: "3"
    

    This limits the Docker logs for the openclaw service to a maximum of 10MB per file, keeping only the three most recent files. This dramatically reduced the strain on the host system during log rotation.

    Second, and more importantly, I realized OpenClaw’s default tmp_dir was often pointing to the system’s default /tmp. While fine for short-lived data, some LLM providers (especially local ones or those with complex streaming responses) can buffer significant data there. If /tmp is on a small, fast filesystem or a memory-backed filesystem (like tmpfs on some distributions), it can hit its limits quickly. The solution was to explicitly define a dedicated temporary directory within OpenClaw’s configuration and ensure it was mounted as a volume in Docker, pointing to a persistent disk location.

    Add this to your .openclaw/config.json:

    
    {
      "api_key": "your-api-key",
      "default_model": "claude-haiku-4-5",
      "tmp_dir": "/app/openclaw_tmp",
      "logging": {
        "level": "INFO",
        "filename": "/var/log/openclaw.log",
        "max_bytes": 10485760,
        "backup_count": 5
      }
    }
    

    Then, ensure this directory is mounted as a volume in your docker-compose.yml:

    
    services:
      openclaw:
        image: openclaw/openclaw:latest
        volumes:
          - ./data/openclaw_tmp:/app/openclaw_tmp
          - ./data/logs:/var/log
          - ./config.json:/app/.openclaw/config.json:ro # Assuming config.json is in your project root
        # ... rest of the service config ...
    

    This ensures /app/openclaw_tmp inside the container maps to ./data/openclaw_tmp on your host, preventing temporary data from exhausting critical system partitions. Similarly, the /var/log mount ensures OpenClaw’s own logs are also managed externally.

    Model Choice and Cost Efficiency

    Another significant surprise was the default model recommendation. While the documentation often suggests the latest frontier models for maximum capability, I quickly found that for 90% of my production tasks – summarizing articles, extracting key entities, or generating short, factual responses – a cheaper, faster model was perfectly adequate. My initial setup used claude-opus-20240229. While incredibly powerful, its latency and cost quickly became prohibitive for high-volume tasks. Switching to claude-haiku-4-5 reduced my API costs by approximately 10x for the same workload and significantly decreased overall inference time without any noticeable degradation in result quality for my specific use cases. Always benchmark with your actual data and desired output quality before committing to the most expensive model.

    Limitations and Resource Requirements

    It’s crucial to be honest about the limitations. These optimizations primarily address I/O and temporary file management issues. They assume your VPS has sufficient base resources for OpenClaw to operate. This setup, for instance, works well on a Hetzner CX21 (2 vCPU, 4GB RAM) for moderate workloads (e.g., 50-100 summarization tasks per hour). If you’re attempting to run OpenClaw on something like a Raspberry Pi 4 with only 1GB or 2GB of RAM, especially if you’re pulling in larger models or running other services, you will still struggle. The overhead of the Docker daemon itself, the Python runtime, and the memory footprint of even smaller LLM interactions means that a minimum of 2GB RAM is generally required for a stable OpenClaw deployment, with 4GB being much more comfortable for any bursty usage. Attempting to run local LLMs on such low-spec hardware is a non-starter.

    The insights here are specifically for cloud VPS environments where you have root access and can configure Docker and OpenClaw’s underlying file system interactions. If you’re running OpenClaw in a more restricted environment, like a shared hosting platform or a serverless function, some of these solutions might not be directly applicable, and you’d need to consult your provider’s specific guidelines for temporary file handling and resource limits.

    Your next concrete step: modify your docker-compose.yml to include the logging options and the volume mounts for openclaw_tmp and logs as specified, then run docker-compose up -d --build to apply the changes.

  • OpenClaw as a Home Server Assistant: Monitoring, Alerts, and Maintenance

    If you’re looking to turn your OpenClaw instance into a reliable home server assistant, handling monitoring, alerts, and automated maintenance, you’ve likely hit a few snags. The default OpenClaw setup is powerful for creative tasks, but a bit too chatty and resource-intensive for the background hum of a server assistant. We’re aiming for quiet, efficient operation, waking up only when something needs attention. Forget the verbose responses; we want concise, actionable information.

    Affiliate Disclosure: As an Amazon Associate, we earn from qualifying purchases. This means we may earn a small commission when you click our links and make a purchase on Amazon. This comes at no extra cost to you and helps support our site.

    Optimizing OpenClaw for Low-Resource Monitoring

    The first step is to optimize OpenClaw itself. Running a full language model for every system check is overkill. For monitoring tasks, we often need pattern matching, simple comparisons, and the ability to summarize logs. The default claude-opus-20240229 is fantastic for complex generation, but it’s a resource hog and expensive. For monitoring, we can be much smarter.

    I’ve found that claude-haiku-20240307 (or even gpt-3.5-turbo-0125 if you’re using OpenAI) is perfectly capable for 90% of monitoring tasks. It’s significantly faster and, crucially, 10x cheaper. Modify your ~/.openclaw/config.json to reflect this. If you don’t have this file, create it. Here’s a snippet:

    {
      "api_key": "YOUR_ANTHROPIC_API_KEY",
      "default_model": "claude-haiku-20240307",
      "max_tokens": 512,
      "temperature": 0.2
    }
    

    Setting max_tokens to 512 prevents verbose responses, forcing the model to be succinct, which is exactly what you want for alerts. A low temperature (0.2) ensures more deterministic and factual output, reducing the chance of creative interpretations of your server’s health. This configuration alone will drastically reduce your API costs and improve response times for monitoring prompts.

    Integrating with System Monitoring: Cron and Custom Scripts

    The core of an effective home server assistant is its ability to interact with your system. OpenClaw isn’t a replacement for Prometheus or Nagios, but it can act as an intelligent layer on top of standard system tools. We’ll use cron jobs to periodically run scripts that collect data, and then feed that data to OpenClaw for interpretation and alerting.

    Let’s say you want to monitor disk usage. Create a script, for example, ~/scripts/check_disk.sh:

    #!/bin/bash
    DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
    if (( DISK_USAGE > 80 )); then
      echo "CRITICAL: Disk usage is at ${DISK_USAGE}%."
      echo "Disk usage for /: $(df -h /)"
      echo "Top 10 largest files/directories in /var: $(du -sh /var/* | sort -rh | head -n 10)"
    else
      echo "Disk usage is normal: ${DISK_USAGE}%."
    fi
    

    Now, we’ll pipe the output of this script directly into OpenClaw. Edit your crontab with crontab -e:

    # Run every hour
    0 * * * * ~/scripts/check_disk.sh | while read line; do
      if echo "$line" | grep -q "CRITICAL"; then
        /usr/local/bin/openclaw "Analyze this server alert and suggest a fix, be concise: $line" > ~/openclaw_alerts/disk_alert_$(date +\%Y\%m\%d_\%H\%M).txt
      fi
    done
    

    This cron job runs hourly. If the disk usage is critical, it pipes the detailed message to OpenClaw, asking for analysis and a fix. The output is saved to a timestamped file. You can then set up another cron job or a simple script to email you the contents of any new files in ~/openclaw_alerts/, or push them to a notification service like ntfy.sh.

    The non-obvious insight here is to *only* invoke OpenClaw when an anomaly is detected. Running openclaw on every check, even when things are normal, wastes tokens and API calls. The shell script handles the conditional logic, minimizing OpenClaw’s involvement.

    Automated Maintenance and Self-Healing

    For more advanced scenarios, OpenClaw can even suggest maintenance or “self-healing” actions. Let’s extend our disk example. Instead of just saving to a file, we could prompt OpenClaw to suggest a command. This requires careful consideration, as you’re giving an AI the ability to suggest commands to be run on your system. Always review suggested commands before execution, especially when starting out.

    Let’s create a script ~/scripts/process_alert.sh:

    #!/bin/bash
    ALERT_MESSAGE="$1"
    OPENCLAW_RESPONSE=$(/usr/local/bin/openclaw "Based on this server alert, what exact Linux command would you run to resolve the issue? Only output the command, nothing else. If no command is applicable, output 'NONE'. Alert: $ALERT_MESSAGE")
    
    if [[ "$OPENCLAW_RESPONSE" != "NONE" && "$OPENCLAW_RESPONSE" != "" ]]; then
      echo "OpenClaw suggested command: $OPENCLAW_RESPONSE" >> ~/openclaw_actions.log
      # For safety, we just log it. For full automation, you'd add:
      # eval "$OPENCLAW_RESPONSE" >> ~/openclaw_actions.log 2>&1
      # echo "Command executed." >> ~/openclaw_actions.log
    else
      echo "OpenClaw found no specific command for: $ALERT_MESSAGE" >> ~/openclaw_actions.log
    fi
    

    Then modify your crontab entry:

    # Run every hour
    0 * * * * ~/scripts/check_disk.sh | while read line; do
      if echo "$line" | grep -q "CRITICAL"; then
        ~/scripts/process_alert.sh "$line"
      fi
    done
    

    This setup will log OpenClaw’s suggested actions. Initially, you should manually review ~/openclaw_actions.log. Once you build confidence, you can uncomment the eval line in process_alert.sh for truly automated responses. Be extremely cautious with this, especially on production systems. The prompt “Only output the command, nothing else” is critical here to prevent OpenClaw from adding conversational filler that would break eval.

    Limitations and Hardware Considerations

    This approach works best on a VPS or a dedicated home server with at least 2GB of RAM. While OpenClaw itself is lightweight when idle, the underlying Python environment and API calls do consume some memory and CPU during execution. A Raspberry Pi 3 or older, especially with less than 2GB RAM, might struggle with the OpenClaw execution alongside other server tasks, leading to slower responses or system instability. Modern Pis (4 or 5) with sufficient RAM should be fine. Network latency to the API provider is also a factor; a fast, stable internet connection is assumed.

    This setup is not a replacement for enterprise-grade monitoring solutions. It’s a practical, cost-effective way to add intelligent, human-readable insights and suggestions to your home server’s existing shell scripts and cron jobs. It excels at summarizing log data, translating technical alerts into understandable language, and suggesting remediation for common issues that might otherwise require manual research.

    Your next concrete step: update your ~/.openclaw/config.json with "default_model": "claude-haiku-20240307" and "max_tokens": 512 to optimize OpenClaw for efficient monitoring.

  • How to Debug OpenClaw Skills That Aren’t Working

    If you’ve been developing custom skills for OpenClaw and find them consistently failing without clear error messages, you’re not alone. The OpenClaw skill execution environment can be a black box, especially when dealing with complex dependencies or subtle runtime issues. This guide will walk you through a systematic debugging process, focusing on practical steps and real-world scenarios that often trip up developers.

    Understanding the OpenClaw Skill Execution Environment

    Before diving into debugging, it’s crucial to understand how OpenClaw executes skills. Each skill runs in an isolated environment, typically a separate process or even a container, depending on your OpenClaw setup. This isolation is great for security and stability but makes direct debugging challenging. OpenClaw captures standard output (stdout) and standard error (stderr) from your skill’s execution and logs them. The primary challenge is that not all errors make it to these logs, especially if the process crashes early or a critical dependency isn’t met.

    A common misconception is that if your skill works locally on your development machine, it will work perfectly within OpenClaw. This often isn’t true due to differences in environment variables, installed packages, user permissions, and working directories. OpenClaw typically executes skills from a specific working directory, often related to ~/.openclaw/skills/<skill_name>/, and might not inherit your shell’s environment path.

    Initial Checks: The Low-Hanging Fruit

    Start with the basics. Many skill failures are due to simple oversight. First, check your skill’s skill.yaml configuration file. Ensure the entrypoint path is correct and executable. For Python skills, this often looks like:

    
    name: my_failing_skill
    description: A skill that never works
    entrypoint: python3 main.py
    runtime: python
    

    Verify that main.py actually exists in the root of your skill’s directory. A common error is placing it in a subdirectory, or misnaming it. Next, ensure all necessary dependencies are declared. For Python skills, this means a requirements.txt file in the skill’s root. OpenClaw will attempt to install these when the skill is loaded or updated.

    
    # requirements.txt
    requests
    numpy
    

    If you’re using a specific Python version, make sure OpenClaw is configured to use it, or explicitly call it in your entrypoint, e.g., python3.9 main.py. The non-obvious insight here is that OpenClaw’s default Python environment might not have all the system-wide packages you expect, even if they’re installed globally on your host. Always declare dependencies in requirements.txt.

    Leveraging OpenClaw’s Internal Logs

    OpenClaw provides internal logging that can be invaluable. The most direct way to access these logs is through the OpenClaw command-line interface (CLI). To see the output of a specific skill failing, use:

    
    openclaw logs --skill my_failing_skill
    

    This command will show you the captured stdout and stderr. Pay close attention to any Python tracebacks, permission denied errors, or “command not found” messages. If you see a Python traceback, the crucial part is often the first few lines indicating the file and line number where the error originated, and the last line describing the exception type.

    For more verbose logging from OpenClaw itself, you can increase the global log level. This is particularly useful if your skill isn’t even getting to the point of execution (e.g., an issue with loading the skill itself). Edit your ~/.openclaw/config.json:

    
    {
      "log_level": "DEBUG",
      "skills_directory": "~/.openclaw/skills"
    }
    

    Restart OpenClaw after making this change. Then, monitor the main OpenClaw logs:

    
    openclaw logs --follow
    

    This will show you much more detail about skill loading, dependency installation attempts, and execution commands. A common non-obvious issue here is a failed dependency installation. If pip install -r requirements.txt fails silently, your skill will still load but crash immediately on import. The DEBUG logs will often reveal the exact pip error.

    Reproducing the Environment Locally

    The most effective way to debug deeply is to replicate OpenClaw’s execution environment as closely as possible outside of OpenClaw. This involves manually running your skill’s entrypoint from the same working directory and with similar environment variables.

    First, navigate to your skill’s directory:

    
    cd ~/.openclaw/skills/my_failing_skill
    

    Next, try to execute your entrypoint directly. If your skill.yaml specifies python3 main.py, run:

    
    python3 main.py arg1 arg2 # replace arg1/arg2 with actual skill inputs if known
    

    If your skill relies on environment variables that OpenClaw might set (e.g., API keys passed via skill configuration), you’ll need to simulate those. For example, if your skill expects OPENCLAW_API_KEY, you would run:

    
    OPENCLAW_API_KEY="sk-..." python3 main.py
    

    This direct execution will often reveal errors that were previously swallowed or difficult to trace through OpenClaw’s logs. The non-obvious insight here is to pay attention to the user running the process. OpenClaw typically runs as the user who started it, but if you’re running it as a systemd service, it might run under a different user with limited permissions. Check the file permissions in your skill directory (ls -l) and ensure the user running OpenClaw has read/execute access.

    For Python skills, consider adding explicit print statements throughout your code, especially at the beginning of functions, and before and after critical operations. These print statements will show up in openclaw logs --skill my_failing_skill and can help pinpoint exactly where the execution flow breaks down.

    Advanced Techniques: Using a Debugger and Test Frameworks

    For very complex skills, direct local execution might not be enough. If your skill is a Python application, you can integrate a debugger. Modify your main.py to include a breakpoint or use a debugger like pdb:

    
    # main.py
    import pdb
    
    def my_skill_function():
        # ... some code ...
        pdb.set_trace() # Execution will pause here
        # ... more code ...
    
    if __name__ == "__main__":
        my_skill_function()
    

    When you run this skill locally (python3 main.py), it will drop into a debugger prompt, allowing you to inspect variables and step through code. This won’t work directly within OpenClaw’s non-interactive environment, but it’s invaluable for isolating the problematic code path locally. The limitation here is that this technique is primarily for local debugging and can’t be used for live debugging within the OpenClaw runtime directly without significant effort to attach a remote debugger.

    Finally, for robust skills, consider implementing unit and integration tests. A comprehensive test suite can catch regressions and ensure your skill functions as expected under various inputs, even before deploying it to OpenClaw. While this is more of a development best practice than a debugging technique, it prevents many issues from reaching the OpenClaw environment in the first place.

    Conclusion and Next Step

    Debugging OpenClaw skills requires a methodical approach, often moving from general checks to detailed environment replication. The key is to systematically narrow down the problem by leveraging OpenClaw’s logging capabilities and then reproducing the failure outside of OpenClaw to use standard debugging tools.

    Your immediate next step is to update your ~/.openclaw/config.json to include "log_level": "DEBUG", restart OpenClaw, and then run openclaw logs --follow while attempting to invoke your failing skill. This will provide the most verbose output directly from OpenClaw, often revealing crucial setup or execution errors.

  • OpenClaw + Notion: Building a Personal Knowledge Base That Manages Itself

    If you’re using OpenClaw to power a personal knowledge base and want it to integrate with Notion, you’ve likely hit a wall with keeping your Notion pages updated and organized without constant manual intervention. The dream is to simply dump information into a raw Notion page, and have OpenClaw categorize, summarize, and link it automatically. The reality is often a jumble of Python scripts, API rate limits, and a knowledge base that feels more like a chore than a help. This guide details a robust, self-managing system using OpenClaw and Notion, focusing on the practical steps and pitfalls.

    Affiliate Disclosure: As an Amazon Associate, we earn from qualifying purchases. This means we may earn a small commission when you click our links and make a purchase on Amazon. This comes at no extra cost to you and helps support our site.

    The Core Problem: Asynchronous Processing and Notion API Limits

    The primary challenge when building an automated Notion knowledge base with OpenClaw is dealing with Notion’s API rate limits and the asynchronous nature of large language model (LLM) processing. You can’t just hit the Notion API with a hundred updates simultaneously. And waiting for OpenClaw to process a lengthy document synchronously before moving on often leads to timeouts or a sluggish user experience. We need a system that queues tasks, processes them in the background, and updates Notion when ready.

    Our solution revolves around a combination of OpenClaw’s event-driven architecture, a simple SQLite queue, and a dedicated worker process. First, ensure your OpenClaw instance is configured to emit events upon document ingestion or modification. Add the following to your ~/.openclaw/config.json:

    {
      "storage": {
        "driver": "sqlite",
        "path": "/var/lib/openclaw/data.db"
      },
      "event_bus": {
        "driver": "filesystem",
        "path": "/var/lib/openclaw/events"
      },
      "plugins": [
        "openclaw-notion-integrator"
      ],
      "notion": {
        "api_token": "secret_YOUR_NOTION_INTEGRATION_TOKEN",
        "database_id": "YOUR_NOTION_DATABASE_ID"
      }
    }
    

    The openclaw-notion-integrator is a custom plugin you’ll need to develop or adapt. It’s not part of the core OpenClaw distribution. This plugin listens for specific OpenClaw events (e.g., document.created, document.updated) and pushes a task into our SQLite queue. Here’s a simplified version of the plugin’s core logic:

    # ~/.openclaw/plugins/openclaw_notion_integrator.py
    import sqlite3
    import json
    import os
    
    class NotionIntegratorPlugin:
        def __init__(self, config):
            self.config = config
            self.db_path = os.path.join(os.path.dirname(config['storage']['path']), 'notion_queue.db')
            self._init_db()
    
        def _init_db(self):
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS notion_tasks (
                    id INTEGER PRIMARY KEY,
                    event_type TEXT NOT NULL,
                    payload TEXT NOT NULL,
                    status TEXT DEFAULT 'pending'
                )
            ''')
            conn.commit()
            conn.close()
    
        def handle_event(self, event_type, payload):
            if event_type in ['document.created', 'document.updated']:
                conn = sqlite3.connect(self.db_path)
                cursor = conn.cursor()
                cursor.execute("INSERT INTO notion_tasks (event_type, payload) VALUES (?, ?)",
                               (event_type, json.dumps(payload)))
                conn.commit()
                conn.close()
                print(f"Queued Notion task for event: {event_type}")
    
        def register_handlers(self, event_bus):
            event_bus.register_handler('document.created', self.handle_event)
            event_bus.register_handler('document.updated', self.handle_event)
    

    This plugin doesn’t directly interact with Notion. Instead, it acts as a bridge, ensuring that any relevant OpenClaw event is safely stored for later processing by a dedicated worker.

    The Non-Obvious Insight: Model Choice and Cost Efficiency

    When OpenClaw processes a document for summarization, categorization, or linking, it uses an LLM. The default models recommended in many OpenClaw examples, while powerful, can be prohibitively expensive for a personal knowledge base that might process dozens or hundreds of documents daily. For 90% of personal knowledge management tasks – generating a short summary, extracting keywords, or classifying a document into predefined categories – a smaller, cheaper model is often more than sufficient.

    Specifically, I’ve found claude-haiku-4-5 to be an excellent balance of cost and capability. It’s often 10x cheaper than larger models like claude-opus-4-5 or even some GPT-4 variants, yet it performs remarkably well for typical knowledge base operations. To configure OpenClaw to use this:

    # ~/.openclaw/config.json (excerpt)
    {
      "llm": {
        "provider": "anthropic",
        "model": "claude-3-haiku-20240307",
        "api_key": "sk-ant-YOUR_ANTHROPIC_API_KEY"
      },
      "embedding": {
        "provider": "openai",
        "model": "text-embedding-3-small",
        "api_key": "sk-YOUR_OPENAI_API_KEY"
      },
      ...
    }
    

    Note: Even though we’re using Anthropic for the LLM, OpenAI’s text-embedding-3-small is incredibly cost-effective and performs well for embeddings. Mixing providers like this is perfectly fine and often leads to the most optimized setup.

    The Dedicated Notion Worker

    With tasks queued and OpenClaw configured for cost-efficient LLM use, we need a separate process to consume these tasks and update Notion. This worker runs independently, polling our SQLite queue and handling Notion API calls. This separation is crucial for rate limit management and fault tolerance.

    Create a Python script, say notion_worker.py, with the following structure:

    # notion_worker.py
    import sqlite3
    import json
    import time
    import os
    from notion_client import Client
    from openclaw.core.document import Document
    from openclaw.core.config import Config
    from openclaw.core.processor import Processor

    # Load OpenClaw config to get Notion API token and database ID
    config_path = os.path.expanduser('~/.openclaw/config.json')
    claw_config = Config.from_file(config_path)
    notion_api_token = claw_config.get('notion.api_token')
    notion_database_id = claw_config.get('notion.database_id')

    if not notion_api_token or not notion_database_id:
    raise ValueError("Notion API token or database ID not found in OpenClaw config.")

    notion = Client(auth=notion_api_token)
    processor = Processor(claw_config) # OpenClaw processor for document analysis

    db_path = os.path.join(os.path.dirname(claw_config.get('storage.path')), 'notion_queue.db')

    def get_next_task():
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute("SELECT id, event_type, payload FROM notion_tasks WHERE status = 'pending' LIMIT 1")
    task = cursor.fetchone()
    conn.close()
    return task

    def update_task_status(task_id, status):
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()
    cursor.execute("UPDATE notion_tasks SET status = ? WHERE id = ?", (status, task_id))
    conn.commit()
    conn.close()

    def process_document_for_notion(doc_payload):
    doc_id = doc_payload['id']
    # Reconstruct OpenClaw Document from payload
    # In a real scenario, you'd fetch the full document from OpenClaw's storage
    # For this example, let's assume 'content' is directly available or fetchable.
    # doc = processor.storage.get_document(doc_id) # This would be the robust way
    # For simplicity, let's assume payload contains enough for a basic Document object
    doc = Document(id=doc_payload['id'], content=doc_payload.get('content', ''))

    # Use OpenClaw's processor to get structured data

  • How to Use OpenClaw’s Exec Tool Safely Without Breaking Your Server

    You’re sitting at your desk, staring at an OpenClaw automation that just executed rm -rf /home/appuser/data when you meant to delete temporary files in a subdirectory. Your hands go cold. This is the reality of the exec tool—incredibly powerful for server-side operations, but one bad command generation and your system can be crippled. The exec tool allows OpenClaw to run arbitrary commands on the host machine, which is essential for certain automation tasks, but this power comes with significant risks if not properly constrained. The good news: you don’t need unfettered exec access for 90% of practical automation. What you need is a tightly controlled sandbox.

    Understanding the Dangers of exec

    The core danger of exec is its direct access to the underlying operating system. OpenClaw, through its agent, executes whatever command string it generates. If the model hallucinates and generates a command like rm -rf /, you’re in deep trouble. Even less malicious but equally problematic commands—an infinite loop consuming all CPU, a script writing gigabytes of log files to disk, or a recursive process spawn—can bring your server to its knees within minutes. Most users initially enable exec without restrictions, only to realize the implications after a few close calls with disk-full errors or runaway processes. The non-obvious insight here is that you don’t need unfettered exec access for 90% of practical automation tasks; what you need is a tightly controlled sandbox.

    The Principle of Least Privilege: Sandboxing exec

    The solution isn’t to avoid exec entirely but to apply the principle of least privilege. We want to give OpenClaw just enough power to do its job and no more. This means two main strategies: strict command whitelisting and user isolation.

    Command Whitelisting with a Wrapper Script

    Instead of letting OpenClaw execute any command directly, we’ll create a wrapper script that acts as a gatekeeper. OpenClaw will only ever call this wrapper script, passing arguments to it. The wrapper script, in turn, will validate the arguments and execute only pre-approved commands.

    First, create a directory for your safe scripts, for example, /opt/openclaw-safe-scripts/. Make sure it’s owned by a non-root user that OpenClaw will run as, and has appropriate permissions (e.g., chmod 700).

    Inside this directory, create a script named openclaw_wrapper.sh:

    
    #!/bin/bash
    
    # Log all calls for auditing
    LOG_FILE="/var/log/openclaw_exec.log"
    echo "$(date) - User: $(whoami) - PID: $$ - Command: $@" >> "$LOG_FILE"
    
    # --- Whitelisted Commands ---
    # Example 1: Safely list files in a specific directory
    if [[ "$1" == "list_dir" ]]; then
        DIR_PATH="$2"
        # Basic path validation to prevent '..' or absolute paths outside designated areas
        if [[ "$DIR_PATH" =~ ^[a-zA-Z0-9_/.-]+$ ]] && [[ "$DIR_PATH" != /* ]] && [[ "$DIR_PATH" != *..* ]]; then
            ls -la "/var/www/mywebapp/$DIR_PATH"
        else
            echo "Error: Invalid directory path" >&2
            exit 1
        fi
    
    # Example 2: Safely restart a specific service
    elif [[ "$1" == "restart_service" ]]; then
        SERVICE_NAME="$2"
        ALLOWED_SERVICES=("nginx" "php-fpm" "mysql")
        if [[ " ${ALLOWED_SERVICES[@]} " =~ " ${SERVICE_NAME} " ]]; then
            systemctl restart "$SERVICE_NAME"
        else
            echo "Error: Service not whitelisted" >&2
            exit 1
        fi
    
    # Example 3: Safely run database backups
    elif [[ "$1" == "backup_db" ]]; then
        BACKUP_DIR="/var/backups/databases"
        mkdir -p "$BACKUP_DIR"
        mysqldump -u backup_user -p"$BACKUP_PASS" --all-databases > "$BACKUP_DIR/backup_$(date +%s).sql"
    
    else
        echo "Error: Command not recognized or not whitelisted" >&2
        exit 1
    fi
    

    Make the wrapper script executable:

    chmod 750 /opt/openclaw-safe-scripts/openclaw_wrapper.sh
    

    Now configure OpenClaw to call only this wrapper. In your OpenClaw configuration (or however you invoke exec), instead of allowing arbitrary commands, restrict it to:

    
    /opt/openclaw-safe-scripts/openclaw_wrapper.sh [command] [args]
    

    User Isolation: Run OpenClaw as a Restricted User

    Never run OpenClaw as root. Create a dedicated, unprivileged system user:

    
    useradd -r -s /bin/false -d /var/lib/openclaw openclaw_user
    

    Ensure this user has minimal filesystem permissions. For example, if it needs to write logs or temporary files, create a directory owned by this user:

    
    mkdir -p /var/lib/openclaw/tmp
    chown openclaw_user:openclaw_user /var/lib/openclaw/tmp
    chmod 700 /var/lib/openclaw/tmp
    

    Then run the OpenClaw process as openclaw_user. If the process is compromised or generates malicious commands, the damage is limited to what that unprivileged user can access.

    Resource Limits: Prevent Resource Exhaustion

    Even with command whitelisting, a script might consume excessive CPU or memory. Use ulimit to enforce hard limits. Create a systemd service file for OpenClaw with resource constraints:

    
    [Service]
    User=openclaw_user
    ExecStart=/usr/local/bin/openclaw-agent
    MemoryLimit=512M
    CPUQuota=50%
    TasksMax=100
    

    These settings limit OpenClaw to 512 MB of memory, 50% of a single CPU core, and a maximum of 100 processes. Adjust these based on your expected workload.

    Audit Logging: Know What Happened

    The wrapper script above already logs all execution attempts to /var/log/openclaw_exec.log. Monitor this file regularly. Set up log rotation to prevent it from filling your disk:

    
    # Add to /etc/logrotate.d/openclaw
    /var/log/openclaw_exec.log {
        daily
        rotate 30
        compress
        delaycompress
        notifempty
        create 0600 root root
    }
    

    A Practical Example: Safe Deployment Script

    Let’s say you want OpenClaw to trigger deployments of your application. Instead of giving it direct access to git, docker, or deployment tools, create a whitelisted deployment wrapper:

    
    elif [[ "$1" == "deploy_app" ]]; then
        ENV="$2"
        ALLOWED_ENVS=("staging" "production")
        if [[ " ${ALLOWED_ENVS[@]} " =~ " ${ENV} " ]]; then
            /opt/deployment-scripts/deploy.sh "$ENV"
        else
            echo "Error: Invalid environment" >&2
            exit 1
        fi
    

    The /opt/deployment-scripts/deploy.sh script can perform whatever deployment steps you need (pull code, run tests, build Docker images using Docker CLI ~$0 as it’s open-source, restart services), but it’s a separate, auditable script that you control and can review for safety.

    Testing Your Sandbox

    Before deploying to production, test your sandbox thoroughly:

    1. Attempt to access files outside permitted directories. Verify the wrapper rejects these.
    2. Try to execute whitelisted commands with malicious arguments (e.g., list_dir ../../../../etc). Confirm the path validation catches them.
    3. Trigger resource-heavy operations and confirm systemd limits kick in.
    4. Review audit logs to ensure all attempts are logged.

    Conclusion

    The exec tool in OpenClaw is powerful and necessary for real-world automation, but it demands respect. By combining command whitelisting, user isolation, resource limits, and audit logging, you can safely harness its power without risking your infrastructure. Start restrictive—whitelist only the exact commands you need—and expand only as you gain confidence. Your future self will thank you when a bad model hallucination tries to execute a blacklisted command and the wrapper simply logs it and moves on.

  • OpenClaw Gateway Explained: How Remote Node Connections Work

    If you’re managing an OpenClaw setup where your main instance needs to access remote resources – like specialized GPUs, custom data sources, or geographically distributed services – the OpenClaw Gateway is your crucial component. Often, users try to build complex SSH tunnels or write custom API proxies, which quickly become unmanageable. The Gateway simplifies this by providing a secure, efficient, and native way for your OpenClaw core to interact with resources on other machines without exposing internal services directly to the internet.

    Affiliate Disclosure: As an Amazon Associate, we earn from qualifying purchases. This means we may earn a small commission when you click our links and make a purchase on Amazon. This comes at no extra cost to you and helps support our site.

    The Problem: Distributed Resources

    Imagine your primary OpenClaw instance is running on a modest cloud VPS, handling orchestrations and API calls. However, you have a separate, powerful GPU server in a different data center for intensive model training, or perhaps a local machine with proprietary sensor data that needs to be processed. Directly exposing the GPU server’s API or the local machine’s data stream to your main OpenClaw instance across the public internet introduces significant security risks and often requires complex network configurations like VPNs or firewall rules that are difficult to maintain. Trying to achieve this by simply running a second OpenClaw instance and having them call each other’s APIs can lead to circular dependencies and difficult-to-debug authentication issues.

    Introducing the OpenClaw Gateway

    The OpenClaw Gateway acts as a secure, authenticated proxy between your main OpenClaw instance and these remote resources. It establishes an outbound connection to your central OpenClaw instance, meaning you don’t need to open inbound ports on the remote resource machine. This is a critical security advantage, especially for machines behind restrictive firewalls or NAT. The Gateway itself is a lightweight daemon that sits on the remote machine, listening for requests from your main OpenClaw instance and forwarding them to local services.

    To set up a Gateway, you’ll first need to generate a Gateway token on your main OpenClaw instance. SSH into your primary OpenClaw host and run:

    openclaw gateway generate-token --name my-gpu-server-gateway --lifetime 30d

    This command will output a long alphanumeric token. Copy this token carefully; it’s used to authenticate your remote Gateway instance with your main OpenClaw installation. The --lifetime flag is important; tokens should be rotated regularly for security. If omitted, the default is typically 90 days.

    Gateway Installation and Configuration

    Now, SSH into your remote machine (e.g., your GPU server). You’ll need to install the OpenClaw client, which includes the Gateway component. The installation method varies slightly by OS, but for most Linux systems, it’s a simple curl command:

    curl -sSL https://get.openclaw.dev | bash

    Once installed, you’ll configure the Gateway to connect to your main OpenClaw instance using the token you just generated. Create a configuration file, typically at /etc/openclaw/gateway.yml or ~/.openclaw/gateway.yml:

    # /etc/openclaw/gateway.yml
    gateway:
      # The URL of your main OpenClaw instance's API endpoint
      # Ensure this is accessible from the remote Gateway machine
      server_url: "https://your-main-openclaw-instance.com/api"
      
      # The authentication token generated earlier
      token: "ocg_your_generated_token_here"
      
      # Name for this gateway, should match the name given during token generation
      name: "my-gpu-server-gateway"
      
      # Services exposed through this gateway. Key is the internal name, value is the local URL.
      services:
        gpu-inference: "http://localhost:8001" # A local API on the GPU server
        local-data-stream: "tcp://localhost:9000" # A TCP stream for sensor data
        
      # Optional: TLS certificate verification settings
      tls:
        insecure_skip_verify: false # Set to true only for testing with self-signed certs
        # ca_cert_path: "/path/to/your/custom/ca.crt" # If your main instance uses a custom CA
    

    The services section is where the magic happens. You define a logical name (e.g., gpu-inference) and map it to a local endpoint on the Gateway machine (e.g., http://localhost:8001). When your main OpenClaw instance requests gateway://my-gpu-server-gateway/gpu-inference, the Gateway on the remote machine will forward that request to http://localhost:8001 on its own host.

    After saving the configuration, start the Gateway service. For systemd-based systems, you’d typically run:

    sudo systemctl enable openclaw-gateway
    sudo systemctl start openclaw-gateway
    sudo systemctl status openclaw-gateway

    Verify that the Gateway is running and connected without errors. You should see output indicating a successful connection to your main OpenClaw instance.

    Using the Gateway from Your Main OpenClaw Instance

    Once the Gateway is connected, you can reference the exposed services directly from your main OpenClaw workflows or configurations. For example, if you have a workflow step that needs to call the GPU inference service:

    # In your OpenClaw workflow definition (e.g., a .claw file)
    {
      "name": "gpu_inference_task",
      "steps": [
        {
          "type": "http_request",
          "config": {
            "method": "POST",
            "url": "gateway://my-gpu-server-gateway/gpu-inference/predict",
            "headers": {
              "Content-Type": "application/json"
            },
            "body": {
              "image_data": "{{ .input.image }}"
            }
          }
        }
      ]
    }
    

    Notice the gateway:// schema. This tells OpenClaw to route the request through the specified Gateway. The path after the gateway name (e.g., /gpu-inference/predict) is appended to the local URL defined in the Gateway’s configuration (http://localhost:8001/predict in this example).

    Non-Obvious Insight: Resource Management and Load Balancing

    While the Gateway simplifies connectivity, it doesn’t inherently provide load balancing or advanced resource management. If you have multiple GPU servers, you’ll need to run a separate Gateway for each and then implement your own load balancing logic within your OpenClaw workflows. A common pattern is to register multiple Gateways and then use a “round-robin” or “least-busy” strategy by dynamically selecting which gateway:// URL to use for a task. For example, you might maintain a list of active Gateways in a shared configuration and have your workflow select one based on current load metrics retrieved via another Gateway service.

    Also, remember that the Gateway connection is outbound from the remote machine. If your main OpenClaw instance is behind a strict firewall, ensure it can accept inbound connections from the remote Gateway machine on its configured API port (typically 443 for HTTPS).

    Limitations

    The OpenClaw Gateway is designed for connecting to specific services on remote machines, not for creating a mesh network or a full-blown VPN. It’s a point-to-point secure channel. While it handles TCP and HTTP/HTTPS, it’s not a general-purpose network tunnel for all protocols. For extremely high-throughput, low-latency scenarios where direct access is paramount, a dedicated private network link or VPN might still be preferable, but for most API and data streaming use cases, the Gateway is more than sufficient and much simpler to manage.

    This setup works best when your remote resources are stable and have a consistent local address. It’s less suited for highly dynamic environments where service endpoints frequently change. The Gateway also adds a small amount of latency due to the proxying, but for most applications, this is negligible.

    To start using the Gateway, go to your primary OpenClaw instance and run: openclaw gateway generate-token --name my-first-gateway --lifetime 90d to get your first token.

  • Using OpenClaw for Affiliate Site Management: Real Workflow Examples

    If you’re running OpenClaw to manage your affiliate sites, particularly for content generation, and you’re finding the default setup for content pipelines to be cumbersome or slow, you’re not alone. The out-of-the-box cron jobs often don’t account for the nuances of SEO-driven content, leading to generic articles or inefficient resource usage. Let’s dig into how to streamline this, specifically focusing on generating product reviews and comparison articles, and how to make OpenClaw really hum for this use case.

    Affiliate Disclosure: As an Amazon Associate, we earn from qualifying purchases. This means we may earn a small commission when you click our links and make a purchase on Amazon. This comes at no extra cost to you and helps support our site.

    Optimizing Content Generation Pipelines

    The core problem with using OpenClaw’s default generate_articles command for affiliate content is its lack of specificity. You typically want highly targeted content: detailed product reviews, “best of” lists, or comparisons. Relying on a broad prompt for all articles often results in content that misses key SEO opportunities or requires heavy manual editing. Instead, we’ll create custom content templates and dedicated generation scripts.

    First, let’s look at the default configuration. You might have a cron job like this:

    0 3 * * * /usr/local/bin/openclaw generate_articles --config /path/to/your/site.json --count 5
    

    This is too generic. We need to break it down. Let’s assume you have a site.json configured for a specific affiliate niche. Instead of one large article_prompt, we need specialized prompts and a way to feed specific product data.

    Create a directory structure for your templates and data:

    ~/openclaw_projects/
    ├── my_affiliate_site/
    │   ├── config.json
    │   ├── data/
    │   │   ├── products_laptops.json
    │   │   └── products_keyboards.json
    │   └── templates/
    │       ├── product_review.txt
    │       └── comparison_article.txt
    └── scripts/
        ├── generate_reviews.py
        └── generate_comparisons.py
    

    Your config.json in my_affiliate_site/ should be minimal, primarily defining the API keys and output directory:

    {
      "api_keys": {
        "openai": "sk-YOUR_OPENAI_KEY",
        "claude": "sk-YOUR_CLAUDE_KEY"
      },
      "output_dir": "/var/www/my-affiliate-site.com/content",
      "model": "claude-haiku-4-5"
    }
    

    The non-obvious insight here is that while the OpenClaw docs might steer you towards larger models like gpt-4o or claude-opus for “quality,” for 90% of affiliate content tasks, claude-haiku-4-5 is incredibly effective and orders of magnitude cheaper. Its speed also means you can generate more content in the same timeframe, which is crucial for scaling. For complex comparisons or deep dive “ultimate guides,” then consider a more powerful model, but for standard reviews, Haiku is your workhorse.

    Custom Content Templates and Data Injection

    Let’s define our templates. For a product review, templates/product_review.txt might look like this:

    TITLE: Review of [PRODUCT_NAME]: Is It Worth Your Money?
    
    INTRODUCTION:
    The [PRODUCT_BRAND] [PRODUCT_NAME] has been making waves in the [PRODUCT_CATEGORY] market. With its [KEY_FEATURE_1] and [KEY_FEATURE_2], it promises a [BENEFIT_1] experience. But does it deliver? We dive deep into its performance, features, and overall value.
    
    FEATURES & SPECIFICATIONS:
    
  • Processor: [PROCESSOR]
  • RAM: [RAM]
  • Storage: [STORAGE]
  • Display: [DISPLAY]
  • Price: [PRICE]
  • PROS:
  • [PRO_1]
  • [PRO_2]
  • [PRO_3]
  • CONS:
  • [CON_1]
  • [CON_2]
  • CONCLUSION: Overall, the [PRODUCT_NAME] is a strong contender for [TARGET_AUDIENCE]. While it has its minor drawbacks, its [MAIN_PRO] makes it a compelling choice. If you're looking for [IDEAL_USE_CASE], this product should be on your shortlist. [AFFILIATE_LINK_BUTTON]

    For comparison articles, templates/comparison_article.txt:

    TITLE: [PRODUCT_A_NAME] vs. [PRODUCT_B_NAME]: Which [PRODUCT_CATEGORY] is Right for You?
    
    INTRODUCTION:
    Choosing between the [PRODUCT_A_BRAND] [PRODUCT_A_NAME] and the [PRODUCT_B_BRAND] [PRODUCT_B_NAME] can be tough. Both are popular choices in the [PRODUCT_CATEGORY] segment, offering distinct advantages. We break down their features, performance, and value to help you make an informed decision.
    
    COMPARISON TABLE:
    | Feature       | [PRODUCT_A_NAME]     | [PRODUCT_B_NAME]     |
    |---------------|----------------------|----------------------|
    | Price         | [PRODUCT_A_PRICE]    | [PRODUCT_B_PRICE]    |
    | [FEATURE_1]   | [PRODUCT_A_FEATURE_1]| [PRODUCT_B_FEATURE_1]|
    | [FEATURE_2]   | [PRODUCT_A_FEATURE_2]| [PRODUCT_B_FEATURE_2]|
    | [FEATURE_3]   | [PRODUCT_A_FEATURE_3]| [PRODUCT_B_FEATURE_3]|
    
    PERFORMANCE:
    The [PRODUCT_A_NAME] excels in [PRODUCT_A_PERFORMANCE_HIGHLIGHT], while the [PRODUCT_B_NAME] shines in [PRODUCT_B_PERFORMANCE_HIGHLIGHT].
    
    CONCLUSION:
    If [IDEAL_USE_CASE_A], the [PRODUCT_A_NAME] is likely your best bet. However, for [IDEAL_USE_CASE_B], consider the [PRODUCT_B_NAME].
    
    [AFFILIATE_LINK_A_BUTTON] [AFFILIATE_LINK_B_BUTTON]
    

    Now, let’s populate our data. data/products_laptops.json:

    [
      {
        "PRODUCT_NAME": "Dell XPS 15",
        "PRODUCT_BRAND": "Dell",
        "PRODUCT_CATEGORY": "laptop",
        "KEY_FEATURE_1": "stunning OLED display",
        "KEY_FEATURE_2": "powerful Intel Core i9 processor",
        "BENEFIT_1": "premium computing",
        "PROCESSOR": "Intel Core i9-13900H",
        "RAM": "32GB DDR5",
        "STORAGE": "1TB NVMe SSD",
        "DISPLAY": "15.6-inch OLED 3.5K",
        "PRICE": "$2299",
        "PRO_1": "Gorgeous OLED screen for content creation",
        "PRO_2": "Excellent build quality and design",
        "PRO_3": "Strong performance for demanding tasks",
        "CON_1": "Battery life could be better under heavy load",
        "CON_2": "Can get warm during intense workloads",
        "CON_3": "High price point",
        "TARGET_AUDIENCE": "creative professionals and power users",
        "IDEAL_USE_CASE": "video editing, graphic design, and multitasking",
        "AFFILIATE_LINK_BUTTON": "[Buy the Dell XPS 15 on Amazon]"
      },
      {
        "PRODUCT_NAME": "MacBook Air M2",
        "PRODUCT_BRAND": "Apple",
        "PRODUCT_CATEGORY": "laptop",
        ...
      }
    ]
    

    Automated Generation with Custom Scripts

    OpenClaw provides a Python SDK, which we’ll leverage. Here’s scripts/generate_reviews.py:

    import json
    import os
    from openclaw import OpenClaw

    # Initialize OpenClaw with the site's config
    claw = OpenClaw(config_path='~/openclaw_projects/my_affiliate_site/config.json')

    # Load product data
    with open('~/openclaw_projects/my_affiliate_site/data/products_laptops.json', 'r') as f:
    laptops = json.load(f)

    # Load review template
    with open('~/openclaw_projects/my_affiliate_site/templates/product_review.txt', 'r') as f:
    review_template = f.read()

    # Generate reviews for each product
    for product in laptops:
    # Replace placeholders in the template with product data
    filled_template = review_template
    for key

  • OpenClaw Token Usage: How to Monitor and Reduce API Costs

    If you’re running OpenClaw for your daily batch processing or real-time inference tasks, you’ve likely seen those API bills climb. It’s easy to get lost in the abstraction of “tokens” until the monthly statement hits. The problem isn’t just about the raw number of requests; it’s about the efficiency of those requests. Without a clear picture of token usage per model, per task, and over time, you’re effectively flying blind when it comes to cost optimization.

    Affiliate Disclosure: As an Amazon Associate, we earn from qualifying purchases. This means we may earn a small commission when you click our links and make a purchase on Amazon. This comes at no extra cost to you and helps support our site.

    Understanding OpenClaw’s Token Reporting

    OpenClaw, by default, provides some basic token usage information in its verbose logs, but it’s often not granular enough for real cost analysis. When you execute a command like openclaw process --config my_batch_job.yaml, you’ll see summary lines if your log level is set appropriately. For instance, an entry might look like this:

    [INFO] [2024-07-23 10:35:12] [claude-haiku-4-5] Request complete. Prompt: 1200 tokens, Completion: 300 tokens. Total: 1500 tokens. Cost: $0.00315.

    This is useful for individual requests, but aggregating this across hundreds or thousands of calls, potentially with different models, is manual and error-prone. The real challenge comes when you want to see which specific parts of your prompts are consuming the most tokens, or if a particular task is consistently over-budget. OpenClaw’s built-in metrics, while present, aren’t exposed in a way that allows for easy, real-time aggregation and visualization without some extra plumbing.

    Setting Up Custom Token Monitoring

    To get a better handle on costs, we need to capture this data systematically. OpenClaw offers a callback mechanism that can be leveraged. We’ll set up a simple local Redis instance to store token data. This isn’t production-grade telemetry, but for a single VPS or a small cluster, it’s incredibly effective and lightweight. First, ensure Redis is installed and running on your system:

    sudo apt update
    sudo apt install redis-server
    sudo systemctl enable redis-server
    sudo systemctl start redis-server

    Next, we need to configure OpenClaw to send token events to a custom script. Create a new Python file, say ~/.openclaw/callbacks/token_logger.py:

    import json
    import redis
    import os
    from datetime import datetime
    
    # Initialize Redis client
    REDIS_HOST = os.getenv('OPENCLAW_REDIS_HOST', 'localhost')
    REDIS_PORT = int(os.getenv('OPENCLAW_REDIS_PORT', 6379))
    REDIS_DB = int(os.getenv('OPENCLAW_REDIS_DB', 0))
    
    try:
        r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB)
        r.ping() # Test connection
    except redis.exceptions.ConnectionError as e:
        print(f"ERROR: Could not connect to Redis: {e}")
        r = None # Disable Redis logging if connection fails
    
    def on_request_complete(data):
        """
        Callback function executed by OpenClaw after each model request.
        Data contains: model_name, prompt_tokens, completion_tokens, total_tokens, cost, task_id (if available), timestamp.
        """
        if r is None:
            return
    
        try:
            log_entry = {
                "timestamp": datetime.now().isoformat(),
                "model_name": data.get("model_name"),
                "prompt_tokens": data.get("prompt_tokens"),
                "completion_tokens": data.get("completion_tokens"),
                "total_tokens": data.get("total_tokens"),
                "cost": data.get("cost"),
                "task_id": data.get("task_id", "unknown"), # Optional, useful for grouping
                "session_id": data.get("session_id", "default") # Optional, useful for multi-session runs
            }
            # Store as a list, or as a sorted set with timestamp for easier range queries
            r.rpush(f"openclaw:tokens:{log_entry['model_name']}", json.dumps(log_entry))
            r.rpush("openclaw:tokens:all", json.dumps(log_entry)) # Global log
            # You could also use an HASH for daily summaries: r.hincrby("openclaw:daily_tokens:2024-07-23", model_name, total_tokens)
        except Exception as e:
            print(f"ERROR in token_logger callback: {e}")
    
    

    Now, tell OpenClaw to use this callback. Add this to your ~/.openclaw/config.json:

    {
      "callbacks": {
        "on_request_complete": [
          "~/.openclaw/callbacks/token_logger.py:on_request_complete"
        ]
      },
      "redis_host": "localhost",
      "redis_port": 6379
    }
    

    The redis_host and redis_port entries are custom config parameters for our callback script; OpenClaw itself doesn’t use them directly, but our script can pick them up via os.getenv or by reading the config (though os.getenv is often simpler for callback parameters). Every time OpenClaw completes a request, it will call on_request_complete in our Python script, which will push the token data to Redis.

    Non-Obvious Insight: Model Selection and Prompt Engineering

    Here’s the kicker: the default models recommended in some OpenClaw tutorials, like gpt-4o or claude-3-opus-20240229, are often overkill for 90% of tasks. We’ve found that for summarization, entity extraction, or even light classification, models like claude-haiku-4-5, gpt-3.5-turbo-0125, or even gemini-pro offer a significantly better cost-to-performance ratio. A typical claude-haiku-4-5 interaction might cost $0.00025 per 1K tokens, whereas claude-3-opus could be $0.015 per 1K, a 60x difference. Monitor your Redis logs. If you consistently see gpt-4o or opus models used for simple tasks, you have a prime optimization target.

    Another crucial insight comes from prompt engineering. Often, developers use very chatty or verbose system prompts, or include excessive examples in few-shot prompts. For instance, providing 5 examples when 2 would suffice, or using a paragraph-long system instruction when a concise sentence would achieve the same result. The data in Redis will help you identify which models are used for which tasks. If your summarize_document task is pulling 5000 prompt tokens and only 200 completion tokens, investigate the prompt for unnecessary context or overly verbose instructions. Try to front-load important information and be as direct as possible. Avoid “fluff” in your prompts. Small changes here can have massive ripple effects on cost.

    Limitations

    This Redis-based monitoring setup is fantastic for understanding your own OpenClaw usage on a single machine or a small, self-managed cluster. It’s designed for quick, actionable insights without the overhead of a full-blown monitoring stack like Prometheus and Grafana. However, it does have limitations:

    • No long-term persistence: While Redis is persistent, it’s not designed for petabytes of historical data. For year-long trends or compliance, you’d want to periodically dump this data to a data warehouse or object storage.
    • Manual aggregation: You’ll need to write simple Python scripts to query Redis and aggregate the data into daily, weekly, or per-task summaries. It doesn’t give you a fancy dashboard out-of-the-box.
    • Scalability: For very high-throughput OpenClaw deployments across many machines, a single Redis instance might become a bottleneck. In such cases, a centralized logging solution like ELK or a dedicated metrics pipeline would be more appropriate.
    • Resource usage: While lightweight, Redis does consume some RAM. On a tiny VPS (e.g., a 512MB RAM instance), running OpenClaw, your application, and Redis might push memory limits. This setup is generally fine for VPS instances with at least 2GB RAM. Raspberry Pi 4 (4GB or 8
  • How I Reduced My Daily Task Load by 60% Using OpenClaw Automation

    If you’re like me, running a small dev shop or managing a personal project, you know the daily grind of repetitive tasks. Checking logs, triaging bug reports, summarizing daily stand-ups from a dozen Slack channels – it adds up. I was spending nearly two hours every morning just getting up to speed and preparing for the day. That’s 10 hours a week I could be coding, designing, or even, dare I say, sleeping. I started looking for ways to automate, and OpenClaw, coupled with a bit of scripting, became my MVP. Here’s how I cut my daily task load by 60%.

    The Problem: Information Overload and Repetitive Summaries

    My typical morning involved:

    1. Scanning through multiple Slack channels (#dev-updates, #bug-reports, #customer-feedback) for key information.
    2. Consolidating new bug reports from GitHub issues.
    3. Summarizing server logs for critical errors or unusual patterns.
    4. Drafting a quick daily summary for my internal team.

    Each of these steps, while seemingly minor, required context switching and manual parsing. I needed a way to ingest raw data, process it intelligently, and present a concise summary.

    Setting Up OpenClaw for Automated Summarization

    OpenClaw, with its ability to interact with various APIs and process natural language, was the perfect fit. My setup involves a Hetzner CX21 VPS (2 vCPU, 4GB RAM) running Ubuntu 22.04, which is more than sufficient. I chose the Hetzner VPS specifically because I needed a stable environment with good network performance for API calls without breaking the bank. For OpenClaw itself, I pulled the latest Docker image:

    docker pull openclaw/openclaw:latest

    Then, I created a persistent volume for configurations and data:

    docker volume create openclaw_data
    docker run -d --name openclaw -p 8080:8080 -v openclaw_data:/app/data openclaw/openclaw:latest

    This exposes the OpenClaw API on port 8080. My interactions are primarily through Python scripts that hit this API.

    The Non-Obvious Insight: Model Choice Matters

    The OpenClaw documentation often defaults to larger, more capable models for general tasks. While these are excellent for complex reasoning, for summarization and triage, they can be overkill and expensive. I initially tried gpt-4o for everything, and my daily API costs were through the roof. After some experimentation, I found that for my summarization tasks, claude-haiku-4-5 (via Anthropic API) or even gpt-3.5-turbo (via OpenAI API) provided 90% of the quality at 10x lower cost. The key is to craft very specific prompts. For example, instead of “Summarize this,” I use:

    "As an experienced DevOps engineer, review the following server logs. Identify any critical errors, warning trends, or unusual access patterns from the last 24 hours. Present a concise summary of no more than 150 words, listing actionable items if any. If there are no issues, state 'No critical issues found.'”

    This specific role-playing prompt guides the LLM to focus on what’s relevant to me, filtering out noise effectively.

    Integrating with Slack and GitHub

    Here’s how I integrated the various data sources. I wrote a small Python script that runs hourly via a cron job on my VPS. This script performs the following:

    Slack Summarization

    I use the Slack API to fetch messages from specific channels. You’ll need a Slack Bot Token with appropriate read permissions (channels:history, groups:history, im:history, mpim:history). Store this token securely, perhaps in an environment variable or a secret management service. My script fetches messages from the last 24 hours:

    import os
    import requests
    from slack_sdk import WebClient
    
    SLACK_TOKEN = os.getenv("SLACK_BOT_TOKEN")
    OPENCLAW_API_URL = "http://localhost:8080/v1/chat/completions"
    SLACK_CHANNELS = ["C01ABCDEF", "C02GHIJKL"] # Replace with your channel IDs
    
    client = WebClient(token=SLACK_TOKEN)
    all_messages = []
    
    for channel_id in SLACK_CHANNELS:
        response = client.conversations_history(channel=channel_id, oldest=str(int(time.time()) - 86400))
        for message in response["messages"]:
            if "text" in message:
                all_messages.append(message["text"])
    
    combined_slack_text = "\n".join(all_messages)
    
    # Send to OpenClaw for summarization
    payload = {
        "model": "claude-haiku-4-5", # Or gpt-3.5-turbo
        "messages": [
            {"role": "system", "content": "You are a helpful assistant that summarizes daily team communications."},
            {"role": "user", "content": f"Summarize the key updates, decisions, and action items from the following Slack messages from the last 24 hours. Focus on important project progress, blockers, and new tasks. Keep it under 200 words:\n\n{combined_slack_text}"}
        ]
    }
    openclaw_response = requests.post(OPENCLAW_API_URL, json=payload, headers={"Content-Type": "application/json"})
    slack_summary = openclaw_response.json()["choices"][0]["message"]["content"]
    print(f"Slack Summary:\n{slack_summary}")

    GitHub Issue Triage

    Similarly, for GitHub, I use the GitHub API to fetch new issues created or updated in the last 24 hours. A Personal Access Token (PAT) with repo scope is required. I filter for open issues and pass their titles and descriptions to OpenClaw:

    import os
    import requests
    from datetime import datetime, timedelta, timezone
    
    GITHUB_TOKEN = os.getenv("GITHUB_PAT")
    OPENCLAW_API_URL = "http://localhost:8080/v1/chat/completions"
    GITHUB_REPOS = ["myorg/myrepo1", "myorg/myrepo2"]
    
    headers = {"Authorization": f"token {GITHUB_TOKEN}"}
    issue_data = []
    since_time = (datetime.now(timezone.utc) - timedelta(days=1)).isoformat()
    
    for repo in GITHUB_REPOS:
        response = requests.get(
            f"https://api.github.com/repos/{repo}/issues?state=open&since={since_time}",
            headers=headers
        )
        for issue in response.json():
            issue_data.append(f"Issue #{issue['number']}: {issue['title']}\nDescription: {issue['body']}\nURL: {issue['html_url']}")
    
    combined_issues = "\n\n---\n\n".join(issue_data)
    
    if combined_issues:
        payload = {
            "model": "claude-haiku-4-5",
            "messages": [
                {"role": "system", "content": "You are a helpful assistant that triages bug reports."},
                {"role": "user", "content": f"Review the following GitHub issues. Identify critical bugs, high-priority features, and any recurring themes. Provide a concise summary of new and updated issues, highlighting anything that needs immediate attention. Keep it under 150 words.\n\n{combined_issues}"}
            ]
        }
        openclaw_response = requests.post(OPENCLAW_API_URL, json=payload, headers={"Content-Type": "application/json"})
        github_summary = openclaw_response.json()["choices"][0]["message"]["content"]
        print(f"GitHub Issues Summary:\n{github_summary}")
    else:
        github_summary = "No new or updated GitHub issues found."
    print(f"GitHub Issues Summary:\n{github_summary}")

    Log Analysis

    For logs, I have my server logs (e.g., Nginx access logs, application error logs) rotated and compressed daily. My script reads the previous day’s log file (e.g., /var/log/nginx/access.log.1.gz), decompresses it, and extracts relevant lines (e.g., lines containing “ERROR”, “CRITICAL”, “500”). I then feed these filtered lines to OpenClaw:

    import gzip

  • OpenClaw File System Access: How to Let Your AI Read and Write Your Files Safely

    If you’re running OpenClaw and trying to get your AI agents to interact with files on your server – whether it’s reading logs, writing reports, or processing data – you’ve likely hit a wall. By default, OpenClaw agents are sandboxed for security reasons, meaning they can’t just waltz into your file system. This is a good thing for preventing accidental data loss or malicious actions, but it also makes many practical automation tasks impossible. Here’s how to safely grant your OpenClaw agents file system access on a Linux-based server, focusing on a scenario where you want an agent to read configuration files from a specific directory and write output to another.

    Understanding OpenClaw’s Sandbox and Permissions

    OpenClaw agents, by design, operate within a constrained environment. When an agent requests a file operation (like read_file or write_file), the OpenClaw runtime intercepts this. It doesn’t directly map to a system call. Instead, it checks against an internal permissions manifest defined in your OpenClaw configuration. If the requested path isn’t explicitly allowed, the operation fails with a permission denied error, which often manifests as an “Agent Error: Permission denied for file operation” in your OpenClaw logs.

    The key to enabling file access is to configure these permissions in your .openclaw/config.json file. You define specific directories or files that agents are allowed to interact with, and crucially, what type of interaction (read, write, or both) is permitted. This granular control is essential for maintaining security, as you want to avoid granting blanket access to your entire file system.

    Configuring File System Access in OpenClaw

    Let’s say you want your OpenClaw agent to read configuration files located in /opt/myapp/configs/ and write its processed output to /var/lib/openclaw_data/output/. You’ll need to modify your .openclaw/config.json. If you don’t have this file, create it in your OpenClaw working directory.

    First, ensure the directories actually exist on your file system and have the correct owner and permissions. For instance:

    sudo mkdir -p /opt/myapp/configs
    sudo mkdir -p /var/lib/openclaw_data/output
    sudo chown -R openclaw_user:openclaw_group /var/lib/openclaw_data/output
    sudo chmod -R 750 /var/lib/openclaw_data/output
    

    Replace openclaw_user and openclaw_group with the actual user and group under which your OpenClaw process runs. This is critical; if OpenClaw doesn’t have the underlying OS permissions, its internal configuration won’t matter.

    Now, add the following to your .openclaw/config.json:

    {
      "file_permissions": {
        "/opt/myapp/configs/": {
          "read": true,
          "write": false
        },
        "/var/lib/openclaw_data/output/": {
          "read": false,
          "write": true
        }
      },
      "agent_defaults": {
        "model": "claude-haiku-4-5"
      }
    }
    

    In this snippet, we’ve defined two distinct permission sets. The /opt/myapp/configs/ directory is marked as “read”: true, allowing agents to read any file within it. Crucially, “write”: false prevents accidental modification or deletion. Conversely, /var/lib/openclaw_data/output/ is “write”: true, enabling agents to create or modify files there, but “read”: false prevents them from reading potentially sensitive output from previous runs or other agents. This separation of concerns is a powerful security practice.

    Non-Obvious Insight: Trailing Slashes and Subdirectories

    One common pitfall is the use of trailing slashes. When you define a path like "/opt/myapp/configs/" in file_permissions, OpenClaw interprets this as granting access to all files and subdirectories within that path. If you omit the trailing slash, e.g., "/opt/myapp/configs", OpenClaw treats it as a specific file named “configs”. This distinction is subtle but important. For directory access, always include the trailing slash. If you only want to grant access to a single file, say /etc/myapp.conf, then define it explicitly:

    {
      "file_permissions": {
        "/etc/myapp.conf": {
          "read": true,
          "write": false
        }
      }
    }
    

    Another insight: while the OpenClaw documentation might suggest using larger, more capable models for agents, for many file processing tasks, especially those involving structured data or simple parsing, a smaller model like claude-haiku-4-5 is often sufficient. It’s significantly cheaper than models like claude-opus-20240229 and can easily handle tasks like extracting data from a log file or formatting text for output. Only upgrade to a larger model if you encounter persistent issues with parsing complex, unstructured data or require advanced reasoning capabilities.

    Limitations and Security Considerations

    This approach to file system access has limitations. Firstly, it relies on the underlying operating system permissions. If the OpenClaw process itself doesn’t have permission to access a directory, no amount of configuration in .openclaw/config.json will grant it. Always double-check your chown and chmod commands.

    Secondly, this method doesn’t provide fine-grained control over individual files within an allowed directory beyond what the OS provides. If an agent has write access to /var/lib/openclaw_data/output/, it can write to any file within that directory, including potentially overwriting files created by other agents or processes. For more complex multi-agent scenarios, consider implementing a custom tool that uses a more robust permission layer or a database for shared data, rather than relying solely on the file system.

    Finally, this setup only works if your OpenClaw instance has enough resources to run the agents and process the files. Reading and writing small configuration files is trivial, but if your agents are processing gigabytes of log data, you’ll need a VPS with sufficient RAM and CPU. A basic Hetzner CX11 (2GB RAM) will likely struggle with very large file operations. For anything substantial, aim for at least a CX21 (4GB RAM) or higher.

    The next step is to ensure your .openclaw/config.json file contains the necessary file_permissions block and is located in the directory where you typically run your openclaw command, then restart your OpenClaw instance to apply the changes.