You can override default paths using environment variables. This is useful for portable installations, containerized deployments, or running picoclaw as a system service. These variables are independent and control different paths.
| `PICOCLAW_CONFIG` | Overrides the path to the configuration file. This directly tells picoclaw which `config.json` to load, ignoring all other locations. | `~/.picoclaw/config.json` |
| `PICOCLAW_HOME` | Overrides the root directory for picoclaw data. This changes the default location of the `workspace` and other data directories. | `~/.picoclaw` |
**Examples:**
```bash
# Run picoclaw using a specific config file
# The workspace path will be read from within that config file
> **Note:** Changes to `AGENT.md`, `SOUL.md`, `USER.md` and `memory/MEMORY.md` are automatically detected at runtime via file modification time (mtime) tracking. You do **not** need to restart the gateway after editing these files — the agent picks up the new content on the next request.
**picoclaw-launcher** serves a browser UI that requires sign-in first. By default, the **dashboard token** and **session signing key** are **generated in memory on each start** (a new random token after every restart). Set **`PICOCLAW_LAUNCHER_TOKEN`** to pin a fixed token for that process (startup logs do not print the secret when this env var is used).
**Where to read the token**: In **console mode** (`-console`), it is printed at startup. In **tray / GUI mode**, use the tray action **Copy dashboard token**, and check **`$PICOCLAW_HOME/logs/launcher.log`** (typically `~/.picoclaw/logs/launcher.log` if `PICOCLAW_HOME` is unset) for the random token logged on startup. The login page shows hints that match how the launcher is running (including the absolute log path); **responses do not include the token itself**.
- **Config file**: Same directory as `config.json` (or the file pointed to by `PICOCLAW_CONFIG`). The launcher-specific file is `launcher-config.json`.
- **Sign-in and links**: Enter the token on the login page, or open with `?token=` when the browser is launched automatically. All responses include **`Referrer-Policy: no-referrer`** to reduce leakage of `token` via the `Referer` header.
- **Sign-out**: Use **`POST /api/auth/logout`** with **`Content-Type: application/json`** (body may be `{}`). Do not rely on a GET URL for logout (CSRF-safe pattern).
- **Brute-force**: **`POST /api/auth/login`** is **rate-limited per client IP per minute** (HTTP 429 when exceeded).
- **Session lifetime**: The HttpOnly session cookie lasts about **7 days** by default; sign in again with the token after it expires.
- Generic slash commands are executed through a single path in `pkg/agent/loop.go` via `commands.Executor`.
- Channel adapters no longer consume generic commands locally; they forward inbound text to the bus/agent path. Telegram still auto-registers supported commands at startup.
- Unknown slash command (for example `/foo`) passes through to normal LLM processing.
- Registered but unsupported command on the current channel (for example `/show` on WhatsApp) returns an explicit user-facing error and stops further processing.
If a binding points to a missing `agent_id`, PicoClaw falls back to the default agent.
#### How matching works (step-by-step)
1. PicoClaw first filters bindings by `match.channel` (must equal current channel).
2. It then filters by `match.account_id`:
- omitted: match only the channel's default account
-`"*"`: match all accounts on this channel
- explicit value: exact account id match (case-insensitive)
3. From the remaining candidates, it applies the priority chain above and stops at the first hit.
In other words: **channel + account form the candidate set; peer/guild/team then decide final winner**.
#### Common recipes
**1) Route one specific DM user to a specialist agent**
```json
{
"agent_id": "support",
"match": {
"channel": "telegram",
"account_id": "*",
"peer": { "kind": "direct", "id": "user123" }
}
}
```
**2) Route one Discord server (guild) to a dedicated agent**
```json
{
"agent_id": "sales",
"match": {
"channel": "discord",
"account_id": "my-discord-bot",
"guild_id": "987654321"
}
}
```
**3) Route all remaining traffic of a channel to a fallback agent**
```json
{
"agent_id": "main",
"match": {
"channel": "discord",
"account_id": "*"
}
}
```
#### Authoring guidelines (important)
- Keep exactly one clear default agent in `agents.list` (`"default": true`).
- Put specific rules (`peer`, `guild_id`, `team_id`) and broad rules (`account_id: "*"` only) together safely; priority already guarantees specific rules win.
- Avoid duplicate rules with the same specificity and match values. If duplicates exist, the first matching entry in the config array wins.
- Ensure every `agent_id` exists in `agents.list`; unknown IDs silently fall back to default.
#### Troubleshooting checklist
- **Rule not taking effect?** Check `match.channel` spelling first (must be exact).
- **Expected account-specific routing but still using default?** Verify `match.account_id` equals actual runtime account id.
- **Wildcard catches too much traffic?** Add more specific `peer/guild/team` rules for critical paths.
- **Unexpected default fallback?** Confirm `agent_id` exists and is not misspelled.
You can restrict an individual agent to a subset of runtime tools with `agents.list[].tools`.
If `tools` is omitted, the agent gets the normal globally enabled tool set. If `tools` is present, PicoClaw registers only the listed tools for that agent.
- Tool names are matched against the runtime tool name 1:1.
- Use runtime tool names such as `web_search`, `web_fetch`, `spawn`, `subagent`, `send_file`.
- The `available_tools` field in Agent Discovery reflects the filtered runtime result.
### Agent Discovery (Automatic)
When more than one agent exists, PicoClaw injects a structured agent registry into each agent's system prompt on every turn. No extra `list_agents` tool call is required.
This registry is intended to make delegation concrete and reliable, especially when using `spawn` with a target `agent_id`.
Each entry includes:
| Field | Meaning |
|-------|---------|
| `id` | Stable agent id |
| `name` | Human-friendly agent name |
| `description` | Short capability summary |
| `model` | Current model used by that agent |
| `available_tools` | Tool names currently visible to that agent |
| `channels` | Channels that route to that agent |
Important behavior:
- The discovery section includes the current agent's own entry, so the model has self-awareness.
-`available_tools` is the most important field for delegation. It reflects the tools the target agent can actually use, not just a natural-language description.
-`description` is sourced from `AGENT.md` frontmatter `description` when available, otherwise from the first meaningful paragraph of `AGENT.md`, and finally `SOUL.md`.
-`name` comes from `agents.list[].name` first, then `AGENT.md` frontmatter `name`, then falls back to the agent id.
-`channels` come from routing state:
- the default agent exposes enabled channels
- other agents expose channels that explicitly bind to them through `bindings`
Example injected shape:
```json
{
"current_agent_id": "main",
"agents": [
{
"id": "main",
"name": "Main Assistant",
"description": "Generalist agent for day-to-day requests.",
In practice, this means a generalist agent can see that a peer has `["web_search", "web_fetch"]` while it only has local file tools, and can decide to delegate to that peer instead of guessing.
> **Security Note:** Symlink protection is enabled by default — all file paths are resolved through `filepath.EvalSymlinks` before whitelist matching, preventing symlink escape attacks.
#### Known Limitation: Child Processes From Build Tools
The exec safety guard only inspects the command line PicoClaw launches directly. It does not recursively inspect child
processes spawned by allowed developer tools such as `make`, `go run`, `cargo`, `npm run`, or custom build scripts.
That means a top-level command can still compile or launch other binaries after it passes the initial guard check. In
practice, treat build scripts, Makefiles, package scripts, and generated binaries as executable code that needs the same
level of review as a direct shell command.
For higher-risk environments:
* Review build scripts before execution.
* Prefer approval/manual review for compile-and-run workflows.
* Run PicoClaw inside a container or VM if you need stronger isolation than the built-in guard provides.
#### Error Examples
```
[ERROR] tool: Tool execution failed
{tool=exec, error=Command blocked by safety guard (path outside working dir)}
```
```
[ERROR] tool: Tool execution failed
{tool=exec, error=Command blocked by safety guard (dangerous pattern detected)}
```
#### Disabling Restrictions (Security Risk)
If you need the agent to access paths outside the workspace:
*`PICOCLAW_HEARTBEAT_INTERVAL=60` to change interval
### Providers
> [!NOTE]
> Groq provides free voice transcription via Whisper. If configured, audio messages from any channel will be automatically transcribed at the agent level.
> **What's New?** PicoClaw now uses a **model-centric** configuration approach. Simply specify `vendor/model` format (e.g., `zhipu/glm-4.7`) to add new providers — **zero code changes required!**
This design also enables **multi-agent support** with flexible provider selection:
- **Different agents, different providers**: Each agent can use its own LLM provider
- **Model fallbacks**: Configure primary and fallback models for resilience
- **Load balancing**: Distribute requests across multiple endpoints
- **Centralized configuration**: Manage all providers in one place
> **Security Note**: You can remove `api_key` fields from your config and store them in `.security.yml` instead. See [Security Configuration](#-security-configuration-recommended) above for details.
> **Tip**: You can omit `api_key` fields and store them in `.security.yml` for better security. See [Security Configuration](#-security-configuration-recommended).
PicoClaw strips only the outer `litellm/` prefix before sending the request, so `litellm/lite-gpt4` sends `lite-gpt4`, while `litellm/openai/gpt-4o` sends `openai/gpt-4o`.
</details>
#### Load Balancing
Configure multiple endpoints for the same model name — PicoClaw will automatically round-robin between them:
The old `providers` configuration is **deprecated** but still supported for backward compatibility. See [docs/migration/model-list-migration.md](../migration/model-list-migration.md) for the full guide.
### Provider Architecture
PicoClaw routes providers by protocol family:
- **OpenAI-compatible**: OpenRouter, Groq, Zhipu, vLLM-style endpoints, and most others.
PicoClaw supports cron-style scheduled tasks via the `cron` tool. The agent can set, list, and cancel reminders or recurring jobs that trigger at specified times.
```json
{
"tools": {
"cron": {
"enabled": true,
"exec_timeout_minutes": 5
}
}
}
```
Scheduled tasks persist across restarts and are stored in `~/.picoclaw/workspace/cron/`.