Introduction
ZeroClaw is an open-source, ultra-lightweight AI assistant framework written in Rust. It is designed for efficiency — it can run on edge devices with minimal memory footprint while supporting multiple messaging channels including Telegram, Discord, Slack, Lark, and more. Lark (a workplace productivity superapp by ByteDance, similar to Slack) provides the messaging layer where users interact with the ZeroClaw agent.
This guide walks through deploying ZeroClaw with Lark channel support on Ubuntu 24.04, covering installation, configuration of LLM providers, Lark bot setup, systemd integration for production, and troubleshooting common issues.
Architecture Overview
ZeroClaw’s architecture separates concerns into distinct components that communicate through well-defined interfaces:
flowchart LR
User[User in Lark Client] -->|Messages via| LarkAPI[Lark API Gateway]
LarkAPI -->|WebSocket/Webhook| Gateway[ZeroClaw Gateway]
Gateway --> Daemon[ZeroClaw Daemon]
Daemon --> Engine[Core Engine]
Engine --> LLM[LLM Provider<br/>e.g. MiniMax-M2.5]
Engine --> Tools[Sandboxed Tools]
Daemon --> Channels[Channel Manager]
Channels --> LarkAPI
style LLM fill:#3b82f6,color:#fff
style Engine fill:#10b981,color:#fff
- Gateway: Exposes a webhook server that receives incoming messages from channel integrations. Handles TLS termination and request routing.
- Daemon: The autonomous runtime that manages heartbeat checks, cron jobs, and channel lifecycle. Runs continuously as a background service.
- Core Engine: Processes messages, invokes the LLM, manages conversation state, and coordinates tool execution.
- Channels: Adapter layer that normalizes messages from different platforms into a common format. Each channel has its own configuration with allowlist-based access control.
- Tools: Sandboxed capabilities the agent can invoke (file access, web search, code execution). Each tool is scoped to a workspace for security.
Installation
Prerequisites
- Ubuntu 24.04 or compatible Linux distribution
- Rust toolchain (for compiling with Lark feature)
- A Lark bot application registered in the Lark Developer Console
- An LLM API key (MiniMax, OpenAI, or Anthropic)
Step 1: Install ZeroClaw
The quickest way to install ZeroClaw is through the official installation script:
curl -fsSL https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/main/scripts/install-release.sh | bash
This script downloads the latest pre-built binary for your architecture and places it in /usr/local/bin/. Verify the installation:
zeroclaw --version
Step 2: Build with Lark Channel Support
The default binary may not include Lark channel support. You need to compile from source with the Lark feature flag enabled:
# Clone the repository (or download the source archive)
git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw
# Build with Lark channel support
cargo build --release --features "channel-lark"
# Find the compiled binary location
which zeroclaw
# Replace the system binary with the Lark-enabled version
cp target/release/zeroclaw /home/username/.cargo/bin/zeroclaw
# Or if installed globally:
sudo cp target/release/zeroclaw /usr/local/bin/zeroclaw
The --features "channel-lark" flag tells the Rust compiler to include the Lark adapter module. Without this flag, the Lark channel configuration section in your config file will be ignored.
Step 3: Initialize the Configuration
ZeroClaw creates its default configuration on first run:
zeroclaw onboard
This generates ~/.zeroclaw/config.toml with default values. You will edit this file in the next steps to add your LLM provider and Lark channel settings.
LLM Provider Configuration
Edit ~/.zeroclaw/config.toml and set your LLM provider. This example uses MiniMax-M2.5:
# ~/.zeroclaw/config.toml — LLM provider settings
api_key = "your-minimax-api-key-here"
default_provider = "minimax-cn"
default_model = "MiniMax-M2.5"
Each field serves a specific purpose:
api_key: The API key for your chosen LLM provider. This is read from the config file at startup and used for all LLM requests. Store it securely — consider using environment variables or a secrets manager rather than hardcoding.default_provider: The provider identifier. ZeroClaw supports multiple providers (OpenAI, Anthropic, MiniMax) and routes requests based on this setting.default_model: The specific model to use. For MiniMax, options include MiniMax-M2.5 (latest), MiniMax-M1, or earlier versions. For OpenAI, this would begpt-4oorgpt-4o-mini.
Lark Channel Configuration
1. Create a Lark Bot
Go to the Lark Developer Console and create a new bot application. After creation, you will receive:
- App ID: Unique identifier for your bot
- App Secret: Secret key for API authentication — treat this like a password
- Verification Token (optional): Used to verify webhook requests originate from Lark
- Encrypt Key (optional): Used to encrypt/decrypt message payloads
2. Configure the Lark Channel in ZeroClaw
Add the following section to ~/.zeroclaw/config.toml:
[channels_config.lark]
app_id = "cli_a5f3b2c1d4e5"
app_secret = "your-app-secret-here"
encrypt_key = "" # optional: set if you enabled encryption in Lark console
verification_token = "" # optional: set if you enabled verification in Lark console
allowed_users = ["*"] # allowlist: "*" = all users, or specify Lark user IDs
mention_only = false # optional: if true, bot only responds to @mentions in groups
use_feishu = false # set true if using Feishu (Chinese version) instead of Lark
receive_mode = "websocket" # "websocket" (recommended) or "webhook"
port = 8081 # only needed for webhook mode
Key configuration decisions:
receive_mode:websocketmode establishes a persistent connection to Lark’s real-time API, eliminating the need for a public-facing webhook endpoint. This is simpler for home servers or NAT’d environments.webhookmode requires a publicly accessible HTTP endpoint and is better for containerized deployments.allowed_users: This is an allowlist — empty by default (deny all). Set to["*"]for development or specify individual Lark user IDs (["user_123", "user_456"]) for production to restrict access.mention_only: Whentrue, the bot only responds when explicitly @mentioned in group chats. Direct messages always work. This prevents the bot from responding to every message in a busy group.use_feishu: Set totrueif your organization uses Feishu (the Chinese mainland version of Lark). The APIs differ slightly between the two platforms.
Production Systemd Service
To ensure ZeroClaw’s Lark channel runs persistently as a background service, create a systemd user service:
# ~/.config/systemd/user/zeroclaw-lark.service
[Unit]
Description=ZeroClaw Lark Channel
After=zeroclaw.service
Requires=zeroclaw.service
[Service]
Type=simple
ExecStart=/home/user/.cargo/bin/zeroclaw channel start
Restart=always
RestartSec=5
Environment="HOME=/home/user"
[Install]
WantedBy=default.target
This service file:
- Declares a dependency on the main
zeroclaw.serviceviaAfterandRequires, ensuring the daemon starts before the Lark channel. - Uses
Restart=alwayswith a 5-second delay so the channel automatically recovers from crashes or transient network failures. - Sets
HOMEexplicitly because systemd services may not inherit the user’s environment.
Enable and start the service:
systemctl --user daemon-reload
systemctl --user enable zeroclaw-lark
systemctl --user restart zeroclaw-lark
Verification and Monitoring
Check Service Status
systemctl --user status zeroclaw-lark
A healthy service shows active (running) with no recent restarts.
Monitor Real-Time Logs
journalctl --user -u zeroclaw-lark -f
Expected Log Output
When the Lark channel connects successfully, you should see:
2026-03-14T04:18:40.545193Z INFO zeroclaw::channels::lark: Lark: WS connected
This confirms ZeroClaw established a WebSocket connection to Lark’s real-time API. If you see connection errors instead, check your app_id and app_secret values in the config file.
Verify Bot Responses
Send a message to your Lark bot. In the journal logs, you should see incoming message events and corresponding LLM responses:
2026-03-14T04:20:15.123456Z INFO zeroclaw::channels::lark: Received message from user_abc123
2026-03-14T04:20:16.789012Z INFO zeroclaw::engine: LLM response generated (latency: 1.2s)
2026-03-14T04:20:16.890123Z INFO zeroclaw::channels::lark: Message sent to user_abc123
Adding the Bot to Lark Messenger
- Open Lark and search for your bot by the name you set in the Developer Console.
- Send a direct message to start a conversation.
- For group chats, add the bot as a member. If
mention_onlyistrue, @mention the bot to trigger a response. - Test with simple requests: “Summarize this article” or “What is the capital of France?”
Troubleshooting
| Symptom | Likely Cause | Solution |
|---|---|---|
WS connected never appears |
Incorrect app_id or app_secret |
Re-check credentials in Lark Developer Console |
| Connection refused on port 8081 | Port conflict or webhook mode without public endpoint | Switch to receive_mode = "websocket" |
| Bot does not respond in groups | mention_only = true |
@mention the bot, or set mention_only = false |
| LLM returns errors | Invalid API key or quota exhausted | Verify api_key and check your LLM provider dashboard |
| Service keeps restarting | Config syntax error | Run zeroclaw channel start manually to see the error |
| Messages received but no response | Model config issue | Check default_provider and default_model match available models |
Multiple LLM Provider Configuration
ZeroClaw supports routing between different LLM providers. Configure fallback and priority:
# ~/.zeroclaw/config.toml — Multi-provider setup
[llm]
default_provider = "minimax-cn"
default_model = "MiniMax-M2.5"
[llm.providers.openai]
api_key = "sk-openai-..."
base_url = "https://api.openai.com/v1"
default_model = "gpt-4o"
[llm.providers.anthropic]
api_key = "sk-ant-..."
default_model = "claude-sonnet-4-20260514"
[llm.providers.minimax]
api_key = "your-minimax-key"
base_url = "https://api.minimaxi.com/v1"
default_model = "MiniMax-M2.5"
# Provider fallback chain
[llm.fallback]
enabled = true
providers = ["minimax-cn", "openai", "anthropic"]
timeout_seconds = 30
The fallback chain ensures high availability — if MiniMax is rate-limited or down, ZeroClaw automatically tries OpenAI, then Anthropic. Each provider’s timeout prevents cascading delays.
Provider-Specific Prompt Templates
[llm.prompts]
default = """
You are a helpful AI assistant running in Lark.
Respond concisely and accurately.
"""
technical = """
You are a technical assistant for developers.
Provide code examples and technical explanations.
Format responses with markdown code blocks.
"""
Multi-Channel Configuration
ZeroClaw supports running multiple channels simultaneously. Each channel has independent configuration:
[channels_config.lark]
app_id = "cli_a5f3b2c1d4e5"
app_secret = "..."
[channels_config.telegram]
bot_token = "123456:ABC-DEF..."
allowed_users = ["user_789"]
[channels_config.discord]
bot_token = "discord_bot_token..."
guild_id = "123456789"
allowed_channels = ["general", "ai-bot"]
This enables a unified agent that responds across Lark, Telegram, and Discord from a single ZeroClaw instance. Each channel can have its own allowlist and configuration.
Prometheus Monitoring Integration
For production monitoring, expose ZeroClaw metrics to Prometheus:
# ~/.zeroclaw/config.toml — Monitoring section
[monitoring]
enable_metrics = true
metrics_port = 9090
enable_tracing = false
# prometheus.yml — scrape config
scrape_configs:
- job_name: 'zeroclaw'
static_configs:
- targets: ['localhost:9090']
metrics_path: '/metrics'
Key metrics exposed:
| Metric | Type | Description |
|---|---|---|
zeroclaw_messages_received_total |
Counter | Total incoming messages |
zeroclaw_messages_processed_total |
Counter | Messages successfully processed |
zeroclaw_llm_latency_seconds |
Histogram | LLM response latency |
zeroclaw_channel_errors_total |
Counter | Channel adapter errors (by channel) |
zeroclaw_tool_executions_total |
Counter | Tool call executions |
zeroclaw_memory_usage_bytes |
Gauge | Current process memory |
zeroclaw_uptime_seconds |
Counter | Service uptime |
Grafana Dashboard
Create a monitoring dashboard with these panels:
{
"title": "ZeroClaw Performance",
"panels": [
{
"title": "Messages Processed",
"type": "stat",
"targets": [{"expr": "rate(zeroclaw_messages_processed_total[5m])"}]
},
{
"title": "LLM Latency p95",
"type": "gauge",
"targets": [{
"expr": "histogram_quantile(0.95, rate(zeroclaw_llm_latency_seconds_bucket[5m]))"
}]
},
{
"title": "Error Rate",
"type": "timeseries",
"targets": [{"expr": "rate(zeroclaw_channel_errors_total[5m])"}]
}
]
}
Alerting Rules
# alerts.yml
groups:
- name: zeroclaw
rules:
- alert: HighErrorRate
expr: rate(zeroclaw_channel_errors_total[5m]) > 0.05
for: 5m
labels: { severity: warning }
annotations:
summary: "ZeroClaw error rate above 5%"
- alert: HighLatency
expr: histogram_quantile(0.95, rate(zeroclaw_llm_latency_seconds_bucket[5m])) > 10
for: 5m
labels: { severity: critical }
annotations:
summary: "LLM p95 latency above 10 seconds"
- alert: ServiceDown
expr: up{job="zeroclaw"} == 0
for: 1m
labels: { severity: critical }
annotations:
summary: "ZeroClaw service is down"
Custom Tool Development
ZeroClaw supports custom tools that the agent can invoke. Tools are defined as WebAssembly (WASM) modules for sandboxed execution:
// example tool: web-search
use zeroclaw_sdk::{Tool, ToolContext, ToolResult};
#[derive(serde::Deserialize)]
struct SearchInput {
query: String,
max_results: Option<u32>,
}
struct WebSearchTool;
#[async_trait]
impl Tool for WebSearchTool {
fn name(&self) -> &str { "web_search" }
fn description(&self) -> &str { "Search the web for current information" }
fn input_schema(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"},
"max_results": {"type": "integer", "default": 5}
},
"required": ["query"]
})
}
async fn execute(&self, input: ToolInput, ctx: ToolContext) -> ToolResult {
let params: SearchInput = serde_json::from_value(input.params)?;
// Implementation: call search API, return formatted results
Ok(ToolResult {
output: serde_json::json!({
"results": [
{"title": "Example", "url": "https://example.com", "snippet": "..."}
]
}),
metadata: None,
})
}
}
Configure custom tool paths:
[tools]
dir = "/etc/zeroclaw/tools"
timeout_seconds = 30
sandbox_enabled = true
[tools.registrations]
web_search = { path = "web-search.wasm", enabled = true }
database_query = { path = "db-query.wasm", enabled = true, allowed_domains = ["localhost"] }
Security Hardening
Network Security
# Restrict ZeroClaw to only necessary outbound connections
sudo ufw allow out to api.larksuite.com port 443
sudo ufw allow out to api.minimaxi.com port 443
sudo ufw deny out from any to any # Default deny all other outbound
# Create dedicated system user for ZeroClaw
sudo useradd -r -s /bin/false -m -d /var/lib/zeroclaw zeroclaw
sudo chown -R zeroclaw:zeroclaw /etc/zeroclaw /var/lib/zeroclaw
Environment Variables for Secrets
Instead of hardcoding secrets in config.toml, use environment variables:
# ~/.zeroclaw/.env
export ZEROCLAW_LARK_APP_SECRET="your-secret-here"
export ZEROCLAW_OPENAI_API_KEY="sk-..."
export ZEROCLAW_MINIMAX_API_KEY="mm-..."
Reference them in config:
# config.toml — using env vars
api_key = "${ZEROCLAW_MINIMAX_API_KEY}"
default_provider = "minimax-cn"
[channels_config.lark]
app_id = "cli_a5f3b2c1d4e5"
app_secret = "${ZEROCLAW_LARK_APP_SECRET}"
Rate Limiting Configuration
[rate_limiting]
enabled = true
messages_per_minute = 30
messages_per_hour_per_user = 100
conversation_timeout_minutes = 60
CI/CD Pipeline for Updates
# .github/workflows/deploy-zeroclaw.yml
name: Deploy ZeroClaw
on:
push:
branches: [main]
paths:
- 'Cargo.toml'
- 'src/**'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build with Lark support
run: |
cargo build --release --features "channel-lark"
- name: Deploy to production
run: |
scp target/release/zeroclaw deploy@server:/tmp/
ssh deploy@server '
sudo systemctl stop zeroclaw-lark
sudo cp /tmp/zeroclaw /usr/local/bin/zeroclaw
sudo systemctl start zeroclaw-lark
sudo journalctl -u zeroclaw-lark --no-pager -n 10
'
Performance Tuning
Token Budget Management
[performance]
max_context_tokens = 4096 # Truncate conversation history to this limit
max_response_tokens = 1024 # Maximum tokens in LLM response
conversation_ttl_minutes = 30 # Clear inactive conversations
batch_process = false # Enable for high-volume async processing
[performance.rate_limits]
tokens_per_minute = 100000
requests_per_minute = 60
Connection Pooling for Lark WebSocket
[channels_config.lark]
reconnect_interval_seconds = 5
max_reconnect_attempts = 0 # 0 = infinite
heartbeat_interval_seconds = 30
message_queue_size = 1000
Tuning these values based on your workload:
- High-traffic teams: Increase
message_queue_sizeto 5000, reduceheartbeat_intervalto 15s - Low-resource environments: Reduce
max_context_tokensto 2048, increaseconversation_ttlto 120 - Global teams: Increase
reconnect_intervalto 30s to handle network instability
Backup and Recovery
#!/bin/bash
# backup-zeroclaw.sh
BACKUP_DIR="/var/backups/zeroclaw"
DATE=$(date +%Y%m%d)
# Backup configuration
mkdir -p $BACKUP_DIR/$DATE
cp -r /etc/zeroclaw $BACKUP_DIR/$DATE/
# Backup data directory
tar czf $BACKUP_DIR/$DATE/zeroclaw-data.tar.gz /var/lib/zeroclaw/
# Backup systemd user services
cp ~/.config/systemd/user/zeroclaw*.service $BACKUP_DIR/$DATE/
# Rotate backups older than 30 days
find $BACKUP_DIR -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;
echo "Backup complete: $BACKUP_DIR/$DATE"
Why Use ZeroClaw
ZeroClaw is distinguished by its lightweight footprint. The Rust binary is typically under 15 MB and consumes less than 50 MB of RAM at idle. This makes it practical for deployment on Raspberry Pi, edge gateways, or shared servers where resource efficiency matters. The deny-by-default allowlist model for channels provides security by design — no channel is active until explicitly enabled and configured.
Resources
- ZeroClaw GitHub Repository — Source code, issues, and releases
- ZeroClaw Official Site — Documentation and feature overview
- Lark Developer Console — Create and manage Lark bot applications
- Lark Bot Development Guide — Official Lark API documentation
- MiniMax Model Documentation — MiniMax LLM API reference
- Systemd Service Unit Documentation — Reference for service file configuration
Comments