Skip to main content

Product Architecture

This is the technical architecture reference for the NeoMind main project (camthink-ai/NeoMind). After reading it you should be able to locate any feature by layer and crate, and understand the process and concurrency boundaries.

For the non-technical, user-facing view (what the product is made of, how data flows), see Product Overview β€” What is NeoMind.

Layered View​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Desktop App / Web UI β”‚
β”‚ React 18 + TypeScript β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Tauri 2.x / Browser β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ REST / WebSocket / SSE
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ API Gateway β”‚
β”‚ Axum Web Server β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Auth β”‚ β”‚Devices β”‚ β”‚Automateβ”‚ β”‚Messagesβ”‚ β”‚Extensionβ”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ Event Bus
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β–Ό β–Ό β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Agent β”‚ β”‚ Devices β”‚ β”‚ Rules β”‚ β”‚ Extensions β”‚
β”‚ (LLM) β”‚ β”‚ (MQTT) β”‚ β”‚ (JSON) β”‚ β”‚ (Process) β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ redb Storage β”‚
β”‚ (time-series) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Crate Layout​

NeoMind is a Rust workspace. Each crate has a single, clear responsibility:

CrateResponsibility
neomind-coreCore traits and types: EventBus, DataSourceId, LLM trait, capability detection
neomind-apiAxum web server, HTTP / WebSocket / SSE handlers, Swagger at /api/docs
neomind-agentAI agent: LLM backends, tool calling, memory system, skill system, scheduler
neomind-devicesDevice management: MQTT / webhook adapters, registration, command queue, draft approval
neomind-storageredb embedded storage: schema and access layer for all *.redb tables
neomind-messagesMessage notification: 7 channels (webhook/email/telegram/wecom/dingtalk/slack/feishu) + in-app
neomind-rulesJSON rule engine: parse, execute, event trigger
neomind-extension-sdkExtension SDK: neomind_export! macro, capability, ML model lifecycle (public API surface)
neomind-extension-runnerExtension process host: isolated sandbox, FFI bridge, crash-loop protection
neomind-data-pushData push: forward telemetry to external webhook / MQTT
neomind-cliCLI entry point (the neomind binary)
neomind-cli-opsCLI command implementations: one module per domain (device/rule/agent/...), dispatched in-process

The Tauri crate is outside the workspace: web/src-tauri/ is a separate Cargo project that calls neomind-api via edge_api::start_server(). When you change neomind-api's re-exports, verify Tauri still compiles.

Process Model​

NeoMind at runtime consists of two process classes:

1. Main Process (neomind serve)​

A single process hosting all core functionality:

  • Axum HTTP / WS / SSE server (port 9375)
  • MQTT broker (port 1883, embedded)
  • Event bus
  • Agent execution pool
  • Rule engine
  • Storage (redb)

2. Extension Processes (one per extension)​

Spawned and supervised by neomind-extension-runner:

  • Each extension runs in its own OS process β€” full process-level isolation
  • Communicates with the main process via FFI (C ABI)
  • A crash doesn't affect the main process: the runner has crash-loop protection; an extension that crashes repeatedly is auto-disabled
  • Capability-gated: the extension declares required capabilities (network, filesystem, ml-model) at startup; the runner authorizes exactly those
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Main (neomind) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ extension-runner │──┼──→ Process A (weather)
β”‚ β”‚ (supervisor) │──┼──→ Process B (yolo-video)
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”€β”€β”Όβ”€β”€β†’ Process C (ocr)
β”‚ ↑ FFI β”‚
β”‚ ExtensionProxy β”‚
β”‚ inside main β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Event Bus​

neomind-core::event_bus is the nervous system that decouples components. All cross-module communication flows through events β€” modules never import each other directly:

SourceEventSubscribers
Device MQTT dataDeviceDataReceivedRule engine, data push, dashboard WS
Rule firesRuleTriggeredMessage notifier, agent
Agent completesAgentExecutionCompletedMemory system, message notifier
Extension metricExtensionMetricStorage, dashboard
System state changeSystemEventIn-app message center

Pub/sub β€” multiple subscribers fire in parallel; within a single subscriber, events are processed sequentially.

Extension ABI​

Extensions are written in Rust but compile to a separate binary from the main process, bridged by FFI:

  • The neomind_export! macro (in the SDK) auto-generates C ABI entry points (extern "C" functions) from your ExtensionHandler trait impl
  • The runner loads the extension binary β†’ invokes the agreed entry β†’ wraps it in an ExtensionProxy registered with the main process
  • Data crosses the FFI boundary as serde JSON (metrics, commands, config)

Capability system: the extension declares capabilities: ["network", "filesystem:read", "ml-model"] in its metadata; the runner opens the sandbox accordingly when spawning. Calls requiring undeclared capabilities are rejected.

See Extension SDK for macro usage and lifecycle.

Storage​

NeoMind uses redb (a pure-Rust embedded KV store, lmdb-like). All data lives under data/:

TableContents
telemetry.redbTime-series telemetry (every device's metric history). Largest table, indexed by (device_id, metric, timestamp)
devices.redbDevice registry (id, name, type, adapter, config)
dashboards.redbDashboard definitions (layout, widget config)
rules.redbRule definitions (JSON config, enabled state)
agents.redbAgent definitions (prompt, schedule, resources)
messages.redbMessage delivery records
sessions.redbAI Chat session history
llm_backends.redbLLM backend config
settings.redbSystem settings
users.redb / api_keys.redbUsers and API keys
extensions.redbInstalled extension manifest
instances.redbMulti-backend registrations

No external database. Backup = stop the service + copy the data/ directory. Migrate by copying to the same path on a new host.

All storage access goes through neomind-storage's repository pattern β€” no other crate opens redb tables directly.

Concurrency & Threading​

Tokio async runtime, multi-threaded scheduler.

Concurrency caps (prevent avalanches):

SemaphoreLimitScope
Global execution10Agent executions running concurrently in the main process
Per-LLM-backend2Concurrent requests to the same backend (avoids 429)
Tool concurrency6Concurrent tool calls

Agent execution safety:

  • Global 5-minute (300s) timeout wrapping all of execute_internal
  • RAII StatusGuard: on panic / timeout / drop, the agent's state resets from Executing back to Active, preventing stuck agents
  • Skipped executions (couldn't acquire the concurrency slot) don't advance next_execution β€” they retry on the next tick

WebSocket / SSE: one task per frontend connection, subscribed to the event bus; auto-reconnect and backfill on disconnect.

Key Invariants (Memorize Before Coding)​

  • Backend snake_case / frontend camelCase: every API response goes through fromDashboardDTO() (web/src/store/persistence/types.ts). Any new code loading dashboards from the API must use this function.
  • Ollama uses /api/chat, NOT /v1/chat/completions.
  • DataSourceId format: {type}:{id}:{field} β€” parsing and generation live in neomind-core.
  • Multimodal capability hierarchy: user override > runtime probe > LiteLLM registry > heuristic > false (see crates/neomind-core/src/llm/registry.rs).
  • Extension component rendering: do NOT add a mountedRef pattern to ComponentRenderer (React 18 StrictMode double-mount breaks it); do NOT wrap renderDashboardComponent in an ErrorBoundary.

Next Steps​


Last updated: 2026-06-15