Skip to main content

Architecture

Maekon follows Hexagonal Architecture (Ports & Adapters). The core (maekon-core) defines all traits and domain models; every other crate is an adapter implementing those ports.

Workspace layout

clients/maekon-client/
├── src-tauri/ # Tauri v2 main binary (package: maekon-app)
└── crates/
├── maekon-core # Port traits + domain models + error codes
├── maekon-network # JWT auth, HTTP/SSE, gRPC, batch upload
├── maekon-storage # SQLite (WAL) + schema migrations
├── maekon-monitor # System metrics, active window, idle detection
├── maekon-vision # Screen capture, delta encoding, OCR, PII filter
├── maekon-suggestion # SSE/gRPC reception, priority queue, feedback
├── maekon-automation # Policy-based command execution + audit
├── maekon-analysis # LLM segment summarization, vector RAG
├── maekon-embedding # INT8 quantization, similarity search
├── maekon-audio # cpal capture + Whisper STT
├── maekon-web # Local Axum dashboard + React frontend
├── maekon-api-contracts # Shared API types
├── maekon-sandbox-worker # Out-of-process action executor
└── maekon-lint # Workspace lint tool (standalone)

The dependency rule

maekon-core ← every adapter (one-way)

Adapters never depend on each other. All cross-crate communication goes through traits defined in maekon-core. This is the core invariant — see ADR-001 §1.

Key invariants

  • All port traits use &self (not &mut self); implementations needing mutable state use interior mutability (Mutex, RwLock, parking_lot)
  • #[async_trait] on every port — required for Arc<dyn Trait> DI
  • Manual mock implementations in #[cfg(test)] mod tests — no mockall
  • Error strategy: thiserror in libraries, anyhow in the binary, all wrapped via #[from]
  • Error code field on every CoreError variant (ADR-019) — current wire snapshot is 42 codes

Image processing pipeline

Screen frames go through a 4-tier importance branching (maekon-vision::EdgeFrameProcessor):

ImportancePipeline
≥ 0.8Full + OCR
≥ 0.5Delta encoding
≥ 0.3Thumbnail
< 0.3Metadata only

PII filter runs before any image leaves the device — phone, API key, IP, email, credit card, SSN, file path masking with a 4-level cascade (Off / Basic / Standard / Strict).

Architecture Decisions

The full ADR series is documented inline. Each ADR captures one design decision the Rust compiler cannot enforce, along with the context, alternatives considered, and consequences.

ADRTitle
ADR-001Rust Client Architecture Patterns
ADR-002OS GUI Interaction Boundary and Runtime Split
ADR-003Directory Module Pattern for Large Source Files
ADR-004Tauri v2 Migration
ADR-005Tauri v2 Governance
ADR-006Tauri IPC Command Contract
ADR-007Async Runtime Safety Patterns
ADR-008Network Resilience Patterns
ADR-009Client Architecture Baseline
ADR-010Local Integration Harness Boundary
ADR-011Standalone Analysis Pipeline
ADR-012Adaptive Tiered Memory
ADR-013LLM Segment Summary + Vector RAG
ADR-014Tauri Managed State Boundary
ADR-015Frame Storage Port Abstraction
ADR-016Config Change Bus
ADR-017FeedbackSignalSink
ADR-018RegimeManager Persistence
ADR-019Error Code Infrastructure

The same files live under docs/architecture/ in the source repository — the Docs site is the rendered, link-rewritten version.

Next