Documentation Index
Fetch the complete documentation index at: https://docs.nanoclaw.dev/llms.txt
Use this file to discover all available pages before exploring further.
In v2, NanoClaw uses a new entity model that separates agent groups (workspaces) from messaging groups (platform chats). These are connected through wirings — many-to-many relationships stored in messaging_group_agents.
Entity model
Agent groups
Agent groups are workspaces where agents run:
interface AgentGroup {
id: string; // Unique identifier
name: string; // Display name
folder: string; // Filesystem folder name
agent_provider?: string; // Optional provider override
created_at: string; // ISO timestamp
}
- Each agent group has a folder under
groups/{folder}/
- Container configuration lives on disk (
container.json), not in the database
- Each gets its own OneCLI agent identifier for credential scoping
Messaging groups
Messaging groups represent platform chats and channels:
interface MessagingGroup {
id: string; // Unique identifier
channel_type: string; // e.g., 'whatsapp', 'telegram', 'discord'
platform_id: string; // Platform-specific chat ID
name?: string; // Display name
unknown_sender_policy: UnknownSenderPolicy; // 'strict' | 'request_approval' | 'public'
denied_at?: string; // ISO timestamp if denied
}
- Unique on
(channel_type, platform_id)
- Auto-created on first mention or DM
denied_at silently drops future mentions
Wirings (messaging_group_agents)
Wirings connect messaging groups to agent groups:
interface MessagingGroupAgent {
messaging_group_id: string;
agent_group_id: string;
engage_mode: EngageMode; // 'pattern' | 'mention' | 'mention-sticky'
engage_pattern: string; // Regex (e.g., '.' = always match)
sender_scope: SenderScope; // 'all' | 'known'
ignored_message_policy: IgnoredMessagePolicy; // 'drop' | 'accumulate'
session_mode: SessionMode; // 'shared' | 'per-thread' | 'agent-shared'
priority: number; // Evaluation order
}
Users and roles
Users
interface User {
id: string; // Namespaced: 'channelType:handle'
kind: string; // phone, email, discord, telegram, matrix, etc.
created_at: string;
}
User roles
interface UserRole {
user_id: string;
role: 'owner' | 'admin';
agent_group_id?: string; // null = global scope
}
- Owner — always global, full system access
- Admin — global or scoped to a specific agent group
Agent group members
interface AgentGroupMember {
user_id: string;
agent_group_id: string;
}
Members can interact with agents in their assigned group when sender_scope='known' is set on a wiring.
Sessions
interface Session {
id: string;
agent_group_id: string;
messaging_group_id?: string;
thread_id?: string;
status: 'active' | 'closed';
container_status: 'running' | 'idle' | 'stopped';
last_active: string;
created_at: string;
}
Session resolution depends on the wiring’s session_mode:
| Mode | Resolution |
|---|
shared | One session per messaging group (ignores thread) |
per-thread | One session per (messaging group, thread) pair |
agent-shared | One session per agent group (all messaging groups share) |
Database schema
Central database (data/v2.db)
| Table | Purpose |
|---|
agent_groups | Agent workspaces |
messaging_groups | Platform chats/channels |
messaging_group_agents | Wirings with engage/scope/session config |
users | Namespaced platform identifiers |
user_roles | Owner and admin roles |
agent_group_members | Unprivileged membership |
user_dms | Cached DM channel mapping |
sessions | Session status and container tracking |
pending_questions | Interactive question cards |
pending_sender_approvals | Unknown sender approval flow |
Session databases
Each session has two databases in data/v2-sessions/{agent_group_id}/{session_id}/:
inbound.db (host writes):
| Table | Purpose |
|---|
messages_in | Inbound messages, tasks, system notifications |
delivered | Delivery tracking |
destinations | Live destination map |
session_routing | Default reply routing |
outbound.db (container writes):
| Table | Purpose |
|---|
messages_out | Outbound messages |
processing_ack | Processing acknowledgments |
session_state | Persistent key/value store |
container_state | Tool-in-flight tracking |
Channel approval flow
When a message arrives on an unwired channel:
- Router detects no wirings exist for this messaging group
- Channel-request gate sends approval card to the owner
- Approve — creates wiring with defaults:
- Groups:
mention-sticky engage mode
- DMs:
pattern='.' (always respond)
- Triggering sender is auto-admitted as a member
- Original event is replayed
- Deny — sets
denied_at on the messaging group
Sender approval flow
When an unknown sender messages on a request_approval channel:
- Approval card sent to the designated approver
- Approve — adds sender to
agent_group_members, replays original message
- Deny — deletes pending row (future messages re-trigger)