OpenClaw Training (I): Growing into a Personal Assistant

OpenClaw is a locally deployable AI assistant framework that supports behavior customization through config files, capability extension via a Skills system, and cross-session persistent memory. This post documents the complete process of configuring it from a default installation into a personal assistant with identity, memory, and the ability to work proactively.


1. Starting from Default

After installation, OpenClaw is essentially a general-purpose AI assistant: it has tool-calling capabilities, can search and execute code, but knows nothing about you, each session is independent, and there’s no extensible business logic.

This default state is fine for light use, but if you want it to genuinely integrate into your workflow, there are four core problems to solve:

  1. It doesn’t know who you are — no background context, you have to re-establish context every time
  2. It has no memory — everything resets when a session ends; next time it has no idea what happened last time
  3. Unclear behavioral boundaries — no rules for what it can do directly vs. what needs confirmation
  4. Fixed capabilities — the default skills set can’t cover all personal use cases

The configuration process below addresses each of these in turn.


2. Identity and Behavioral Guidelines

OpenClaw supports placing Markdown config files in the workspace directory (~/.openclaw/workspace/), which the AI loads on startup. This is the foundation of the entire configuration system.

2.1 SOUL.md — Behavioral Principles

This file defines not style, but baselines.

# SOUL.md

**Be genuinely helpful, not performatively helpful.**
Skip "Sure, I'd be happy to help!" — get straight to the task.

**Have opinions.**
It's okay to disagree with the user's judgment; call out problems directly.

**Be resourceful before asking.**
Read files and look things up first; only ask when you genuinely can't find the answer.

The effect of these rules on an LLM is anchoring style through few-shot examples — not command-and-control, but giving it a “default direction when facing a fork in the road.”

2.2 IDENTITY.md — Identity Definition

# IDENTITY.md

- Name: [give your AI a name]
- Description: [one-line description of its role]
- Emoji: [a representative emoji]

Having a concrete identity improves behavioral consistency across sessions. Giving the model a name isn’t just decoration — it influences how it positions itself and sets its response tone in ambiguous situations.

2.3 USER.md — User Profile

# USER.md

- Name: [your name]
- Timezone: [your timezone]
- Work: [professional background]
- Interests: [areas of interest]
- Reply style: [preferred style]
- Language: [language preference]

This profile is in context every session — no need to re-introduce yourself. The user’s professional background and interests also influence how the AI prioritizes information retrieval and analysis.

2.4 AGENTS.md — Behavioral Guidelines

This is the most critical config file, defining tiered operation permissions:

**No confirmation needed, can execute directly:**
- Read files, browse directories
- Web search, query calendar and email
- File operations within the workspace

**Must confirm before executing:**
- Send emails, messages, social media content
- Delete or overwrite important files
- Any write operations to external systems

Beyond permissions, AGENTS.md also contains memory write rules, group chat behavior guidelines, and heartbeat process documentation — essentially the AI’s job description, re-read at the start of every session.


3. Memory System

This is the most technically involved part of the configuration.

3.1 Why Context Window ≠ Memory

An LLM’s context window is temporary — when conversations get too long, the system auto-compresses (Compaction), discarding early content; everything resets when a session ends. This means:

Session A: Discussed and finalized Plan X → not written to file
Session B: AI has no idea about Plan X → repeat discussion → wasted time

There’s only one solution: write important information to files; files are the physical medium of memory.

Why Markdown over a database:

DimensionMarkdown FilesDatabase
ReadabilityView and edit directly in any text editorRequires query tools
DebuggingOpen the file to see full stateRequires SQL/API
Version controlNative git supportNeeds additional tooling
AI read/writeNative Read/Write tool supportNeeds additional integration

3.2 Three-Layer Architecture

Conversation/event occurs
  │ Written in real time

memory/YYYY-MM-DD.md        ← Daily log (append-only, raw records)
  │ Distilled nightly at 23:45 via cron

memory/knowledge/            ← Structured knowledge base
  ├── lessons/               ← Reusable lessons / pitfall records
  ├── decisions/             ← Important decision records
  └── people/                ← People / project info
  │ Weekly GC archival on Sundays

memory/.archive/             ← Cold storage (not actively loaded)

Two special files:

  • MEMORY.md — Global index, hard-capped at 40 lines, contains only the most critical information, read on every startup.
  • NOW.md — Current state snapshot, overwritten (not appended) on every heartbeat, records current focus and pending items.

3.3 Daily Log Writing

Log files are append-only with a fixed format:

# 2026-03-06 · Thursday

## Event Stream

### 14:30 — Completed Research Report

Research topic X, collected Y sources, conclusion is Z.
Key judgment: chose Plan A over Plan B because...

---

## Takeaways & Reflections

> 

## Tomorrow / Pending

- [ ] 

To prevent the AI from using hallucinated timestamps or accidentally overwriting files with write, a memlog.sh script handles all log appending:

#!/usr/bin/env bash
# Usage: bash memlog.sh "event title" "detailed content"
# Automatically gets system time, appends to memory/YYYY-MM-DD.md

WORKSPACE_DIR="~/.openclaw/workspace"
DAILY_DIR="$WORKSPACE_DIR/memory"
TODAY=$(TZ=Asia/Shanghai date +%Y-%m-%d)
NOW=$(TZ=Asia/Shanghai date +%H:%M)
FILE="$DAILY_DIR/$TODAY.md"

TITLE="${1:?Title required}"
BODY="${2:-}"

