Skip to content

Architecture Overview

Snippbot runs as a single self-hosted daemon on your own infrastructure. The daemon serves the REST API and bundled web UI from one process; everything below is internal to that process.

┌──────────────────────────────────┐
│ Client Interfaces │
│ CLI · Web UI · REST API · WS │
└────────────────┬─────────────────┘
┌────────────────▼─────────────────┐
│ Snippbot Daemon │
│ port 18781 (API + UI) │
│ │
│ Middleware: Auth · CORS · CSRF │
│ Rate Limiting · Security Headers │
└────────────────┬─────────────────┘
┌───────────────────────────┼──────────────────────────┐
│ │ │
┌────────▼───────┐ ┌──────────▼────────┐ ┌──────────▼────────┐
│ Core Engine │ │ Background │ │ Integrations │
│ │ │ Workers │ │ │
│ • Projects │ │ • Scheduler │ │ • Browser (CDP) │
│ • Agents │ │ • Workflow engine │ │ • Sandbox │
│ • Memory │ │ • Sub-agent mgr │ │ (Docker/Podman) │
│ • Security │ │ • Insight queue │ │ • Channels (6) │
│ • Hooks │ │ • Digest engine │ │ • Email (SMTP) │
│ • Config │ │ • MCP servers │ │ • Devices (WS) │
│ • Proactivity │ │ • Thinking engine │ │ • Audio (STT/TTS) │
│ │ │ │ │ • Push (APNs/FCM) │
│ │ │ │ │ • Files / Assets │
└────────┬───────┘ └──────────┬─────────┘ └────────────────────┘
│ │
└───────────────────────────┤
┌────────────────▼─────────────────┐
│ SQLite Databases │
│ ~/.snippbot/*.db │
└──────────────────────────────────┘

Cutting across the system diagram above is one routing layer that every signal passes through before reaching the subsystem that acts on it: the Unified Intent Dispatcher.

Producers Dispatcher Executors
───────── ────────── ─────────
USER_MESSAGE ────► ┌──────────────────┐ ────► TurnOrchestrator
CHANNEL ────► │ Tier 1: hints │ ────► channel_adapter
HOOK ────► │ Tier 2: policy │ ────► DeviceToolRouter
SCHEDULER ────► │ Tier 3: LLM │ ────► InsightQueue
AUTONOMY ────► │ Tier 4: fallback │ ────► schedule_followup
PROACTIVE ────► └──────────────────┘ ────► HookEngine
DEVICE ────► decision log ────► push notifications
API / INTERNAL ────► ────► Suppress (telemetry)

The dispatcher applies user-policy (quiet hours, urgency thresholds, daily caps via ProactivityManager), classifies ambiguous signals with the user’s default internal model, and writes one structured JSONL entry per decision to ~/.snippbot/dispatch/. Every executor in the system diagram above is reachable via the dispatcher.

See Unified Intent Dispatcher for the full design — envelope schema, tier rules, producer table, executor table, decision-log format, and operator controls.

A Snippbot install ships as three things a user interacts with directly:

ComponentWhat it is
Daemon (snippbot)The single ASGI server that hosts the REST API, the web UI, and all background workers. Runs on port 18781.
CLI (snippbot)Command-line interface for installing, starting, stopping, and configuring the daemon, plus utility commands (project, channel, device, dispatcher, secrets, etc.).
Device agent (snippbot-device, optional)Lightweight Python agent installed on remote machines that pair to the daemon over WebSocket to expose local tools (bash, files, system info).

Inside the daemon, the core engine groups the major subsystems shown in the diagram above: projects, agents, memory, scheduler, loops, workflows, sub-agents, hooks, MCP, channels, browser automation, sandboxing, security, and proactivity. The Intent Dispatcher (described below) sits in front of every subsystem.

See Packages for the per-package breakdown.

REST API request:

Client
│ HTTP request + Bearer token
Starlette middleware
│ Auth check (token → user lookup)
│ CORS validation
│ CSRF protection
│ Rate limiting
│ Security headers
Route handler
│ Input validation
│ Permission check
│ Business logic call
Core engine
│ Database read/write
│ Background task dispatch
│ Event bus publish
JSON response

Task execution flow:

POST /api/projects/{id}/approve
project_store.py: status → approved
Background worker picks up project
Permission check: verify agent permissions
executor.py: load agent + tools + memory context
Team orchestration: Architect → Executor → Reviewer
LLM API call (streaming)
Tool calls executed (browser, files, search, sandbox, etc.)
Episode written to memory
Task status → completed / failed
Event bus → hooks, notifications, digest, insights

All components communicate via an in-process pub/sub event bus with 100+ event types:

# Publish an event
await event_bus.publish("task.completed", {"task_id": "...", "status": "completed"})
# Subscribe to events
@event_bus.subscribe("task.*")
async def on_task_event(event_type: str, data: dict):
...

Events are also written to a JSONL log at ~/.snippbot/events.jsonl for replay and debugging.

Snippbot uses multiple SQLite databases, all in ~/.snippbot/:

FilePurpose
snippbot.dbProjects, phases, tasks, agents, conversations, messages
auth.dbUsers, sessions, API keys, refresh tokens
memory.dbEpisodic memory, embeddings, metadata
vector.dbVector embeddings (FTS5 + faiss)
graph.dbKnowledge graph (entities, relations)
scheduler.dbJobs, run history, chains, NL parsing history
workflows.dbWorkflow definitions, versions, templates, runs, step runs
sub_agents.dbSub-agent state, messages, approvals

| settings.db | UI settings (14 categories) | | channels.db | Channel platform credentials, bindings, access | | devices.db | Paired device registry, capabilities, health | | insights.db | Proactive insights, engagement signals | | credentials.db | Encrypted secrets and audit log | | execution.db | Execution jobs, plans, messages, worker state |

Plus additional databases for metrics, sandbox audit, notifications, thinking cycles, and subsystem configuration.