Skip to content

02.3 First Steps

Nikolay Vyahhi edited this page Feb 19, 2026 · 3 revisions

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.


Prerequisites

Before proceeding, ensure you have:

  • Completed zeroclaw onboard successfully (see 2.2)
  • A valid ~/.zeroclaw/config.toml file with at least one provider configured
  • The zeroclaw binary available in your PATH

Verify your setup:

zeroclaw status

Sources: README.md:226-227


Running Your First Agent Command

The simplest way to interact with ZeroClaw is single-message mode using the agent subcommand with -m or --message.

Command Structure

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.0 to 2.0)
  • --peripheral <board:path> — Attach hardware peripherals (e.g., nucleo-f401re:/dev/ttyACM0)

Sources: src/main.rs:129-150, README.md:213

Execution Flow Diagram

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
Loading

Sources: src/main.rs:546-554, src/agent/mod.rs, src/agent/loop_.rs:820-846

Example: Simple Query

zeroclaw agent -m "What is the capital of France?"

What happens:

  1. CLI parses Commands::Agent variant src/main.rs:546-554
  2. Config loads from ~/.zeroclaw/config.toml src/main.rs:540-541
  3. agent::run() initializes provider, memory, tools src/agent/mod.rs
  4. agent_turn() invokes the tool call loop src/agent/loop_.rs:820-846
  5. run_tool_call_loop() sends messages to provider src/agent/loop_.rs:851-1044
  6. Provider returns response (no tool calls for simple query)
  7. Response prints to stdout

Sources: src/main.rs:546-554, src/agent/loop_.rs:820-846

Example: Tool-Calling Query

zeroclaw agent -m "List files in the current directory"

What happens:

  1. Same initialization as above
  2. run_tool_call_loop() receives response with tool calls src/agent/loop_.rs:892-937
  3. parse_tool_calls() extracts tool name and arguments src/agent/loop_.rs:595-748
  4. find_tool() locates shell tool in registry src/agent/loop_.rs:276-278
  5. Tool::execute() runs ls command src/tools/mod.rs
  6. Tool result appends to history src/agent/loop_.rs:1001-1017
  7. Loop continues until provider returns text-only response

Sources: src/agent/loop_.rs:851-1044, src/agent/loop_.rs:595-748


Interactive Agent Mode

Omit the -m flag to enter an interactive REPL session.

Starting Interactive Mode

zeroclaw agent

You will see:

🦀 ZeroClaw Agent (type 'exit' to quit)
> 

Interactive Session Lifecycle

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
Loading

Sources: src/agent/mod.rs, src/agent/loop_.rs:820-846

Session Context

Each interactive session maintains:

History compaction is triggered automatically when non-system message count exceeds DEFAULT_MAX_HISTORY_MESSAGES (50). The compaction process:

  1. Preserves the system prompt (first message if role=system)
  2. Keeps most recent COMPACTION_KEEP_RECENT_MESSAGES (20)
  3. Summarizes older messages via LLM src/agent/loop_.rs:158-205

Sources: src/agent/loop_.rs:82-132, src/agent/loop_.rs:158-205


Starting the Gateway

The gateway exposes an HTTP API for webhooks and WebSocket connections.

Basic Gateway Start

zeroclaw gateway

Default behavior:

  • Binds to 127.0.0.1:3000 (localhost only)
  • Requires pairing if gateway.require_pairing = true in config
  • Displays pairing code on startup if enabled

Sources: README.md:218-220, src/main.rs:556-565

Gateway Command Options

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 3000

Sources: src/main.rs:556-565, README.md:218-220

Gateway Security Enforcement

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
Loading

Sources: src/gateway/mod.rs:283-505

Gateway Endpoints

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

Testing the Gateway

1. Start the gateway

zeroclaw gateway

Output:

🦀 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

2. Health check

curl http://127.0.0.1:3000/health

Expected response:

{
  "status": "ok",
  "paired": false,
  "runtime": {
    "gateway": "ok",
    "scheduler": null,
    "daemon": null
  }
}

Sources: src/gateway/mod.rs:512-519

3. Pairing (if enabled)

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

4. Send webhook message

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


Testing Channel Communication

Channels (Telegram, Discord, Slack) require the daemon to be running.

Starting All Channels

zeroclaw daemon

This 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

Channel Message Flow

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
Loading

Sources: src/channels/mod.rs, src/agent/loop_.rs:820-846

Testing Telegram

Prerequisites

  1. Create a Telegram bot via @BotFather
  2. Get bot token (format: 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz)
  3. 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

Start daemon

zeroclaw daemon

Send test message to your bot

Open 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

Authorize your identity

zeroclaw channel bind-telegram 123456789

This updates config.toml:

[channels_config.telegram]
allowed_users = ["123456789"]

Sources: src/channels/telegram.rs, README.md:423-425

Send another message

The bot will now respond with LLM-generated text.

Sources: src/channels/mod.rs

Testing Discord

Prerequisites

  1. Create Discord application at discord.com/developers
  2. Create bot user and copy token
  3. Enable "Message Content Intent" in Bot settings
  4. 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

Start daemon

zeroclaw daemon

Send DM or mention bot in a channel

The bot will respond if your user ID is in allowed_users.

Sources: src/channels/discord.rs


Basic Diagnostics

Status Check

zeroclaw status

Output 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

Doctor Command

zeroclaw doctor

Checks:

  • Daemon freshness (via HEARTBEAT.md timestamp)
  • Scheduler activity
  • Channel health

Sources: README.md:229-233, src/doctor/mod.rs

Channel Health Check

zeroclaw channel doctor

Tests each configured channel:

  • Connection status
  • Authentication validity
  • Basic message send capability

Sources: README.md:233, src/channels/mod.rs

Auth Status

zeroclaw auth status

Shows:

  • Active auth profiles
  • Token expiry times
  • Provider authentication state

Sources: README.md:227-228, src/auth/mod.rs


Common Workflows

Workflow: Quick Local Test

# 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
> exit

Sources: README.md:213-216

Workflow: Gateway + Webhook

# 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

Workflow: Full Autonomous Runtime

# 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 list

Sources: README.md:223, src/daemon/mod.rs, src/cron/mod.rs


Next Steps

  • 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


Clone this wiki locally