# Create from template if file doesn't exist
if [[ ! -f "$FILE" ]]; then
    cat > "$FILE" << EOF
# $TODAY

## Event Stream

---

## Takeaways & Reflections

> 

## Tomorrow / Pending

- [ ] 
EOF
fi

# Insert new event before the "Takeaways" separator (not at end of file)
ENTRY=$(printf "\n### %s — %s\n\n%s\n" "$NOW" "$TITLE" "$BODY")

python3 - "$FILE" "$ENTRY" << 'PYEOF'
import sys
filepath, entry = sys.argv[1], sys.argv[2]
with open(filepath, 'r') as f:
    content = f.read()
marker = "\n---\n\n## Takeaways"
idx = content.find(marker)
content = content[:idx] + entry + content[idx:] if idx != -1 else content + entry
with open(filepath, 'w') as f:
    f.write(content)
PYEOF

Write prohibitions, enforced via AGENTS.md:

ProhibitedReason
❌ Use write to overwrite memory/ filesOverwriting = data loss (NOW.md is the only exception)
❌ Write to knowledge files without reading firstCreates duplicate entries and contradictions
❌ Hardcode timestampsLLMs can hallucinate times; always get system time from the script
❌ Write records with no substantive contentWastes retrieval space, degrades signal-to-noise ratio

3.4 Knowledge Base Standards

Files under lessons/, decisions/, and people/ must include YAML frontmatter:

---
title: "ClawHub Publishing Process Notes"
date: 2026-02-27
category: lessons
priority: 🔴         # 🔴 Core, never archive | 🟡 Important | ⚪ Reference
status: active        # active | superseded | conflict
last_verified: 2026-03-01
tags: [clawhub, cli, deployment]
---

INDEX.md is the navigation hub — the AI scans it on startup to decide which files to load:

# Memory Vault Index

## Lessons

| File | Priority | Status | Last Verified | Notes |
|------|----------|--------|---------------|-------|
| [[clawhub-publish]] | 🟡 | ✅ active | 2026-02-27 | Publishing pitfalls |
| [[network-constraints]] | ⚪ | ⚠️ stale | 2026-02-27 | Sandbox network limits |

## Decisions

| File | Date | Notes |
|------|------|-------|
| [[2026-02-27-memory-architecture]] | 2026-02-27 | Memory system architecture decision |

⚠️ stale means unverified for over 30 days — content may be outdated. 🔴 priority files are never archived.

3.5 Pre-Write CRUD Validation

Before writing to a knowledge file, a “read before write” check must be performed:

Preparing to write lessons/xxx.md

