Waggle OS — Backend Map
Six cross-cutting diagrams tracing the Waggle OS runtime: from the Tauri shell through the
apps/web React SPA into the Fastify local sidecar (:3333), down through the workspace
packages and per-workspace SQLite *.mind stores, out to the LiteLLM router and LLM
providers — plus the data model, the chat-turn lifecycle, the feature→endpoint contract, and the
tier × trust gating model.
System Architecture (Layers)
01-system-architecture.md
The cross-cutting runtime map for one request: from the Tauri Rust shell, through the
apps/web React SPA, into the Fastify Local Sidecar (port 3333) that serves the SPA and
exposes all /api/* routes, down into the workspace packages, into the per-workspace
SQLite *.mind stores (plus optional team/cloud Postgres + Redis on the :3100 Cloud
Server), and out to the LiteLLM router and LLM providers. The frontend origin is the
sidecar (same-origin, two-step Bearer auth); the memory substrate (mind +
harvest) lives in @waggle/hive-mind-core, not @waggle/core.
flowchart TD
subgraph Shell["Desktop shell — app/ (Tauri 2.0, Rust)"]
TAURI["Tauri WebView\nloads apps/web dist from sidecar\norigin = tauri://localhost"]
end
subgraph Web["Frontend — apps/web (React 19 + Vite + Tailwind 4)"]
SPA["SPA shell + assets\nauth-exempt GET"]
AUTH2["Two-step auth\n1. GET /api/auth/session-token\n2. Authorization Bearer on every /api/*"]
APPS["OS apps + overlays\nChat · Memory{Harvest,Evolution,Wiki}\nMarketplace · Connectors · Launcher\nWaggleDance · Room · Mission Control"]
WS["WebSocket client\nGET /ws?token="]
end
subgraph Sidecar["Local Sidecar :3333 — packages/server (Fastify, Node, bundled in Tauri)"]
SEC["securityMiddleware\nHost allowlist · Bearer · CORS · RateLimit · CSP"]
STATIC["@fastify/static + SPA fallback\nserves apps/web dist"]
ROUTES["62 route plugins, flat /api/* paths\nchat · memory · harvest · evolution · skills\nmarketplace · connectors · wiki · tools\nwaggle-dance · stripe · vault · personas"]
WSL["/ws event-bus relay\napprovals · steps · tools · notifications"]
end
subgraph Packages["Workspace packages (responsibilities)"]
AGENT["@waggle/agent\nrunAgentLoop · Orchestrator · personas\ntool-executor · completion gates · cost-tracker\nconnectors · capability/trust · KVARK tools"]
HMC["@waggle/hive-mind-core\nmind: Identity·Awareness·Frames·KnowledgeGraph\nHybridSearch·Cognify·embeddings\nharvest: source adapters + 4-pass pipeline"]
SHARED["@waggle/shared\ntypes · zod schemas · TIERS\nmcp-catalog · tool-detection"]
DANCE["@waggle/waggle-dance\nWaggleMessage protocol\ndispatcher · SignalBus"]
MKT["@waggle/marketplace\ncatalog · installer · SecurityGate · sync"]
WIKI["@waggle/wiki-compiler\nentity/concept/synthesis/index/health pages"]
OPT["@waggle/optimizer\nself-evolution: GEPA + EvolveSchema\njudge · gates · evolution runs"]
end
subgraph LocalData["Per-workspace local data (better-sqlite3 + sqlite-vec)"]
MIND[("*.mind SQLite per workspace\nmemory_frames + _fts + _vec float[1024]\nknowledge_entities/relations · sessions\nharvest_runs · evolution_runs · wiki_pages")]
CFG[("~/.waggle/config.json\ntier · stripe_customer_id")]
VAULT[("Vault\napi keys · connector creds · kvark:connection")]
MKTDB[("marketplace.db SQLite")]
SKILLS[("~/.waggle/skills/*.md\nplugins · hooks.json")]
end
subgraph Cloud["Optional Cloud Server :3100 — packages/server (team/SaaS only)"]
AUTHP["authPlugin — Clerk JWT verify"]
CROUTES["teams · agents · jobs · scout\nsuggestions · messages · audit · analytics"]
WSG["/ws gateway — team chat"]
PG[("Postgres via drizzle\nDATABASE_URL")]
REDIS[("Redis pub/sub\nteam:*:waggle · job:*:progress")]
end
LITELLM["LiteLLM router\nlitellm-config.yaml\nPOST /chat/completions"]
PROVIDERS["LLM providers\nAnthropic · OpenAI · Ollama (local)\nVoyage · others"]
STRIPEAPI["Stripe API"]
KVARK["KVARK sovereign FastAPI\nsearch · ask · actions (TEAMS/ENTERPRISE)"]
TAURI --> SPA
SPA --> AUTH2
AUTH2 --> APPS
APPS -->|"Bearer /api/*"| SEC
WS -->|"?token="| WSL
SPA -.->|"shell + assets"| STATIC
SEC --> ROUTES
ROUTES --> AGENT
ROUTES --> HMC
ROUTES --> DANCE
ROUTES --> MKT
ROUTES --> WIKI
ROUTES --> OPT
ROUTES --> SHARED
WSL --> AGENT
AGENT --> SHARED
HMC --> SHARED
OPT --> AGENT
WIKI --> HMC
MKT --> SKILLS
AGENT -->|"recall/write memory"| HMC
HMC --> MIND
AGENT --> VAULT
ROUTES --> CFG
MKT --> MKTDB
AGENT --> SKILLS
AGENT -->|"POST /chat/completions"| LITELLM
OPT -->|"judge + mutate (Haiku)"| LITELLM
HMC -->|"embeddings (litellm/ollama)"| LITELLM
WIKI -->|"synthesize (Haiku/Ollama)"| LITELLM
LITELLM --> PROVIDERS
ROUTES -->|"stripeRoutes"| STRIPEAPI
STRIPEAPI -->|"webhook -> write tier"| CFG
VAULT -->|"KvarkClient"| KVARK
APPS -. "team mode only" .-> AUTHP
AUTHP --> CROUTES
CROUTES --> PG
CROUTES --> REDIS
WSG <--> REDIS
flowchart LR A["1. Load shell\nauth-exempt GET"] --> B["2. GET /api/auth/session-token\nsame-origin gated"] B --> C["token"] C --> D["3. Authorization Bearer token\non every /api/* call"] C --> E["4. GET /ws?token=\nWebSocket relay"] D --> F["Sidecar route plugins\nrate-limited, tier-gated"] F -->|"403 TIER_INSUFFICIENT"| G["render upgrade prompt\nrequired + upgradeUrl"] F -->|"429 + Retry-After"| H["backoff and retry"]
flowchart TD R["Fastify sidecar routes\npackages/server"] --> A R --> H R --> D R --> M R --> W R --> O A["@waggle/agent\nagent loop · orchestrator · 22 personas\ntool execution + 11-step chain\ncompletion gates D3/D4/D1\nconnectors (30) · capability + trust\nautonomy gating · KVARK tools · cost"] H["@waggle/hive-mind-core\nMIND: identity · awareness · frames\nknowledge graph · hybrid search\nrelevance scoring · cognify · embeddings\nHARVEST: adapters · 4-pass pipeline · dedup"] D["@waggle/waggle-dance\nWaggleMessage protocol\ndispatcher + combo validation\nSignalBus ring buffer · AI-OS signals"] M["@waggle/marketplace\nSQLite catalog · installer\nSecurityGate (4 layers) · 9 sync adapters"] W["@waggle/wiki-compiler\n5 page types from memory\nincremental compile · Obsidian/Notion export"] O["@waggle/optimizer\nGEPA + EvolveSchema + judge\nevolution gates · run store · deploy"] S["@waggle/shared\nwire types · zod schemas\n5-tier model + capabilities\nMCP catalog · tool detection"] A --> S H --> S D --> S M --> S W --> S O --> S
Master Data Model (two ER diagrams)
02-master-er.md
Data lives in two physically separate stores. The per-workspace memory layer is a
single SQLite file (*.mind) per workspace — 14 base tables plus 2 virtual search tables
(FTS5 + vec0). The team/cloud relational layer is a shared PostgreSQL database (20
tables, Drizzle ORM). There are no cross-database foreign keys: the only bridge is
the users.mind_path text column in Postgres, pointing at where a user's local SQLite
*.mind file lives.
erDiagram
sessions ||--o{ memory_frames : "gop_id FK"
memory_frames ||--o{ memory_frames : "base_frame_id self-FK"
memory_frames ||--|| memory_frames_fts : "rowid=id FTS5"
memory_frames ||--|| memory_frames_vec : "rowid=id vec0"
knowledge_entities ||--o{ knowledge_relations : "source_id FK"
knowledge_entities ||--o{ knowledge_relations : "target_id FK"
meta {
TEXT key PK
TEXT value
}
identity {
INTEGER id PK "CHECK id=1, single row"
TEXT name
TEXT role
TEXT department
TEXT personality
TEXT capabilities
TEXT system_prompt
TEXT created_at
TEXT updated_at
}
awareness {
INTEGER id PK
TEXT category "task action pending flag"
TEXT content
INTEGER priority
TEXT metadata "JSON"
TEXT created_at
TEXT expires_at "nullable"
}
sessions {
INTEGER id PK
TEXT gop_id UK "join key for frames"
TEXT project_id "nullable"
TEXT status "active closed archived"
TEXT started_at
TEXT ended_at "nullable"
TEXT summary "nullable"
}
memory_frames {
INTEGER id PK
TEXT frame_type "I P B"
TEXT gop_id FK
INTEGER t "per-gop ordinal"
INTEGER base_frame_id FK "nullable self"
TEXT content "JSON for B-frames"
TEXT importance "critical..deprecated"
TEXT source "user_stated..system"
INTEGER access_count
TEXT created_at
TEXT last_accessed
}
memory_frames_fts {
TEXT content "FTS5 rowid=id"
}
memory_frames_vec {
FLOAT embedding "float 1024 rowid=id"
}
knowledge_entities {
INTEGER id PK
TEXT entity_type
TEXT name
TEXT properties "JSON"
TEXT valid_from
TEXT valid_to "nullable=active"
TEXT recorded_at
}
knowledge_relations {
INTEGER id PK
INTEGER source_id FK
INTEGER target_id FK
TEXT relation_type
REAL confidence
TEXT properties "JSON"
TEXT valid_from
TEXT valid_to "nullable=active"
TEXT recorded_at
}
procedures {
INTEGER id PK
TEXT name
TEXT model
TEXT template
INTEGER version
REAL success_rate
REAL avg_cost
TEXT created_at
TEXT updated_at
}
improvement_signals {
INTEGER id PK
TEXT category "capability_gap..skill_promotion"
TEXT pattern_key
TEXT detail
INTEGER count
TEXT first_seen
TEXT last_seen
INTEGER surfaced "0 or 1"
TEXT surfaced_at "nullable"
TEXT metadata "JSON"
}
install_audit {
INTEGER id PK
TEXT timestamp
TEXT capability_name
TEXT capability_type "native..marketplace"
TEXT source
TEXT version "nullable"
TEXT risk_level "low medium high"
TEXT trust_source
TEXT approval_class
TEXT action
TEXT initiator "agent user system"
TEXT detail
}
ai_interactions {
INTEGER id PK "APPEND-ONLY"
TEXT timestamp
TEXT workspace_id "nullable"
TEXT session_id "nullable"
TEXT model
TEXT provider
INTEGER input_tokens
INTEGER output_tokens
REAL cost_usd
TEXT tools_called "JSON"
TEXT human_action "nullable"
TEXT risk_context "nullable"
TEXT imported_from "nullable"
TEXT persona "nullable"
TEXT input_text "nullable"
TEXT output_text "nullable"
}
execution_traces {
INTEGER id PK
TEXT session_id "nullable"
TEXT persona_id "nullable"
TEXT workspace_id "nullable"
TEXT model "nullable"
TEXT task_shape "nullable"
TEXT outcome "success..pending"
TEXT trace_json "JSON"
REAL cost_usd
INTEGER duration_ms
TEXT created_at
TEXT finalized_at "nullable"
}
evolution_runs {
INTEGER id PK
TEXT run_uuid UK
TEXT target_kind
TEXT target_name "nullable"
TEXT baseline_text
TEXT winner_text
TEXT winner_schema_json "nullable"
REAL delta_accuracy
TEXT gate_verdict "pass fail"
TEXT gate_reasons_json "JSON"
TEXT status "proposed..failed"
TEXT artifacts_json "nullable"
TEXT user_note "nullable"
TEXT failure_reason "nullable"
TEXT created_at
TEXT decided_at "nullable"
TEXT deployed_at "nullable"
}
harvest_sources {
INTEGER id PK
TEXT source UK
TEXT display_name
TEXT source_path "nullable"
TEXT last_synced_at "nullable"
INTEGER items_imported
INTEGER frames_created
INTEGER auto_sync "0 or 1"
INTEGER sync_interval_hours
TEXT last_content_hash "nullable"
TEXT created_at
}
erDiagram
users ||--o{ teams : "owns owner_id"
users ||--o{ team_members : "is"
teams ||--o{ team_members : "has"
users ||--o{ agents : "owns user_id"
teams ||--o{ agents : "scopes team_id"
users ||--o{ agent_groups : "owns"
agent_groups ||--o{ agent_group_members : "contains"
agents ||--o{ agent_group_members : "joins"
teams ||--o{ tasks : "has"
users ||--o{ tasks : "creates created_by"
users ||--o{ tasks : "assigned assigned_to"
teams ||--o{ messages : "channel"
users ||--o{ messages : "sends sender_id"
teams ||--o{ team_entities : "owns"
users ||--o{ team_entities : "shares shared_by"
teams ||--o{ team_relations : "owns"
team_entities ||--o{ team_relations : "source_id"
team_entities ||--o{ team_relations : "target_id"
teams ||--o{ team_resources : "owns"
users ||--o{ team_resources : "shares"
teams ||--o{ team_capability_policies : "governs"
users ||--o{ team_capability_policies : "updates"
teams ||--o{ team_capability_overrides : "governs"
users ||--o{ team_capability_overrides : "decides"
teams ||--o{ team_capability_requests : "scopes"
users ||--o{ team_capability_requests : "requests-decides"
teams ||--o{ agent_jobs : "owns"
users ||--o{ agent_jobs : "runs"
teams ||--o{ cron_schedules : "owns"
users ||--o{ cron_schedules : "creates"
users ||--o{ scout_findings : "for-user"
teams ||--o{ scout_findings : "for-team"
proactive_patterns ||--o{ suggestions_log : "fires"
users ||--o{ suggestions_log : "receives"
users ||--o{ agent_audit_log : "acts"
teams ||--o{ agent_audit_log : "scopes"
users {
uuid id PK
text clerk_id UK
text display_name
text email UK
text avatar_url "nullable"
text mind_path "nullable to SQLite mind"
timestamptz created_at
timestamptz updated_at
}
teams {
uuid id PK
text name
text slug UK
uuid owner_id FK
timestamptz created_at
}
team_members {
uuid team_id PK_FK
uuid user_id PK_FK
text role "default member"
text role_description "nullable"
jsonb interests "nullable"
timestamptz joined_at
}
agents {
uuid id PK
uuid user_id FK
uuid team_id FK "nullable"
text name
text role "nullable"
text system_prompt "nullable"
text model "default claude-haiku-4-5"
jsonb tools "default empty"
jsonb config "default empty"
timestamptz created_at
}
agent_groups {
uuid id PK
uuid user_id FK
text name
text description "nullable"
text strategy "default parallel"
timestamptz created_at
}
agent_group_members {
uuid group_id PK_FK
uuid agent_id PK_FK
text role_in_group "default worker"
integer execution_order "default 0"
}
tasks {
uuid id PK
uuid team_id FK
text title
text description "nullable"
text status "default open"
text priority "default normal"
uuid created_by FK
uuid assigned_to FK "nullable"
uuid parent_task_id "no FK logical self-ref"
timestamptz created_at
timestamptz updated_at
}
messages {
uuid id PK
uuid team_id FK
uuid sender_id FK
text type
text subtype
jsonb content
uuid reference_id "no FK"
jsonb routing "nullable"
timestamptz created_at
}
team_entities {
uuid id PK
uuid team_id FK
text entity_type
text name
jsonb properties "default empty"
uuid shared_by FK
timestamptz valid_from
timestamptz valid_to "nullable open-ended"
timestamptz created_at
}
team_relations {
uuid id PK
uuid team_id FK
uuid source_id FK
uuid target_id FK
text relation_type
real confidence "default 1.0"
jsonb properties "default empty"
timestamptz created_at
}
team_resources {
uuid id PK
uuid team_id FK
text resource_type
text name
text description "nullable"
jsonb config
uuid shared_by FK
real rating "default 0"
integer use_count "default 0"
timestamptz created_at
}
team_capability_policies {
uuid id PK
uuid team_id FK
text role
jsonb allowed_sources "default empty"
jsonb blocked_tools "default empty"
text approval_threshold "default none"
uuid updated_by FK "nullable"
timestamptz created_at
timestamptz updated_at
}
team_capability_overrides {
uuid id PK
uuid team_id FK
text capability_name
text capability_type
text decision
text reason "default empty"
uuid decided_by FK
timestamptz created_at
timestamptz decided_at
}
team_capability_requests {
uuid id PK
uuid team_id FK
uuid requested_by FK
text capability_name
text capability_type
text justification
text status "default pending"
uuid decided_by FK "nullable"
text decision_reason "nullable"
timestamptz created_at
timestamptz decided_at "nullable"
}
agent_jobs {
uuid id PK
uuid team_id FK
uuid user_id FK
text job_type
text status "default queued"
jsonb input
jsonb output "nullable"
timestamptz started_at "nullable"
timestamptz completed_at "nullable"
timestamptz created_at
}
cron_schedules {
uuid id PK
uuid team_id FK
uuid created_by FK
text name
text cron_expr
text job_type
jsonb job_config "default empty"
boolean enabled "default true"
timestamptz last_run_at "nullable"
timestamptz next_run_at "nullable"
timestamptz created_at
}
scout_findings {
uuid id PK
uuid user_id FK "nullable"
uuid team_id FK "nullable"
text source
text category
text title
text summary "nullable"
real relevance_score "default 0"
text url "nullable"
text status "default new"
timestamptz created_at
}
proactive_patterns {
uuid id PK "config-only no FK"
text name
jsonb trigger
text suggestion_type
text template
boolean enabled "default true"
}
suggestions_log {
uuid id PK
uuid user_id FK
uuid pattern_id FK
jsonb context
text status "default pending"
timestamptz created_at
}
agent_audit_log {
uuid id PK
uuid user_id FK
uuid team_id FK "nullable"
text agent_name
text action_type
text description
jsonb before_state "nullable"
jsonb after_state "nullable"
boolean requires_approval "default false"
boolean approved "nullable tri-state"
uuid approved_by FK "nullable"
timestamptz created_at
}
Chat Turn Sequence
03-chat-turn-sequence.md
One conversational chat turn, end to end. The frontend POSTs to POST /api/chat (an SSE
stream — the server validates, then reply.hijack() and writes a raw
text/event-stream). The handler assembles the layered system prompt via the per-session
Orchestrator (buildSystemPrompt + recallMemory over
HybridSearch), filters tools by persona/context, then calls runAgentLoop,
which POSTs to LiteLLM. Tool calls pass the 11-step executeToolCall chain; after the
loop, autoSaveFromExchange runs the CognifyPipeline to write new memory
frames.
sequenceDiagram
autonumber
participant UI as "Frontend (chat UI)"
participant Route as "POST /api/chat\nchat.ts (SSE)"
participant Orch as "Orchestrator\nbuildSystemPrompt + recallMemory"
participant Search as "HybridSearch\n(workspace mind)"
participant AL as "runAgentLoop\nagent-loop.ts"
participant LLM as "LiteLLM\n/chat/completions"
participant Tools as "executeToolCall\n11-step chain"
participant Trace as "TraceRecorder"
participant Cog as "CognifyPipeline\nautoSaveFromExchange"
participant Disk as "session .jsonl"
UI->>Route: "POST { message, workspace, session, model?, persona?, autonomy? }"
Note over Route: "validate · injection scan score < 0.7 · RBAC · path guard (all pre-hijack)"
alt rejected
Route-->>UI: "400 / 403 JSON error (MESSAGE_TOO_LONG, INJECTION_DETECTED, ...)"
else accepted
Note over Route: "reply.hijack() · write SSE headers · wire AbortController to client close"
Route->>Disk: "persistMessage user turn (append .jsonl)"
Route->>Route: "generateTurnId UUID v4 · resolve model fallback chain"
Route->>Orch: "recallMemory(query, turnId)"
Note over Orch: "catch-up vs semantic · drop temporary/deprecated · injection scan"
Orch->>Search: "search(query) keyword + vector"
Search-->>Orch: "SearchResult[] ranked by finalScore = rrfScore * relevanceScore"
Orch-->>Route: "recalledContext text"
Route-->>UI: "event: step Recalling relevant memories..."
Route-->>UI: "event: tool / tool_result (auto_recall)"
Route->>Orch: "buildSystemPrompt()"
Orch-->>Route: "identity + self-awareness + preloaded context"
Note over Route: "layer profile + runtime facts + activeSpec.rules + skills + Workspace Now + corrections, then composePersonaPrompt"
Note over Route: "filterToolsForContext + filterAvailableTools + persona allow/deny"
Route->>AL: "runAgentLoop { systemPrompt, tools, messages, stream:true, maxTurns:200, turnId, traceRecording }"
loop "each turn up to maxTurns"
AL->>LLM: "POST /chat/completions { model, messages, tools, stream }"
Note over AL,LLM: "signal = client-abort + 300s timeout · 429/5xx -> backoff, turn--, retry"
LLM-->>AL: "stream chunks: content + tool_calls + usage"
AL-->>UI: "event: token xN assistant text"
alt "tool_calls present"
AL-->>UI: "event: tool { name, input }"
AL->>Tools: "executeToolCall(name, args)"
Note over Tools: "parse args -> onToolUse -> governance blockedTools -> pre:tool -> pre:memory-write -> LoopGuard.check -> execute -> scanForInjection -> onToolResult -> post hooks"
opt "gated tool"
AL-->>UI: "event: approval_required { requestId, toolName, input }"
UI->>Route: "POST /api/approval/:requestId { approved, always? }"
Route-->>AL: "resolve(approved) (auto-deny after 5 min)"
end
Tools->>Trace: "record tool call + sanitized result"
Tools-->>AL: "role:tool result message (sanitized)"
AL-->>UI: "event: tool_result { name, result, isError }"
else "no tool_calls (final answer)"
Note over AL: "maybeFireCompletionGate: D3 verification -> D4 phantom-write -> D1 skill-distillation"
alt "a gate fired"
Note over AL: "push corrective directive, continue one more turn"
else "none fired"
AL-->>Trace: "finalize trace"
AL-->>Route: "AgentResponse { content, toolsUsed, usage }"
end
end
end
Note over Route: "cost tracking · KG entity extraction · disclaimers"
Route->>Cog: "autoSaveFromExchange(message, result.content)"
Cog->>Disk: "cognify writes new memory frame (I/P) + FTS + vec index + entities"
Route->>Orch: "commitSurfacedSignals()"
Route->>Disk: "persistMessage assistant turn"
Route-->>UI: "event: done { content, usage, toolsUsed, model, cost? }"
end
Note over Route: "on failure -> event: error { message } (raw turn still persisted) · finally: raw.end()"
Feature → API Map (rebuild blueprint)
04-feature-api-map.md
The rebuild contract. The left column lists every implemented OS app and overlay; the right column
lists the backend endpoint GROUPS each one depends on, grouped exactly as the section files split
them (03a–03g). Every app first goes through the shared adapter singleton and
ServiceProvider; auth and tier-gating are cross-cutting. Apps with no backend calls
(Voice, Team Governance, Keyboard Shortcuts) are wired only to the shared layer.
flowchart LR classDef app fill:#1b2330,stroke:#a78bfa,color:#e8e8ef classDef ovl fill:#23202e,stroke:#e5a000,color:#f3ead0 classDef shared fill:#0f1622,stroke:#7dd3fc,color:#dff1ff classDef grp fill:#10271b,stroke:#34d399,color:#d7f7e6 %% Shared layer every feature passes through ADAPTER["adapter singleton\nlib/adapter.ts\n127.0.0.1:3333"]:::shared SVC["ServiceProvider\nadapter.connect"]:::shared AUTH["Auth + Health\nGET /api/auth/session-token\nGET /health"]:::shared TIER["Tier gating bus\n403 TIER_INSUFFICIENT\nwaggle:tier-insufficient"]:::shared SVC --> ADAPTER ADAPTER --> AUTH ADAPTER --> TIER %% Endpoint GROUPS (right side) G_CHAT["03a Chat + Agents\n/api/chat SSE\n/api/history /api/agent/*\n/api/sessions/* /api/jobs/*"]:::grp G_MEM["03b Memory + Knowledge\n/api/memory/* /api/identity\n/api/mind/* /api/documents"]:::grp G_WIKI["03b Wiki + Harvest + Import\n/api/wiki/* /api/harvest/*\n/api/import/*"]:::grp G_WS["03c Workspace + Templates\n/api/workspaces/* /api/files/*\n/api/workspace-templates/* /api/browse"]:::grp G_TEAM["03c Team + Persona + Settings + Profile + Pins\n/api/personas/* /api/agent-groups/*\n/api/team/* /api/settings/* /api/profile/* /api/pins"]:::grp G_MKT["03d Marketplace + Skills + Capabilities\n/api/marketplace/* /api/skills/*\n/api/capabilities/status"]:::grp G_CONN["03d Connectors + Vault\n/api/connectors/* /api/vault/*"]:::grp G_TOOLS["03d AI-OS Tool Launcher\n/api/tools/detect|launch|processes|kill|hooks"]:::grp G_EVO["03e Evolution + Compliance + Costs + Feedback + Approvals\n/api/evolution/* /api/compliance/*\n/api/cost*|/api/costs /api/feedback /api/approval/*"]:::grp G_RT["03f Realtime + Ops\n/api/events* SSE /api/notifications/*\n/api/cron/* /api/fleet/* /api/litellm/* /api/local-inference/* /api/backup|restore"]:::grp G_WD["03f WaggleDance signals\n/api/waggle/signals\n/api/waggle/stream SSE"]:::grp G_BILL["03g Billing + Tier + GDPR\n/api/tier /api/tier/start-trial\n/api/stripe/* /api/data/erase"]:::grp G_TEL["03e Telemetry\n/api/telemetry/*"]:::grp %% ---- Dock apps (left) ---- A_CHAT["Chat"]:::app A_DASH["Dashboard / Home"]:::app A_MEM["Memory\nframes/KG/harvest/wiki/evolution"]:::app A_EVT["Events and Logs"]:::app A_CAP["Skills and Apps"]:::app A_CONN["Connectors"]:::app A_COCK["Cockpit / Command Center"]:::app A_MC["Mission Control"]:::app A_WD["Waggle Dance"]:::app A_AGT["Personas / Agents"]:::app A_FILE["Files"]:::app A_CRON["Scheduled Jobs"]:::app A_MKT["Marketplace"]:::app A_LAUN["AI Tools / Launcher"]:::app A_VOICE["Voice\nstatic placeholder"]:::app A_ROOM["Room\nsub-agent canvas"]:::app A_APPR["Approvals"]:::app A_TL["Timeline"]:::app A_BAK["Backup and Restore"]:::app A_TEL["Usage and Telemetry"]:::app A_GOV["Team Governance\nTEAMS tier"]:::app A_SET["Settings"]:::app A_VAULT["Vault"]:::app A_PROF["My Profile"]:::app %% All apps go through the shared adapter A_CHAT & A_DASH & A_MEM & A_EVT & A_CAP & A_CONN & A_COCK & A_MC & A_WD & A_AGT & A_FILE & A_CRON & A_MKT & A_LAUN & A_VOICE & A_ROOM & A_APPR & A_TL & A_BAK & A_TEL & A_GOV & A_SET & A_VAULT & A_PROF --> ADAPTER %% Chat A_CHAT --> G_CHAT A_CHAT --> G_MEM A_CHAT --> G_TEAM A_CHAT --> G_WIKI %% Dashboard A_DASH --> G_MEM A_DASH --> G_CHAT %% Memory app A_MEM --> G_MEM A_MEM --> G_WIKI A_MEM --> G_EVO A_MEM --> G_RT %% Events A_EVT --> G_RT %% Skills and Apps A_CAP --> G_MKT %% Connectors A_CONN --> G_CONN %% Cockpit + Compliance A_COCK --> G_RT A_COCK --> G_EVO A_COCK --> G_CONN A_COCK --> G_MKT A_COCK --> G_WIKI %% Mission Control A_MC --> G_RT A_MC --> G_TEAM A_MC --> G_TOOLS %% Waggle Dance A_WD --> G_WD %% Personas / Agents A_AGT --> G_TEAM A_AGT --> G_CHAT A_AGT --> G_MKT %% Files A_FILE --> G_WS %% Scheduled Jobs A_CRON --> G_RT %% Marketplace A_MKT --> G_MKT %% Launcher A_LAUN --> G_TOOLS %% Room A_ROOM --> G_RT %% Approvals A_APPR --> G_EVO %% Timeline A_TL --> G_RT %% Backup A_BAK --> G_RT %% Telemetry A_TEL --> G_EVO A_TEL --> G_TEL %% Settings A_SET --> G_TEAM A_SET --> G_TEL A_SET --> G_RT %% Vault A_VAULT --> G_CONN %% Profile A_PROF --> G_TEAM
flowchart LR classDef ovl fill:#23202e,stroke:#e5a000,color:#f3ead0 classDef grp fill:#10271b,stroke:#34d399,color:#d7f7e6 classDef shared fill:#0f1622,stroke:#7dd3fc,color:#dff1ff ADAPTER["adapter singleton\nlib/adapter.ts"]:::shared GO_WS["03c Workspace + Templates\n/api/workspaces/* /api/workspace-templates/*\n/api/browse"]:::grp GO_TEAM["03c Persona + Settings + Profile\n/api/personas/* /api/agent-groups/*\n/api/settings /api/profile"]:::grp GO_MEM["03b Memory + Identity\n/api/memory/search /api/memory/stats\n/api/identity"]:::grp GO_HARV["03b Harvest + Import\n/api/harvest/* /api/import/*"]:::grp GO_PROV["03d Vault + Providers + Skills\n/api/vault /api/providers\n/api/skills /v1/models"]:::grp GO_FLEET["03f Fleet + LiteLLM\n/api/fleet/spawn /api/litellm/models"]:::grp GO_NOTIF["03f Notifications\n/api/notifications/*"]:::grp GO_BILL["03g Billing + GDPR\n/api/tier/start-trial\n/api/stripe/* /api/data/erase"]:::grp GO_TEL["03e Telemetry\n/api/telemetry/track"]:::grp GO_SVC["Connect + Health\n/api/auth/session-token /health"]:::shared O_ONB["Onboarding wizard\n8 steps"]:::ovl O_LOGIN["Login briefing"]:::ovl O_SEARCH["Global search Cmd+K"]:::ovl O_CWS["Create workspace dialog"]:::ovl O_PSW["Persona switcher"]:::ovl O_SPAWN["Spawn agent dialog"]:::ovl O_WSW["Workspace switcher\nprops-driven"]:::ovl O_NINBOX["Notification inbox"]:::ovl O_CRAIL["Context rail"]:::ovl O_ERASE["Erase data dialog GDPR"]:::ovl O_UPG["Upgrade modal"]:::ovl O_TRIAL["Trial expired modal"]:::ovl O_KB["Keyboard shortcuts help\nno calls"]:::ovl O_TOUR["Onboarding tooltips\nno calls"]:::ovl O_ONB & O_LOGIN & O_SEARCH & O_CWS & O_PSW & O_SPAWN & O_ERASE & O_UPG & O_TRIAL & O_NINBOX & O_CRAIL --> ADAPTER O_ONB --> GO_SVC O_ONB --> GO_PROV O_ONB --> GO_HARV O_ONB --> GO_TEAM O_ONB --> GO_WS O_ONB --> GO_TEL O_LOGIN --> GO_MEM O_LOGIN --> GO_WS O_SEARCH --> GO_WS O_SEARCH --> GO_MEM O_SEARCH --> GO_PROV O_CWS --> GO_WS O_CWS --> GO_TEAM O_PSW --> GO_TEAM O_SPAWN --> GO_FLEET O_SPAWN --> GO_PROV O_SPAWN --> GO_WS O_NINBOX --> GO_NOTIF O_CRAIL --> GO_MEM O_ERASE --> GO_BILL O_UPG --> GO_BILL O_TRIAL --> GO_BILL
Tier + Trust Gating
05-tier-gating.md
Waggle gates every action on two orthogonal axes. The subscription tier
(TRIAL / FREE / PRO / TEAMS / ENTERPRISE) decides whether a feature exists — enforced via
requireTier() returning 403 TIER_INSUFFICIENT. The
autonomy / trust level (Normal / Trusted / YOLO) decides, per tool call, whether the
UI must show an approval prompt. Rule of thumb: tier decides whether the door exists; trust
decides whether it needs a key turn each time you walk through.
flowchart TD USER["User stored tier plus trialStartedAt"] --> EFF["getEffectiveTier\nTRIAL expired collapses to FREE"] EFF --> CAPS["getCapabilities\nTierCapabilities flag set"] CAPS --> AXIS1["AXIS 1 — Tier gate\nfeature exists for this plan?"] SESSION["Session AutonomyLevel\nnormal / trusted / yolo"] --> AXIS2["AXIS 2 — Trust gate\nclick needed for this call?"] AXIS1 --> ROUTES["requireTier preHandler\n403 TIER_INSUFFICIENT on fail"] AXIS1 --> KREG["KVARK tools registered\nonly when KVARK configured"] AXIS2 --> CONF["needsConfirmationWithAutonomy\nper tool call"] ROUTES --> GATED["Marketplace, Personas, Admin,\nTeam, Cloud-sync, Enterprise packs"] KREG --> KVARK["kvark_search, kvark_feedback,\nkvark_action, kvark_ask_document"] CONF --> TOOLS["Tools, connectors, bash,\ninstall_capability"]
flowchart LR
subgraph TRIAL["TRIAL — 0 USD / 15 days"]
T1["All features unlocked\nmirrors TEAMS plus ENTERPRISE\nselfHosted OFF\ndecays to FREE after 15 days"]
end
subgraph FREE["FREE — 0 USD forever"]
F1["5 workspaces, 5 connectors\nspawnAgents ON, memory free\nbuilt-in skills only\nexport txt and md\nauditLog none, kvarkCta subtle"]
end
subgraph PRO["PRO — 19 USD / mo (solo)"]
P1["Unlimited workspaces plus connectors\ncustomSkills ON, marketplace\nexport txt md pdf json\nteamMembersLimit 1, auditLog basic\nno sharedWorkspaces, no cloudSync"]
end
subgraph TEAMS["TEAMS — 49 USD / seat"]
M1["sharedWorkspaces, teamSkillLibrary\ncloudSync, adminPanel\nauditLog full, selfHosted\nmanagedModelPool, priorityModels\nWaggleDance, kvarkCta active"]
end
subgraph ENT["ENTERPRISE — consultative"]
E1["KVARK sovereign on-prem\nall TEAMS capabilities\nselfHosted ON, kvarkCta none\ngovernance permissions route"]
end
TRIAL -->|"trial expires"| FREE
FREE -->|"upgrade 19/mo"| PRO
PRO -->|"upgrade 49/seat"| TEAMS
TEAMS -->|"sales contact"| ENT
| Capability | TRIAL | FREE | PRO | TEAMS | ENTERPRISE |
|---|---|---|---|---|---|
| connectorLimit | -1 | 5 | -1 | -1 | -1 |
| workspaceLimit | -1 | 5 | -1 | -1 | -1 |
| embeddingProviders | all 6 | inprocess, mock, ollama | + voyage, openai | all 6 | all 6 |
| embeddingQuotaPerMonth | -1 | -1 | -1 | -1 | -1 |
| messageHistoryLimit | -1 | -1 | -1 | -1 | -1 |
| spawnAgents | yes | yes | yes | yes | yes |
| customSkills | yes | no | yes | yes | yes |
| teamSkillLibrary | yes | no | no | yes | yes |
| cloudSync | yes | no | no | yes | yes |
| exportFormats | txt md pdf json | txt md | txt md pdf json | txt md pdf json | txt md pdf json |
| teamMembersLimit | -1 | 1 | 1 | -1 | -1 |
| sharedWorkspaces | yes | no | no | yes | yes |
| adminPanel | yes | no | no | yes | yes |
| auditLog | full | none | basic | full | full |
| selfHosted | no | no | no | yes | yes |
| managedModelPool | yes | no | no | yes | yes |
| priorityModels | yes | no | no | yes | yes |
| kvarkCta | subtle | subtle | subtle | active | none |
| stripePriceId | null | null | STRIPE_PRICE_PRO | STRIPE_PRICE_TEAMS | null |
-1 means unlimited. all 6 embedding providers =
inprocess, mock, ollama, voyage, openai, litellm. Tier ordering
(TIER_ORDER): FREE 0, PRO 1, TEAMS 2, ENTERPRISE 3, TRIAL 3 — TRIAL ties
ENTERPRISE for capabilities but is time-limited.
flowchart TD
CALL["Tool call name plus args"] --> NC{"needsConfirmation?\nALWAYS_CONFIRM, connector write,\ndestructive bash"}
NC -- no --> RUN["Run silently"]
NC -- yes --> LVL{"AutonomyLevel"}
LVL -- normal --> PROMPT["Show approval prompt"]
LVL -- "trusted or yolo" --> CRIT{"isCriticalNeverAutopass?"}
CRIT -- yes --> PROMPT
CRIT -- no --> WHICH{"which level?"}
WHICH -- yolo --> AUTO["Auto-approve plus audit step"]
WHICH -- trusted --> TAP{"in TRUSTED_AUTOPASS\nor safe bash?"}
TAP -- yes --> AUTO
TAP -- no --> PROMPT
flowchart TD A["FREE user — connector_github_create_issue"] --> A1["Tier: no route gate"] --> A2["Trust: write so Normal prompts,\nTrusted gates, YOLO auto-passes"] --> A3["Runs after approval if connected"] B["FREE user — Install marketplace pack"] --> B1["Tier: 403 needs PRO"] --> B3["Blocked, upgrade prompt"] C["ENTERPRISE user — kvark_action"] --> C1["Tier: KVARK configured so registered"] --> C2["Trust: requires approval"] --> C3["Governed action runs with audit ref"] D["Any tier, YOLO — bash sudo rm -rf /"] --> D1["Tier: none"] --> D2["Trust: isCriticalNeverAutopass"] --> D3["Still prompts even at YOLO"] E["PRO user — install_capability starter-pack"] --> E1["Tier: none"] --> E2["Trust: ALWAYS_CONFIRM,\nclass from _riskLevel"] --> E3["Prompts, critical if high-risk"]
API Domain Overview
06-api-domains.md
The cross-cutting map of every HTTP/SSE/WebSocket surface the frontend talks to. Almost everything
lives in the Local Fastify Sidecar (loopback :3333, flat
/api/* paths, Bearer session-token + same-origin guards); a smaller set of
Clerk-authenticated routes live in the separate Cloud Server (:3100,
SaaS/team only). KVARK has no Fastify routes — it is reached only through the in-process
KvarkClient.
mindmap
root(("Waggle API\n7 domains"))
("Chat / Agents / Sessions\n~29 endpoints\nsec 03a")
("POST /api/chat SSE + DELETE history\n2")
("GET /api/history\n1")
("Agent status / cost / model / active\n6")
("Slash commands /api/commands/execute\n1")
("Sessions CRUD + search + export + timeline\n8")
("Agent groups CRUD + run\n5")
("POST /api/agent/run one-shot SSE\n1")
("Approvals SSE-paused gate\nshared with 03e")
("Memory / Wiki / Harvest\n~38 endpoints\nsec 03b")
("Memory frames search/CRUD/stats\n7")
("Knowledge graph read /api/memory/graph\n1")
("Wiki pages / compile / export\n8")
("Harvest preview/commit/sources/runs SSE\n11")
("Legacy import preview/commit\n2")
("Identity record + mind context\n5")
("Document version registry\n3")
("GDPR data erase\n1")
("Workspace / Team / Personas\n~76 endpoints\nsec 03c")
("Workspaces lifecycle + storage + context\n16")
("Workspace templates 15 built-in\n5")
("Team remote-proxy + local CRUD\n25")
("Personas catalog + create + generate\n5")
("Settings / tier / cloud-sync / admin\n14")
("User profile + style + brand\n7")
("Pins per workspace\n4")
("Marketplace / Skills / Connectors\n~60 endpoints\nsec 03d")
("Marketplace search/install/sources PRO\n15")
("Skills + plugins + hooks\n28")
("Connectors connect/disconnect/health\n4")
("AI-OS tool launcher detect/launch/hooks\n5")
("OAuth 5 providers\n3")
("Vault list/add/delete/reveal\n4")
("LLM + search providers catalog\n1")
("Evolution / Governance\n~39 endpoints\nsec 03e")
("Evolution runs accept/reject/run SSE\n8")
("Feedback thumbs + stats\n2")
("Telemetry local-only\n6")
("Compliance EU AI Act + templates + PDF\n12")
("Cost dashboard + by-workspace TEAMS\n3")
("Capabilities status + plugin toggles\n3")
("Approvals inbox + grants\n5")
("Real-time / Ops\n~52 endpoints\nsec 03f")
("WaggleDance UI signals + stream SSE\n4")
("WaggleDance v2 protocol bus\n2")
("Audit events + stats + stream SSE\n3")
("Cron schedules + trigger + history\n7")
("Notifications stream SSE + store\n6")
("Offline message queue\n5")
("Backup / restore / metadata\n3")
("Agent fleet spawn/pause/resume/kill\n5")
("LiteLLM control + pricing\n4")
("Local inference hardware/models/pull\n4")
("Anthropic proxy /v1/chat/completions SSE\n2")
("Filesystem browse local-only\n2")
("Browser extension health\n1")
("Telegram outbound push\n4")
("Cloud / Billing / KVARK\n~35 surfaces\nsec 03g")
("Sidecar inline auth/docs/health/ws\n6")
("Stripe checkout/webhook/sync/portal\n4")
("Cloud agents + groups Clerk\n10")
("Cloud jobs queue Clerk\n4")
("Cloud scout findings Clerk\n2")
("Cloud suggestions Clerk\n2")
("KVARK client methods no routes\n5")
("WebSocket sidecar + cloud\n2")
graph LR
FE["Frontend SPA\napps/web served by sidecar"]
subgraph SIDECAR["Local Sidecar :3333 - Bearer session-token + same-origin"]
D1["Chat / Agents / Sessions\n~29"]
D2["Memory / Wiki / Harvest\n~38"]
D3["Workspace / Team / Personas\n~76"]
D4["Marketplace / Skills / Connectors\n~60"]
D5["Evolution / Governance\n~39"]
D6["Real-time / Ops SSE+WS\n~52"]
BILL["Stripe billing\n4"]
BOOT["Auth token / docs / health / ws\n6"]
end
subgraph CLOUD["Cloud Server :3100 - Clerk JWT - optional SaaS"]
C1["Agents + Groups\n10"]
C2["Jobs queue\n4"]
C3["Scout findings\n2"]
C4["Suggestions\n2"]
C5["Team WebSocket gateway\n1"]
end
KVARK["KVARK client\nno routes - vault-credentialed\n5 methods - TEAMS/ENTERPRISE"]
FE -->|"same-origin Bearer"| SIDECAR
FE -.->|"team mode Clerk JWT"| CLOUD
D5 -->|"agent tools"| KVARK
D3 -->|"teamServerUrl proxy"| CLOUD
BILL -->|"writes tier to config.json"| D3