Architecture Explorer
Click on any component in the diagrams below to jump to its detailed documentation. Use the "Zoom In" sections to explore each subsystem in depth.
System Overview
The top-level view of ClawDesk — from external messaging platforms through the gateway to LLM providers.
Crate Dependency Graph
The full 27-crate dependency DAG. Arrows point from dependant → dependency (i.e., "uses").
Reading the DAG
The crate dependency graph follows a strict layering rule:
$$ \text{Layer}(A) > \text{Layer}(B) \Rightarrow A \text{ may depend on } B $$ $$ \text{Layer}(A) \leq \text{Layer}(B) \Rightarrow A \text{ must NOT depend on } B $$
| Layer | Crates | Depends on |
|---|---|---|
| 0 | types | Nothing |
| 1 | storage | Layer 0 |
| 2 | domain | Layers 0–1 |
| 3 | channel, sochdb | Layers 0–2 |
| 4 | channels, agents, skills, memory, security | Layers 0–3 |
| 5 | providers | Layers 0–4 |
| 6 | gateway | Layers 0–5 |
| 7 | acp | Layers 0–6 |
| 8 | cli, tauri | Layers 0–7 |
Message Flow View
The complete data transformation pipeline for a single message.
Zoom In: Type Transformations
The key insight is the funnel pattern:
- 13 shapes in — each platform has unique fields
- 1 shape out —
NormalizedMessageis the universal representation
// 13 platform-specific shapes...
enum InboundMessage {
Telegram(TelegramInbound), // chat_id, reply_to, media_group
Discord(DiscordInbound), // guild_id, channel_id, embeds
Slack(SlackInbound), // team_id, thread_ts, blocks
// ... 10 more variants
}
// ...funneled into 1 canonical shape
struct NormalizedMessage {
channel_id: ChannelId,
conversation_id: ConversationId,
sender: Option<Sender>,
content: Content,
thread_id: Option<ThreadId>,
timestamp: DateTime<Utc>,
metadata: Metadata,
}
See the Type Algebra Deep Dive for the full mathematical analysis.
Zoom In: Pipeline Stages
| Stage | Crate | Input | Output | May Fail? |
|---|---|---|---|---|
| 1 | clawdesk-security | NormalizedMessage | AuthContext | Yes (blocked user) |
| 2 | clawdesk-memory | NormalizedMessage + AuthContext | Vec<HistoryEntry> | Yes (storage) |
| 3 | clawdesk-agents | History + Auth | GuardedContext | Yes (over budget) |
| 4 | clawdesk-skills | Context + Budget | Vec<SelectedSkill> | No (empty is valid) |
| 5 | clawdesk-providers | Request | ProviderResponse | Yes (LLM error) |
| 6 | clawdesk-providers | Response/Error | Final response | Yes (all exhausted) |
Stages are sequential but substages (like parallel tool execution in Stage 5) use JoinSet. See Structured Concurrency.
Security Layer View
The 4-layer security cascade that processes every message and tool call.
Zoom In: Content Scanning (Layer 2)
The three scanning stages run in sequence, each progressively more expensive:
| Stage | Method | Speed | Catches |
|---|---|---|---|
| Regex | Pattern matching | ~1μs | Known malicious patterns, PII |
| AST | Structural analysis | ~100μs | Code injection, prompt injection |
| Semantic | ML-based analysis | ~10ms | Novel attacks, context-dependent threats |
pub async fn scan(content: &str) -> ScanResult {
// Fast regex first (eliminates most benign content)
if let Some(finding) = regex_scan(content) {
return ScanResult::Blocked(finding);
}
// AST analysis for structured attacks
if let Some(finding) = ast_scan(content).await {
return ScanResult::Blocked(finding);
}
// Semantic analysis only for content that passed regex+AST
if let Some(finding) = semantic_scan(content).await {
return ScanResult::Flagged(finding);
}
ScanResult::Clean
}
This cascade is deliberately ordered: cheap checks first, expensive checks only if needed. ~95% of messages are cleared by the regex stage alone.
Channel Trait Layers
The layered capability model for channels.
Zoom In: Capability Detection
At runtime, the gateway checks whether a channel supports a capability before using it:
// Check if channel supports streaming
if let Some(streaming) = channel.as_streaming() {
streaming.send_stream(&conv_id, response_stream).await?;
} else {
// Fall back to collecting the stream and sending as a single message
let full = collect_stream(response_stream).await;
channel.send(full).await?;
}
This is Rust's trait object downcasting — compile-time safe, zero runtime overhead for channels that don't support a capability.
Fallback FSM View
The provider fallback state machine (see Fallback FSM Deep Dive for the full analysis).
Zoom In: Error Classification
| Error | Classification | FSM Action |
|---|---|---|
| 429 Rate Limit | Transient | Retry after backoff |
| 500 Server Error | Transient | Retry once, then next provider |
| 401 Auth Error | Fatal | Abort immediately |
| Context too long | Recoverable | Try next provider |
| Network timeout | Transient | Retry once |
The FSM terminates in at most $2n + 1$ transitions where $n$ is the number of candidate providers.
Helpful Links
| Diagram | Deep Dive | Tutorial |
|---|---|---|
| System Overview | Architecture Overview | Message Flow |
| Crate DAG | Crate Dependency Graph | — |
| Message Flow | Type Algebra | Build a Channel |
| Security | Security Model | — |
| Channel Traits | Channel Traits | Build a Channel |
| Fallback FSM | Fallback FSM | Build a Provider |