├─ Step 1: Read current content of target file (create if doesn't exist)
├─ Step 2: Compare new knowledge with existing content
│  ├─ Existing content fully covers new content    → NOOP (don't write)
│  ├─ New content updates old content              → UPDATE (mark old as ~~Superseded~~)
│  ├─ The two contradict each other                → CONFLICT (keep both, add ⚠️, status: conflict)
│  └─ Entirely new content                         → ADD (append new section)
└─ Step 3: Update frontmatter's last_verified date

Conflict resolution principle: expose conflicts rather than silently overwrite. When contradictions are found, keep both versions and wait for human judgment before reverting to active.


4. Heartbeat and Scheduled Tasks

4.1 Two Mechanisms Compared

OpenClaw provides two automatic task mechanisms:

MechanismUse Case
HeartbeatMultiple periodic checks can be batched together (email + calendar + status update); timing precision not critical
CronRequires precise timing (run exactly at 23:45 every day), or tasks that need to be isolated from the main session

4.2 Heartbeat Configuration

Heartbeat triggers every 60 minutes. HEARTBEAT.md defines the execution checklist for each run:

# HEARTBEAT.md

## Required Every Time

### 1. Overwrite NOW.md

Use the write tool (not edit/append) to overwrite NOW.md with this format:

# NOW.md — Current State Snapshot

_Last updated: YYYY-MM-DD HH:MM (Asia/Shanghai)_

## Current Focus
(One sentence describing what's currently being worked on)

## Recent Events
- HH:MM — Event title (max 5 items, extracted from today's log)

## Pending
- [ ] Incomplete todos

### 2. Check External Service Status
(Configure as needed, e.g. check if a custom API has new instructions)

4.3 Proactive Notification Rules

Not every heartbeat needs to interrupt the user. Trigger conditions are defined in HEARTBEAT.md:

## When to Proactively Reach Out

Trigger contact:
- Important unread email (prioritized by keywords/sender)
- Calendar event within 2 hours
- Found information worth sharing

Don't contact:
- 23:00–08:00 (unless urgent)
- Less than 60 minutes since last contact
- No new information

4.4 Scheduled Task: Nightly Reflection (23:45 Daily)

This is the core integration step of the memory system — essentially converting “workbench records” into “long-term usable knowledge.”

OpenClaw cron config:

{
  "crons": [
    {
      "name": "daily-reflection",
      "schedule": "45 23 * * *",
      "task": "Execute daily reflection: read today's memory/YYYY-MM-DD.md, distill valuable content into corresponding knowledge base files (lessons/decisions/people), update core information in MEMORY.md, clean up noise records."
    }
  ]
}

Reflection process steps:

Step 1: Read today's log (memory/YYYY-MM-DD.md)
Step 2: Identify content worth preserving
  - Pitfalls encountered → lessons/
  - Important choices made → decisions/
  - New information about people → people/
Step 3: Run CRUD validation, write to corresponding files
Step 4: Update MEMORY.md (if core information has changed)
Step 5: Append a summary of this reflection to the end of the log

4.5 Scheduled Task: Weekly Knowledge Distillation (Every Sunday 00:00)

{
  "name": "weekly-knowledge-distill",
  "schedule": "0 0 * * 0",
  "task": "Scan the last 7 days of logs, check knowledge/INDEX.md for stale markers (>30 days unverified), flag expired entries with ⚠️, move old logs past the threshold into memory/.archive/ (preserve 🔴 priority knowledge files)."
}

Archival rules:

File TypeArchive ConditionProtection Rule
Log YYYY-MM-DD.md>30 days + no references in last 7 daysKeep if referenced
decisions/Never archiveStrategic decisions are permanent
lessons/ 🔴Never archiveCore lessons are permanent
people/Never archivePeople info is relationship-critical

.archive/ uses a dot-prefixed directory. OpenClaw’s semantic search engine (QMD) automatically skips directories starting with . during scans — zero-config hot/cold separation.


5. Skills System

Skills are OpenClaw’s capability extension mechanism. Each skill is a directory containing:

  • SKILL.md: Usage instructions that the AI reads when needed
  • scripts/: The actual execution scripts (Node.js, Python, etc.)

The official distribution includes about 50 pre-built skills covering weather, GitHub, email, notes, music, and other common scenarios. Below are the custom additions built on top.

5.1 Search Enhancement: Perplexity

How it works

OpenClaw’s default web_search tool calls the Brave Search API and returns a list of raw results (title + URL + snippet) — the AI then has to decide which ones are worth following up, fetch them, and synthesize the content itself. Perplexity works differently: it’s an AI-native search engine that takes a question and returns a synthesized answer directly, with a list of cited sources. It handles the full search → fetch → distill pipeline on its servers, so what you receive is already a conclusion, not raw results.

A simple analogy: web_search is searching Google. Perplexity is having another AI pre-digest those results before handing them to you.

Use cases in OpenClaw

This skill calls the Perplexity API and is useful in two main scenarios:

  • First step in research tasks: For unfamiliar topics, use Perplexity to build an information overview — much faster than scraping raw search results one by one
  • Fallback when web_fetch is blocked: In sandbox environments, web_fetch is blocked by SSRF policy. Perplexity fetches on its servers, bypassing this constraint
# Single query
node skills/perplexity/scripts/search.mjs "some research topic"

# Batch queries (same API call, saves context)
node skills/perplexity/scripts/search.mjs "question A" "question B" "question C"

Limitations: Perplexity excels at factual and overview-style questions. It’s not suited for cases where you need to access a specific URL or real-time data (e.g. the latest version of a GitHub README). For those, the browser tool is more appropriate.

Using the Perplexity skill requires a PERPLEXITY_API_KEY environment variable.

5.2 Research Learning Workflow: research-learning

The design of this skill draws from Karpathy’s autoresearch project — instead of giving the AI a checklist of steps, it defines an autonomous research protocol: explicit permission boundaries, completion criteria, error-handling rules, and autonomy level, so the AI can run a full research session without supervision.

autoresearch’s approach: give the AI a program.md defining the research strategy (what it can modify, what it can’t, and how to measure success), then let it loop autonomously. The human sleeps and wakes up to results. Their metric is val_bpb (model training loss); this skill’s metric is research note completion quality.

How the workflow runs

Each research session follows a fixed protocol:

Setup: Confirm topic → read the master index _index.md → create directory + index.md → inform the user, then begin.

Permission boundaries:

  • Autonomous: switch sources, decide file count and naming, self-check quality and fix
  • Must ask: topic scope is fundamentally ambiguous, core materials are completely unavailable

Completion criteria (all must be met):

CriterionDescription
index.md exists and is completeIncludes research goal, file map, OpenClaw’s judgment
Zero placeholdersNo TBD / “to be filled” / placeholder text
Clear contextWhy it was done, what problem it solves, what the core insight is
OpenClaw has a judgmentA real evaluation, not a templated summary
git pushedVault is synced

Simplicity Criterion: Good enough is good enough — don’t pad. If 3 files cover the topic, don’t create 6. Deleting redundant content beats adding more. Better to skip a section entirely than fill it with placeholder text.

Core loop:

Gather materials (parallel) → Sufficient?
  → No: switch source, max 3 attempts
  → Yes: write notes → quality check
    → Fail: fix without interrupting the user
    → Pass: git push → notify completion

Async mode: Once the user says “research XX,” OpenClaw goes straight into the loop and notifies when done — no mid-task check-ins. Research sessions can complete in the background while the user is offline.

Vault directory structure

Research output goes into the Learning/ directory of your Obsidian vault, one subdirectory per topic:

Learning/
├── _index.md            ← Status overview of all topics, updated after each session
└── [topic name]/
    ├── index.md         ← Topic entry point (required, should be detailed)
    └── [other files]    ← Count and naming decided autonomously by OpenClaw

_index.md is the global progress table — analogous to the results.tsv in autoresearch that logs every experiment’s outcome:

| Topic | Directory | Status | Completed | Score | Notes |
|-------|-----------|--------|-----------|-------|-------|
| Solo Company × AI Agent | solo-ai-agent | ✅ Done | 2026-03-01 | 4/5 | — |

index.md is the entry point for each topic and should be substantive — covering the research goal, core conclusions, a file map, key data points, and OpenClaw’s overall judgment. Detailed enough that reopening a topic weeks later requires no context recovery.

5.3 Newsletter Archiving: newsletter-archiver

Archives newsletter emails/articles as Obsidian notes — not just saving the original, but including deep analysis.

Note structure:

---
title: "Title"
date: YYYY-MM-DD
source: "Source - Sub-column"
tags: [tag1, tag2]
url: "original link"
---

## Main Content

(Core summary of the original)

---

# AI Analysis

## Key Insights
(1-3 most important judgments)

## Related Context
(Industry/technical background supplemented via web_search)

## What This Means for You
(Specific actionable recommendations based on user's professional background — no generic advice)

---

## Selected Quotes (Bilingual)

> **Original**
> "..."

**Translation**: ...

Execution flow: fetch original → deep analysis → web_search for background → write to newsletter/<source>/YYYY-MM-DD-<slug>.md → memlog records archival log.

5.4 Financial Data

Three skills covering different layers:

akshare-cn-market — A-share data based on the AKShare library:

  • Individual stock K-lines (daily/weekly/monthly)
  • Market indices (CSI 300, ChiNext, etc.)
  • Macro data: GDP, CPI, PMI, M2, China/US treasury yields
# Example: get recent daily K-line for a stock
python3 scripts/akshare_query.py --type kline --code "600XXX" --period daily --limit 30

stock-info-explorer — Yahoo Finance real-time quotes + chart generation:

  • Real-time quotes, 52-week range, volume
  • High-resolution technical charts with overlaid indicators: MA/RSI/MACD/Bollinger/VWAP/ATR

us-stock-analysis — Comprehensive US stock analysis framework:

  • Fundamentals: financial metrics, business quality assessment, valuation analysis
  • Technicals: trend, pattern recognition, support/resistance levels
  • Competitive comparison analysis
  • Generates structured investment reports

Use cases: stock-info-explorer for daily market checks, us-stock-analysis for deep research, akshare-cn-market for macro data.

5.5 Google Workspace: gog

After OAuth authentication is configured, you can directly operate Gmail, Calendar, Drive, Sheets, and Docs:

# Search unread emails from last 7 days
gog gmail search 'is:unread newer_than:7d' --max 10 --json

# Query this week's calendar events
gog calendar events primary \
  --from 2026-03-04 \
  --to 2026-03-08 \
  --json

# Read Google Sheets data
gog sheets get <sheet-id> "Sheet1!A1:D20" --json

# Update Sheets cells
gog sheets update <sheet-id> "Sheet1!A1:B2" \
  --values-json '[["value A","value B"]]' \
  --input USER_ENTERED

Used with Heartbeat: each heartbeat uses gog gmail search to check priority emails, only pushing notifications when important mail arrives.


6. Obsidian Integration

Obsidian serves as the note library. Integration with OpenClaw mainly works in two directions: AI writing notes, and AI retrieving notes.

6.1 Vault Configuration

OpenClaw interacts with Obsidian vaults via obsidian-cli, a command-line tool. OpenClaw will automatically install obsidian-cli the first time it’s needed — no manual setup required. If you prefer to install it manually:

npm install -g obsidian-cli

After installing, set your vault directory as the default:

obsidian-cli set-default /path/to/your/vault

Once configured, all write operations target .md files directly — no Obsidian client needs to be running:

# View current default vault path
obsidian-cli print-default --path-only

# Search note titles
obsidian-cli search "keyword"

# Search note content (returns summaries and line numbers)
obsidian-cli search-content "keyword"

# Move note (also updates all WikiLinks)
obsidian-cli move "old-path/note" "new-path/note"

Writing .md files directly with the write tool is more efficient; obsidian-cli is mainly used for cross-note link maintenance and search.

6.2 Learning Notes Directory Structure

The research-learning skill uses a flexible structure. Each topic gets its own directory, with index.md as the only required file — everything else is created based on the topic’s complexity:

DigitalGarden/
└── Learning/
    ├── _index.md              ← Master progress tracker (status of all topics at a glance)
    └── [topic name]/
        ├── index.md           ← Topic entry point (required — see template below)
        └── [other files]      ← Count and naming decided autonomously by the AI

_index.md is the global status table, updated after each research session:

# Learning Index

| Topic | Directory | Status | Completed | Score | Notes |
|-------|-----------|--------|-----------|-------|-------|
| Topic A | topic-a | ✅ Done | 2026-03-01 | 4/5 | — |
| Topic B | topic-b | 🔄 In progress | — | — | — |

index.md is the topic entry point — detailed enough that an agent picking it up can immediately understand what coverage exists and what’s left:

---
tags: [learning, topic-tag]
date: YYYY-MM-DD
status: in-progress | complete
research_goal: "One sentence describing the research goal"
---

# [Topic Name] · Index

> One-line definition: what this is and why it's worth researching.

## Research Goal
(What to find out, and what's explicitly out of scope)

## Core Conclusions
(Written after completion — 3-5 sentences of judgment, not summary)

## File Map

| File | Content | Status |
|------|---------|--------|
| index.md | This file | ✅ |
| [filename] | [content description] | ✅ |

## Key Data / Facts
(Most important data points, with source links)

## AI's Overall Judgment
(Not a summary — a real evaluation: how to think about this topic, what's worth acting on)

## Learning Status
- Started: YYYY-MM-DD
- Completed: YYYY-MM-DD
- Next: [next step]

The benefit works in both directions: file count is flexible (a quick overview can be just one file, a deep dive might have ten), but the entry point format is consistent across topics — making cross-topic status comparison effortless.

6.3 Newsletter Archive Structure

DigitalGarden/
└── newsletter/
    └── [source name]/
        └── YYYY-MM-DD-slug.md

Slug rules: lowercase English + hyphens, drop articles (a/the/an), use 3-5 keywords. Example: 2026-03-01-glp1-next-generation-drugs.md.

6.4 Git Sync

Git sync is configured inside Obsidian itself — OpenClaw doesn’t handle this part. The recommended approach is the Obsidian Git community plugin. Here’s the full setup:

Step 1: Initialize the Git repository

In your vault directory, initialize git and connect it to a remote repository (GitHub, GitLab, etc.):

cd /path/to/your/vault
git init
git remote add origin https://github.com/your-username/your-repo.git
git add -A && git commit -m "init vault"
git push -u origin main

Step 2: Install the Obsidian Git plugin

  1. Open Obsidian → Settings → Community Plugins → Browse
  2. Search for Obsidian Git, install and enable it
  3. Open the plugin settings and configure:
    • Auto pull interval: 10 (pull from remote every 10 minutes)
    • Auto push interval (or Auto backup interval): 10 (commit and push every 10 minutes)
    • Commit message: vault backup: {{date}}
    • Pull on startup: Enable (pull the latest version each time Obsidian opens)

Step 3: Verify

Trigger a manual backup: Command Palette (Cmd+P) → search Obsidian Git: Create backup → run it and confirm there are no errors.

Once configured, OpenClaw can write files freely — Obsidian Git will commit and push on its schedule automatically.

Note: If your vault is stored inside iCloud Drive or another cloud sync folder, it’s best to disable iCloud sync for that directory (right-click in Finder → Don’t sync) to avoid conflicts between iCloud and Git.


7. Cursor Agent Integration

Cursor has a little-known CLI subcommand cursor agent that can invoke Cursor Agent in non-interactive mode from the terminal, making it callable by OpenClaw as a coding sub-agent.

7.1 Cursor’s Capability

One line in Cursor’s --help output:

agent    Start the Cursor agent in your terminal.

cursor agent --help shows the full parameter list:

cursor agent [options] [prompt]

Options:
  --print               Non-interactive mode, exits after outputting result (key parameter)
  --workspace <path>    Specify working directory
  --model <model>       Specify model (claude-sonnet-4, gpt-4o, etc.)
  --force               Auto-approve all commands without asking for confirmation

7.2 Usage

Login first:

cursor agent login  # Opens browser for authorization

After login, OpenClaw can call it like this:

# Have Cursor Agent analyze the current project and answer questions
cursor agent --print \
  --workspace /path/to/project \
  --model claude-sonnet-4 \
  "Analyze this project's architecture and identify potential performance bottlenecks"

# Execute a specific coding task (--force skips confirmation)
cursor agent --print \
  --workspace /path/to/project \
  --model claude-sonnet-4 \
  --force \
  "Add a date formatting utility function under src/utils/ with timezone support"

7.3 Position in the Toolchain

OpenClaw (coordination layer)

├─ Lightweight tasks (search, file read/write, calendar/email)
│  └─ Call individual skill tools directly

├─ Complex coding tasks
│  └─ cursor agent --print (has full IDE context, suited for in-codebase operations)

└─ Independent feature development
   └─ sessions_spawn (Claude Code / Codex, suited for brand-new modules)

Cursor Agent’s advantage over other coding agents: it reads the project’s .cursorrules, indexes the entire codebase, understands workspace configuration — for modifications and debugging of existing projects, context quality is higher.

7.4 Known Limitations

In sandbox environments, cursor agent has PTY compatibility issues — the process starts but produces no output, because the sandbox restricts terminal I/O. Solution: ensure cursor agent runs on the host machine, not inside the sandbox.


8. Channel Configuration: Telegram

OpenClaw supports multiple channels (Telegram, Discord, Signal, WhatsApp, etc.), with Telegram currently offering the most complete experience. In March 2026, Telegram Bot API 9.5 opened the sendMessageDraft endpoint to all bots, meaning OpenClaw can now update draft messages in real time as the AI generates — i.e., Streaming Preview.

8.1 Basic Configuration

First create a Bot via BotFather and get the token, then add to your OpenClaw config:

{
  channels: {
    telegram: {
      enabled: true,
      botToken: "YOUR_BOT_TOKEN",
      dmPolicy: "pairing",       // pairing | allowlist | open | disabled
      allowFrom: [123456789],    // Allowed Telegram user numeric IDs
    },
  },
}

After starting the gateway, the first DM requires a pairing flow:

openclaw gateway          # Start
openclaw pairing list telegram
openclaw pairing approve telegram <CODE>

dmPolicy: "pairing" (default) is recommended — after the first pairing, the user is automatically added to the allowlist without needing to configure allowFrom each time.

8.2 Streaming Preview

This is the core advantage of Telegram as the best interface.

OpenClaw has two layers of streaming:

LayerMechanismDescription
Preview streamingchannels.telegram.streamingUpdates message content in real time during generation
Block streamingagents.defaults.blockStreamingDefaultSplits long replies into multiple messages sent progressively

Preview streaming config:

{
  channels: {
    telegram: {
      streaming: "partial",   // off | partial | block | progress
    },
  },
}
  • partial (recommended): Uses sendMessageDraft for in-place updates in DMs; sends a preview message and continuously edits it in groups. No second message after generation completes — clean.
  • block: Updates the preview by appending chunks, suited for very long replies.
  • off: Disables preview; waits for full generation before sending.

In private chats, partial mode delivers an experience close to the streaming effect of Claude Web on desktop: text appears progressively without waiting for the full reply.

Block streaming config (optional):

{
  agents: {
    defaults: {
      blockStreamingDefault: "off",   // Off by default; when enabled, long replies are sent in segments
    },
  },
}

Note: when both Preview and Block streaming are enabled, Telegram automatically skips Preview to avoid double-streaming and prioritizes Block mode. Generally, only enabling Preview streaming is sufficient.

8.3 Access Control

DM permissions:

{
  channels: {
    telegram: {
      dmPolicy: "allowlist",
      allowFrom: [123456789],   // Numeric ID only, @username not supported
    },
  },
}

If you have old entries in @username format, running openclaw doctor --fix will automatically resolve them to numeric IDs.

Group permissions:

{
  channels: {
    telegram: {
      groupPolicy: "allowlist",    // open | allowlist | disabled
      groups: {
        "-1001234567890": {        // Group numeric ID (negative number)
          requireMention: true,    // Whether @bot mention is required to respond
          groupPolicy: "open",     // This group allows all members
        },
        "*": {
          requireMention: true,    // Global default: @mention required
        },
      },
    },
  },
}

To get a group ID: forward a group message to @getidsbot, or read chat.id from openclaw logs --follow.

Group bots have Privacy Mode enabled by default — they can only receive messages that @mention them. To receive all group messages: BotFather → /setprivacy → Disable, then remove the bot from the group and re-add it.

8.4 Inline Buttons and Reactions

Inline Buttons allow the AI to send clickable buttons in replies, useful for confirmation flows and quick selections:

{
  channels: {
    telegram: {
      capabilities: {
        inlineButtons: "allowlist",   // off | dm | group | all | allowlist
      },
    },
  },
}

AI usage example (sending a message with buttons):

{
  action: "send",
  channel: "telegram",
  to: "123456789",
  message: "Confirm this action?",
  buttons: [
    [
      { text: "Confirm", callback_data: "confirm", style: "success" },
      { text: "Cancel", callback_data: "cancel", style: "danger" },
    ],
  ],
}

Reactions config (show Ack emoji during message processing):

{
  channels: {
    telegram: {
      ackReaction: "👀",         // Ack emoji shown while processing
      reactionLevel: "minimal",  // off | ack | minimal | extensive
      reactionNotifications: "own",  // Whether to trigger notification when user reacts to bot message
    },
  },
}

8.5 Custom Command Menu

In Telegram, bots can register a command menu (the / list in the bottom left):

{
  channels: {
    telegram: {
      customCommands: [
        { command: "brief", description: "Daily briefing" },
        { command: "stock", description: "Query stock" },
        { command: "archive", description: "Archive current content" },
      ],
    },
  },
}

Commands are just menu entry points — behavior is determined by the AI based on skills and context; no handler functions need to be implemented.

8.6 Why Telegram Is Currently the Best Interface

Compared to other channels, the combined advantages after Telegram Bot API 9.5:

  • Streaming preview: Native sendMessageDraft support — no flicker, no duplicate messages in DMs
  • Inline Buttons: Enables interactive confirmation flows; other channels have limited support
  • Forum Topics: One bot can maintain independent conversations under different Topics in a supergroup — great for multi-topic management
  • Reactions: Can serve as a lightweight processing status indicator
  • No extra app needed: Mobile Telegram is always available with low latency

The Dashboard (web UI) is suited for extended desktop work sessions; Telegram is better for mobile scenarios and tasks requiring instant notifications. Currently using both, with Telegram as the primary entry point.


9. Multi-Model Switching

OpenClaw supports switching models within the same gateway instance without restarting the service. This is useful across different task scenarios — use a powerful model for writing and research, a fast model for simple Q&A.

9.1 Model Configuration

Set the primary model and fallback chain in the config file:

{
  agents: {
    defaults: {
      model: {
        primary: "openrouter/anthropic/claude-sonnet-4-6",
        fallbacks: [
          "openrouter/google/gemini-2.5-pro",
          "openrouter/deepseek/deepseek-chat",
        ],
      },
    },
  },
}

Fallback chain trigger order: primary model fails (rate limit, outage, timeout) → try fallbacks in sequence.

Advantage of using OpenRouter as a unified gateway: one API key accesses dozens of models without managing separate keys and quotas for each provider.

9.2 Model Aliases

With aliases configured, you don’t need to type full model paths when switching:

{
  agents: {
    defaults: {
      models: {
        "openrouter/anthropic/claude-sonnet-4-6": { alias: "sonnet" },
        "openrouter/google/gemini-2.5-pro":       { alias: "gemini" },
        "openrouter/deepseek/deepseek-chat":       { alias: "deepseek" },
        "openrouter/deepseek/deepseek-reasoner":   { alias: "r1" },
        "minimax/MiniMax-M2.5":                    { alias: "minimax" },
      },
    },
  },
}

CLI alias configuration:

openclaw models aliases add sonnet openrouter/anthropic/claude-sonnet-4-6
openclaw models aliases add r1 openrouter/deepseek/deepseek-reasoner

9.3 Runtime Switching

In a Telegram (or any channel) conversation, send the /model command directly:

/model              → Open model selector (numbered list)
/model list         → List all available models
/model sonnet       → Switch to claude-sonnet-4-6
/model r1           → Switch to DeepSeek R1 (reasoning model)
/model status       → View current model and auth status

Switching only affects the current session — the config file is not modified. Starting a new session with /new restores the default model.

9.4 Model Selection by Task Type

Experience from actual use:

Task TypeRecommended ModelReason
Daily Q&A, writingClaude Sonnet (default)Best balance of speed and quality
Long document processingGemini 2.5 Pro2M token context window
Code tasksClaude Sonnet / Cursor AgentCursor has project context advantage
Quick search / simple answersDeepSeek V3Fast, low cost

Reasoning models (R1, etc.) are suited for tasks requiring step-by-step analysis, but response times are noticeably longer — no need to use them for regular conversation.

A practical pattern: run Sonnet once, let cheaper models follow the playbook. For repetitive flows like scheduled tasks and heartbeats, run through the process once with claude-sonnet-4-6 and have it document its reasoning, steps, and edge cases into the skill’s SKILL.md. Then switch those tasks to MiniMax or DeepSeek V3 as the default model — with a detailed skill document to reference, they perform close to Sonnet at a fraction of the cost. Essentially, one Sonnet run buys quality assurance for all the cheap runs that follow.

9.5 Model Failover

OpenClaw has built-in provider-level automatic failover:

graph LR
    A[Primary model request] --> B{Rate limited 429?}
    B -- No --> Z[Return result]
    B -- Yes --> C[Switch to Fallback 1]
    C --> D{Rate limited 429?}
    D -- No --> Z
    D -- Yes --> E[Switch to Fallback 2]
    E --> F{Failed?}
    F -- No --> Z
    F -- Yes --> G[Return error]

This is useful for availability-critical scenarios — for example, the nightly reflection cron doesn’t need to worry about a provider temporarily rate-limiting and causing the task to fail.

9.6 Multi-Provider Authentication

Current providers and their key configuration:

ProviderEnvironment VariableNotes
OpenRouterOPENROUTER_API_KEYUnified gateway, recommended
AnthropicANTHROPIC_API_KEYDirect connection, no OpenRouter needed
GoogleGEMINI_API_KEYGemini series
DeepSeekDEEPSEEK_API_KEYDirect connection, low cost

Key rotation support: for providers with multiple keys, configure OPENROUTER_API_KEYS (comma-separated) for automatic rotation on rate limits — no manual intervention needed.


10. macOS Security Configuration

Running OpenClaw on a Mac means it continuously has network access, file read/write permissions, and the ability to execute local CLI tools in the background. After completing the core functional configuration, it’s worth doing a security audit of the host machine.

10.1 Port Exposure Check

First, confirm which ports are listening for external connections:

# View all listening TCP/UDP ports and their associated processes
sudo /usr/sbin/lsof -iTCP -sTCP:LISTEN -n -P
sudo /usr/sbin/lsof -iUDP -n -P

Note: macOS’s default PATH doesn’t include /usr/sbin — use the full path /usr/sbin/lsof, otherwise the command won’t be found.

Interpreting typical output:

PortCommon SourceRisk Assessment
5900Screen Sharing / VNC client (e.g. UURemote)Confirm password protection and LAN-only access
22SSH (system remote login)Close if remote SSH isn’t needed
8080OrbStack / container managementNormal dev tool, low risk
3000–9000Local development/debug processesNormal — just confirm it’s your own project

Port 5900 is a common false alarm — many third-party remote desktop tools (UURemote, etc.) reuse the VNC port but aren’t bare VNC. As long as the tool itself has password protection, the risk is manageable.

10.2 Firewall Configuration

macOS has a built-in application firewall, but it may not be enabled by default after installation:

Check firewall status:

/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
# Output: Firewall is enabled. (State = 1) means it's on

Check Stealth Mode:

/usr/libexec/ApplicationFirewall/socketfilterfw --getstealthmode
# Output: Stealth mode enabled means it's on

Enable Stealth Mode:

sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode on

Or via GUI: System Settings → Network → Firewall → Options → Enable Stealth Mode

What Stealth Mode does: normally, a Mac responds to external pings and port probes (even with “this port is closed”). With Stealth Mode on, it silently drops packets without responding, making automated scanners unable to determine whether the IP is online. No impact on normal usage — only affects passive probing behavior.

View firewall application allowlist:

/usr/libexec/ApplicationFirewall/socketfilterfw --listapps

Check whether the allowlist contains apps that have been uninstalled or are no longer in use but still have allow rules. TeamViewer is a classic example — firewall rules may persist after uninstallation and need to be manually cleaned up.

10.3 OpenClaw Security Audit

OpenClaw has a built-in security audit command that scans skill scripts for suspicious behavior:

openclaw security audit

The audit checks:

  • Environment variable access in skill scripts (to prevent credential harvesting)
  • Outbound network requests (especially patterns of reading sensitive env vars immediately followed by network requests)
  • Validity of denyCommands in the config file

Actual output format (0 critical is the target state):

OpenClaw security audit
Summary: 0 critical · 2 warn · 1 info
Run deeper: openclaw security audit --deep

WARN
gateway.trusted_proxies_missing Reverse proxy headers are not trusted
  gateway.bind is loopback and gateway.trustedProxies is empty. If you expose
  the Control UI through a reverse proxy, configure trusted proxies so
  local-client checks cannot be spoofed.
  Fix: Set gateway.trustedProxies to your proxy IPs or keep the Control UI local-only.
gateway.nodes.deny_commands_ineffective Some gateway.nodes.denyCommands entries are ineffective
  gateway.nodes.denyCommands uses exact node command-name matching only
  (for example `system.run`), not shell-text filtering inside a command payload.
- Unknown command names: camera.snap, camera.clip, screen.record, calendar.add
  Fix: Use exact command names (for example: canvas.present, canvas.hide).

INFO
summary.attack_surface Attack surface summary
  groups: open=0, allowlist=2
tools.elevated: enabled
hooks.webhooks: disabled
hooks.internal: enabled
browser control: enabled
trust model: personal assistant (one trusted operator boundary)

How to handle the two common WARN items:

  • gateway.trusted_proxies_missing: Safe to ignore if you’re accessing the Control UI locally without a reverse proxy
  • gateway.nodes.deny_commands_ineffective: denyCommands only does exact command name matching — the listed unknown command names mean those entries don’t exist in the default command set; update them to correct command IDs (e.g. canvas.present)

For a deeper scan, add the --deep flag:

openclaw security audit --deep

10.4 SSH Management

If you don’t need to SSH into this Mac from outside, disabling the remote login service reduces the attack surface:

GUI: System Settings → General → Sharing → Disable “Remote Login”

Command line:

# Stop SSH service
sudo systemsetup -setremotelogin off

# Confirm status
sudo systemsetup -getremotelogin

If SSH access is genuinely needed, recommendations:

  • Disable password login, allow only key-based authentication (PasswordAuthentication no)
  • Change the default port (reduces automated scan hit rate)
  • Consider a zero-trust solution like Tailscale to avoid directly exposing port 22

10.5 Remote Access Tool Cleanup

Remote desktop tools (TeamViewer, UU Remote, Sunflower, AnyDesk, etc.) share these characteristics: persistent background processes, auto-start on boot, continuously listening on ports. Even when not actively in use, having them installed is a potential attack surface.

If you have TeamViewer or UU Remote installed, there are two things to specifically check:

  1. Whether auto-connect is enabled — many tools enable unattended access by default, meaning someone can remote in without any confirmation on your end. Verify this option is disabled, or that the access password is a strong one you set yourself rather than a weak auto-generated one.
  2. That it’s logged into your own account — confirm the tool is bound to your account and hasn’t been associated with someone else’s. For TeamViewer specifically, check the “Trusted Devices” list and remove any devices you don’t recognize.

Cleanup recommendations:

  1. If you only use it occasionally, launch it when needed and quit the background process when done
  2. If you no longer need it at all, delete it from /Applications and clean up the firewall rules
  3. Keep at most one — don’t have multiple overlapping tools

Check whether a tool is still running:

# Using UURemote as an example
pgrep -l UURemote
# or
ps aux | grep -i uuremote

10.6 Scheduled Security Checks

Consider adding a security check to OpenClaw’s cron tasks, running once a week:

{
  "name": "weekly-security-check",
  "schedule": "0 10 * * 1",
  "task": "Run security check: execute openclaw security audit, compare listening ports against last result, push Telegram alert if any new issues or unknown ports are found. Log results to memory/YYYY-MM-DD.md."
}

No need to manually remember to check — you only get notified when something changes; otherwise it runs silently.


11. Complete Directory Structure

~/.openclaw/workspace/

├── AGENTS.md          # Behavioral guidelines (read on startup)
├── SOUL.md            # Behavioral principles
├── IDENTITY.md        # Identity definition
├── USER.md            # User profile
├── HEARTBEAT.md       # Heartbeat execution checklist
├── MEMORY.md          # Global memory index (≤40 lines)
├── NOW.md             # Current state (overwritten each heartbeat)

├── memory/
│   ├── 2026-03-06.md              # Today's log
│   ├── heartbeat-state.json       # Heartbeat state tracking
│   ├── projects.md                # Project progress
│   ├── infra.md                   # Infrastructure configuration
│   ├── knowledge/
│   │   ├── INDEX.md               # Knowledge navigation
│   │   ├── lessons/               # Lessons / pitfalls
│   │   ├── decisions/             # Important decisions
│   │   └── people/                # People information
│   └── .archive/                  # Cold storage

├── scripts/
│   └── memlog.sh                  # Log append script

└── skills/
    ├── gog/                       # Google Workspace
    ├── perplexity/                # AI search
    ├── research-learning/         # Research learning workflow
    ├── newsletter-archiver/       # Newsletter archiving
    ├── akshare-cn-market/         # A-share data
    ├── us-stock-analysis/         # US stock analysis
    ├── stock-info-explorer/       # Real-time quotes + charts
    ├── xmind-generator/           # Mind map generation
    └── s1cli/                     # Forum operations

12. Some Observations

A few things worth noting from the process of building this system.

Configuring AI is a process of self-clarification. Writing USER.md requires being explicit about your work and interests; writing SOUL.md requires thinking through your preferences for collaboration style; designing the knowledge base structure is essentially deciding what information has long-term value to you. These are questions you don’t usually think about explicitly — the configuration process forces them out.

Markdown files as AI memory storage are more stable than expected. Humans can edit them directly, git can version-control them, and debugging means just opening the file to see the full state — this kind of transparency is hard to get with other approaches. The trade-off is retrieval efficiency, but combined with OpenClaw’s semantic search (memory_search), the difference is barely noticeable in practice.

The skill iteration feedback loop matters. Each skill’s SKILL.md ends with an “Experience Log” section, where improvement notes are appended whenever a problem is found. These notes aren’t for humans — they’re for the AI to reference the next time it executes a similar task. It’s essentially delegating the iteration of operational SOPs to the system itself.

Comments