-
Notifications
You must be signed in to change notification settings - Fork 4.4k
02.3 First Steps
Relevant source files
The following files were used as context for generating this wiki page:
This page walks through basic usage patterns after completing installation (2.1) and onboarding (2.2). You will learn how to run single commands, start interactive sessions, launch the gateway server, and test channel communication. For detailed architecture concepts, see Core Architecture. For configuration reference, see Configuration File Reference.
Before proceeding, ensure you have:
- Completed
zeroclaw onboardsuccessfully (see 2.2) - A valid
~/.zeroclaw/config.tomlfile with at least one provider configured - The
zeroclawbinary available in yourPATH
Verify your setup:
zeroclaw statusSources: README.md:226-227
The simplest way to interact with ZeroClaw is single-message mode using the agent subcommand with -m or --message.
zeroclaw agent -m "your prompt here"Optional flags:
-
--provider <name>— Override configured provider (e.g.,anthropic,openai,openrouter) -
--model <id>— Override configured model (e.g.,anthropic/claude-sonnet-4) -
--temperature <float>— Set sampling temperature (default:0.7, range:0.0to2.0) -
--peripheral <board:path>— Attach hardware peripherals (e.g.,nucleo-f401re:/dev/ttyACM0)
Sources: src/main.rs:129-150, README.md:213
flowchart TD
CLI["CLI Entry: main()"]
ParseCmd["Parse Commands::Agent"]
LoadConfig["Config::load_or_init()"]
AgentRun["agent::run()"]
CreateProvider["providers::create_resilient_provider()"]
CreateMemory["memory::create_memory()"]
CreateTools["tools::all_tools_with_runtime()"]
BuildHistory["Build ChatMessage history"]
AgentTurn["agent_turn()"]
RunLoop["run_tool_call_loop()"]
ProviderChat["provider.chat()"]
ParseTools["parse_tool_calls()"]
ExecuteTools["Execute tools via Tool::execute()"]
ProviderResponse["Provider returns final text"]
Output["Print response to stdout"]
CLI --> ParseCmd
ParseCmd --> LoadConfig
LoadConfig --> AgentRun
AgentRun --> CreateProvider
AgentRun --> CreateMemory
AgentRun --> CreateTools
AgentRun --> BuildHistory
AgentRun --> AgentTurn
AgentTurn --> RunLoop
RunLoop --> ProviderChat
ProviderChat --> ParseTools
ParseTools -->|"Has tool_calls"| ExecuteTools
ExecuteTools --> ProviderChat
ParseTools -->|"Text only"| ProviderResponse
ProviderResponse --> Output
Sources: src/main.rs:546-554, src/agent/mod.rs, src/agent/loop_.rs:820-846
zeroclaw agent -m "What is the capital of France?"What happens:
- CLI parses
Commands::Agentvariant src/main.rs:546-554 - Config loads from
~/.zeroclaw/config.tomlsrc/main.rs:540-541 -
agent::run()initializes provider, memory, tools src/agent/mod.rs -
agent_turn()invokes the tool call loop src/agent/loop_.rs:820-846 -
run_tool_call_loop()sends messages to provider src/agent/loop_.rs:851-1044 - Provider returns response (no tool calls for simple query)
- Response prints to stdout
Sources: src/main.rs:546-554, src/agent/loop_.rs:820-846
zeroclaw agent -m "List files in the current directory"What happens:
- Same initialization as above
-
run_tool_call_loop()receives response with tool calls src/agent/loop_.rs:892-937 -
parse_tool_calls()extracts tool name and arguments src/agent/loop_.rs:595-748 -
find_tool()locatesshelltool in registry src/agent/loop_.rs:276-278 -
Tool::execute()runslscommand src/tools/mod.rs - Tool result appends to history src/agent/loop_.rs:1001-1017
- Loop continues until provider returns text-only response
Sources: src/agent/loop_.rs:851-1044, src/agent/loop_.rs:595-748
Omit the -m flag to enter an interactive REPL session.
zeroclaw agentYou will see:
🦀 ZeroClaw Agent (type 'exit' to quit)
>
sequenceDiagram
participant User
participant CLI as "CLI REPL"
participant Agent as "agent::run()"
participant Provider as "Provider"
participant Tools as "Tool Registry"
User->>CLI: zeroclaw agent
CLI->>Agent: Start interactive mode
loop Until 'exit'
Agent->>User: Prompt: "> "
User->>Agent: Enter message
Agent->>Provider: chat(history)
Provider->>Agent: Response + tool_calls
loop While has tool_calls
Agent->>Tools: execute(tool)
Tools->>Agent: ToolResult
Agent->>Provider: chat(history + result)
Provider->>Agent: Response
end
Agent->>User: Print final response
end
User->>CLI: Type 'exit'
CLI->>Agent: Terminate
Sources: src/agent/mod.rs, src/agent/loop_.rs:820-846
Each interactive session maintains:
- History buffer — All messages (user, assistant, tool results) src/agent/loop_.rs:83-92
-
Auto-compaction — When history exceeds
max_history_messages, older messages are summarized src/agent/loop_.rs:158-205 - Memory context — Relevant memories injected into each turn src/agent/loop_.rs:210-233
History compaction is triggered automatically when non-system message count exceeds DEFAULT_MAX_HISTORY_MESSAGES (50). The compaction process:
- Preserves the system prompt (first message if
role=system) - Keeps most recent
COMPACTION_KEEP_RECENT_MESSAGES(20) - Summarizes older messages via LLM src/agent/loop_.rs:158-205
Sources: src/agent/loop_.rs:82-132, src/agent/loop_.rs:158-205
The gateway exposes an HTTP API for webhooks and WebSocket connections.
zeroclaw gatewayDefault behavior:
- Binds to
127.0.0.1:3000(localhost only) - Requires pairing if
gateway.require_pairing = truein config - Displays pairing code on startup if enabled
Sources: README.md:218-220, src/main.rs:556-565
| Flag | Description | Default |
|---|---|---|
--port <n> |
Bind to specific port (0 = random) |
config.gateway.port or 3000
|
--host <addr> |
Bind to specific address |
config.gateway.host or 127.0.0.1
|
Examples:
# Random port (security hardened)
zeroclaw gateway --port 0
# Custom port
zeroclaw gateway --port 8080
# Public bind (requires tunnel or allow_public_bind=true)
zeroclaw gateway --host 0.0.0.0 --port 3000Sources: src/main.rs:556-565, README.md:218-220
flowchart TD
Start["gateway::run_gateway()"]
CheckPublic{"is_public_bind(host)?"}
CheckTunnel{"tunnel.provider != 'none'?"}
CheckAllowPublic{"gateway.allow_public_bind?"}
RefuseStart["bail!('Refusing to bind...')"]
CreateListener["TcpListener::bind(addr)"]
InitProvider["providers::create_resilient_provider()"]
InitMemory["memory::create_memory()"]
InitTools["tools::all_tools_with_runtime()"]
CreatePairing["PairingGuard::new()"]
CreateRateLimiter["GatewayRateLimiter::new()"]
StartTunnel["tunnel.start()"]
BuildRouter["Router::new()"]
ServeAxum["axum::serve()"]
Start --> CheckPublic
CheckPublic -->|Yes| CheckTunnel
CheckPublic -->|No| CreateListener
CheckTunnel -->|Yes| CreateListener
CheckTunnel -->|No| CheckAllowPublic
CheckAllowPublic -->|Yes| CreateListener
CheckAllowPublic -->|No| RefuseStart
CreateListener --> InitProvider
InitProvider --> InitMemory
InitMemory --> InitTools
InitTools --> CreatePairing
CreatePairing --> CreateRateLimiter
CreateRateLimiter --> StartTunnel
StartTunnel --> BuildRouter
BuildRouter --> ServeAxum
Sources: src/gateway/mod.rs:283-505
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/health |
GET | None | Health check (always public) |
/metrics |
GET | None | Prometheus metrics |
/pair |
POST |
X-Pairing-Code header |
Exchange one-time code for bearer token |
/webhook |
POST | Authorization: Bearer <token> |
Send message: {"message": "..."}
|
/whatsapp |
GET | Query params | Meta webhook verification |
/whatsapp |
POST | X-Hub-Signature-256 |
WhatsApp incoming message |
Sources: src/gateway/mod.rs:483-495, README.md:751-759
zeroclaw gatewayOutput:
🦀 ZeroClaw Gateway listening on http://127.0.0.1:3000
POST /pair — pair a new client
POST /webhook — {"message": "your prompt"}
GET /health — health check
🔐 PAIRING REQUIRED — use this one-time code:
┌──────────────┐
│ 123456 │
└──────────────┘
Sources: src/gateway/mod.rs:433-457
curl http://127.0.0.1:3000/healthExpected response:
{
"status": "ok",
"paired": false,
"runtime": {
"gateway": "ok",
"scheduler": null,
"daemon": null
}
}Sources: src/gateway/mod.rs:512-519
curl -X POST http://127.0.0.1:3000/pair \
-H "X-Pairing-Code: 123456"Expected response:
{
"paired": true,
"persisted": true,
"token": "zcl_abc123...",
"message": "Save this token — use it as Authorization: Bearer <token>"
}Sources: src/gateway/mod.rs:545-604
curl -X POST http://127.0.0.1:3000/webhook \
-H "Authorization: Bearer zcl_abc123..." \
-H "Content-Type: application/json" \
-d '{"message": "Hello from webhook"}'Expected response:
{
"response": "Hello! How can I help you?",
"model": "anthropic/claude-sonnet-4"
}Sources: src/gateway/mod.rs:621-804
Channels (Telegram, Discord, Slack) require the daemon to be running.
zeroclaw daemonThis starts:
- Gateway server (webhook endpoint)
- All configured channels (Telegram, Discord, Slack, etc.)
- Scheduler (cron jobs)
- Heartbeat monitor
Sources: README.md:223, src/daemon/mod.rs
flowchart LR
User["User"]
Platform["Messaging Platform<br/>(Telegram Bot API,<br/>Discord Gateway,<br/>Slack HTTP)"]
Channel["Channel Implementation<br/>(TelegramChannel,<br/>DiscordChannel,<br/>SlackChannel)"]
Allowlist["Check allowlist"]
Dispatcher["Message Dispatcher"]
Agent["Agent Core<br/>(agent_turn)"]
Provider["LLM Provider"]
Response["Channel::send()"]
User -->|"Send message"| Platform
Platform -->|"Webhook/WS"| Channel
Channel --> Allowlist
Allowlist -->|"Authorized"| Dispatcher
Dispatcher --> Agent
Agent --> Provider
Provider --> Agent
Agent --> Response
Response --> Platform
Platform --> User
Sources: src/channels/mod.rs, src/agent/loop_.rs:820-846
- Create a Telegram bot via @BotFather
- Get bot token (format:
1234567890:ABCdefGHIjklMNOpqrsTUVwxyz) - Configure in
~/.zeroclaw/config.toml:[channels_config.telegram] bot_token = "1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" allowed_users = [] # Empty = deny all initially
Sources: README.md:407-426, src/channels/telegram.rs
zeroclaw daemonOpen Telegram, search for your bot username, send: /start
Expected response:
I'm sorry, but I cannot respond to your message. Your identity is not in the allowlist.
Operator: run this command to authorize access:
zeroclaw channel bind-telegram 123456789
Sources: src/channels/telegram.rs
zeroclaw channel bind-telegram 123456789This updates config.toml:
[channels_config.telegram]
allowed_users = ["123456789"]Sources: src/channels/telegram.rs, README.md:423-425
The bot will now respond with LLM-generated text.
Sources: src/channels/mod.rs
- Create Discord application at discord.com/developers
- Create bot user and copy token
- Enable "Message Content Intent" in Bot settings
- Configure in
~/.zeroclaw/config.toml:[channels_config.discord] bot_token = "MTIzNDU2Nzg5..." allowed_users = ["your_discord_user_id"]
Sources: README.md:409-410, src/channels/discord.rs
zeroclaw daemonThe bot will respond if your user ID is in allowed_users.
Sources: src/channels/discord.rs
zeroclaw statusOutput includes:
- Version and workspace paths
- Active provider and model
- Autonomy level, runtime mode, memory backend
- Channel configuration status
- Peripheral configuration
Sources: src/main.rs:578-659, README.md:226
zeroclaw doctorChecks:
- Daemon freshness (via
HEARTBEAT.mdtimestamp) - Scheduler activity
- Channel health
Sources: README.md:229-233, src/doctor/mod.rs
zeroclaw channel doctorTests each configured channel:
- Connection status
- Authentication validity
- Basic message send capability
Sources: README.md:233, src/channels/mod.rs
zeroclaw auth statusShows:
- Active auth profiles
- Token expiry times
- Provider authentication state
Sources: README.md:227-228, src/auth/mod.rs
# 1. Single command test
zeroclaw agent -m "What is 2+2?"
# 2. Check status
zeroclaw status
# 3. Interactive session
zeroclaw agent
> Tell me a joke
> exitSources: README.md:213-216
# Terminal 1: Start gateway
zeroclaw gateway
# Terminal 2: Test pairing and webhook
PAIR_CODE="123456" # Copy from Terminal 1 output
TOKEN=$(curl -s -X POST http://127.0.0.1:3000/pair \
-H "X-Pairing-Code: $PAIR_CODE" | jq -r .token)
curl -X POST http://127.0.0.1:3000/webhook \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Hello via webhook"}'Sources: README.md:218-220, src/gateway/mod.rs:545-804
# Start daemon (gateway + channels + scheduler)
zeroclaw daemon
# In another terminal, check health
zeroclaw doctor
zeroclaw channel doctor
# Add a scheduled task
zeroclaw cron add "0 9 * * *" --tz "America/Los_Angeles" \
"Send daily summary to channel"
# Check scheduled tasks
zeroclaw cron listSources: README.md:223, src/daemon/mod.rs, src/cron/mod.rs
- Configuration: Customize providers, models, tools, and security policies (4)
- Architecture: Understand the trait-driven design and subsystem interactions (3)
- Providers: Add or switch LLM providers (5)
- Channels: Configure additional messaging platforms (6)
- Tools: Enable browser automation, Composio integrations, or hardware tools (8)
- Security: Adjust autonomy levels, sandboxing, and rate limits (3.2)
Sources: README.md:169-251, src/main.rs:76-753, src/agent/loop_.rs:820-1044, src/gateway/mod.rs:283-505, src/daemon/mod.rs, src/channels/mod.rs