Architecture Overview
Design Philosophy
Section titled “Design Philosophy”Inklings is a polyglot modular monolith following Clean Architecture with compile-time layer enforcement via separate Rust crates. The core principle: dependencies flow inward — framework and infrastructure layers depend on application and domain, never the reverse.
Three foundational decisions shape the architecture:
- Rust owns the domain — All business logic, validation, and data integrity rules live in Rust. The TypeScript frontend is a “dumb pipe” that handles only UI/UX behavior.
- SQLite per workspace — Each workspace is a single SQLite database file (
inklings.db). Local-first by default, with optional cloud sync via Supabase. - CRDT-backed content — Block content uses Loro CRDTs for persistent undo/redo and multi-device sync. The CRDT binary is the source of truth; materialized text is a derived projection for search indexing.
Clean Architecture Layers
Section titled “Clean Architecture Layers”Domain Layer (crates/domain/)
Section titled “Domain Layer (crates/domain/)”Pure business entities with zero external dependencies. Every struct, enum, and validation rule here is framework-agnostic and infrastructure-agnostic.
Key contents:
- Entity types:
Page,Block,Tag,Workspace,Attachment,Bookmark,EventLogEntry - Value objects:
WorkspaceName(filesystem-safe validation),Layout(CSS Grid template validation),IconConfig - Enums:
PageType,BlockContentType,Capability,EntityType,EventType - Domain errors and validated identifier newtypes
See the Entity Catalog for the complete auto-generated inventory.
Application Layer (crates/application/)
Section titled “Application Layer (crates/application/)”Use cases and service trait abstractions. Each use case is a single-purpose struct following the file-per-use-case pattern:
crates/application/src/page/├── mod.rs # Re-exports├── services.rs # PageRepository trait + errors├── get.rs # GetPageUseCase├── create.rs # CreatePageUseCase└── update.rs # UpdatePageUseCaseThe application layer depends only on domain. Infrastructure is injected via trait objects (e.g., Box<dyn PageRepository>), enforcing the dependency inversion principle at compile time.
Infrastructure Layer (crates/infrastructure/)
Section titled “Infrastructure Layer (crates/infrastructure/)”Concrete implementations of application-layer traits:
| Crate | Purpose |
|---|---|
sqlite | SQLite storage — WAL mode, 1 writer + 4 reader pool |
onnx | ONNX Runtime embedding (snowflake-arctic-embed-m-v2.0) |
supabase | Cloud services — auth, sync, analytics, realtime, storage |
llm | Multi-provider LLM abstraction via Rig crate |
agent-core | Custom agent loop, process model, tool abstraction |
agent-harness | Agent harness with skill execution, prompt engine, MCP bridge |
task-runner | Background task execution (event-driven + scheduled) |
import | External markdown import (Obsidian, folders) |
image | Image processing (resize, WebP conversion) |
json | JSON file-based settings repository |
Framework Layer
Section titled “Framework Layer”In this architecture, the Framework Layer encompasses both the Tauri desktop shell and the React frontend, as both serve as the outermost adapter translating between user interaction and the application core. The React frontend follows a “dumb pipe” pattern — it contains no business logic and delegates all data handling to the Tauri backend via invoke() calls.
Commands (crates/commands/): Transport-agnostic validation, error sanitization, and shared types. Both Tauri and the HTTP bridge use the same command definitions. This crate strips internal error details before they reach the frontend, ensuring user-facing error messages are always safe and meaningful.
Tauri Desktop App (apps/desktop/src-tauri/): Thin adapter layer that wires infrastructure implementations to application use cases via dependency injection (state.rs). Tauri commands are ~32 modules that delegate to commands/use cases.
React Frontend (apps/desktop/src-react/): Single-page app with Zustand state management. Contains zero business logic — all data operations go through Tauri invoke() IPC calls. TipTap editor with Loro CRDT integration.
HTTP Bridge (apps/http-bridge/): Axum REST server that replaces Tauri IPC for QA testing. Same command layer, different transport.
Crate Naming Conventions
Section titled “Crate Naming Conventions”Crate names reflect their Clean Architecture layer and responsibility:
inklings-domain— The single domain crate (no prefix needed within)inklings-application— Use cases and service traitsinklings-commands— Shared validation and error sanitization (framework layer)inklings-sqlite,inklings-onnx, etc. — Infrastructure crates named by technologyinklings-agent-core,inklings-agent-harness— Agent subsystem crates
Type Generation Pipeline
Section titled “Type Generation Pipeline”Rust structs are projected to TypeScript via Specta:
Rust structs (with #[derive(Type)]) → Specta code generation → packages/contracts/generated/bindings.ts → TypeScript consumers import generated typesRun pnpm generate:types after modifying any Rust type that crosses the IPC boundary. Specta preserves snake_case field names (Rust storage_path → TypeScript storage_path, not storagePath).
System Map
Section titled “System Map”The application is organized into three functional groups of systems:
Content Systems
Section titled “Content Systems”Core content management — pages, blocks, and the structures that connect them.
| System | Description |
|---|---|
| Page System | Core content CRUD, hierarchy, blocks |
| Block Content System | Block content types (Markdown, Image) |
| Attachment System | File attachments with SHA-256 dedup |
| Wiki-Link System | Wiki-link parsing, reference index, ghost detection |
| Tag System | Tag management with groups, FTS5, frontmatter bridge |
| Property System | Inline property references and autocomplete |
| Layout System | CSS Grid template areas with per-cell CRDT |
| Loro CRDT System | CRDT integration, binary passthrough, TipTap sync |
| Workspace System | Workspace initialization, switching, lifecycle |
| Type System | Type definitions, properties, container rules |
| Import System | External markdown and Obsidian vault import |
Agent Systems
Section titled “Agent Systems”AI agent integration — the harness, LLM providers, and integration surface.
| System | Description |
|---|---|
| Agent Core System | Agent process model, session management |
| Agent Memory System | Dual-database memory with scope isolation |
| LLM System | Multi-provider LLM abstraction |
| MCP System | In-process MCP server for agent integration |
| Process Model | 4-type agent process model + Context Pipeline |
| Skill System | Multi-artifact skill packages and template engine |
| Scheduling System | Background task scheduling |
| Prompt Injection Boundary | Trust boundary enforcement |
Platform Systems
Section titled “Platform Systems”Infrastructure that supports all content and agent systems.
| System | Description |
|---|---|
| Auth System | Authentication via Supabase Auth |
| Embedding System | ONNX Runtime embedding pipeline |
| Event Log System | Continuous event log with named bookmarks |
| Frontend System | React frontend architecture and IPC patterns |
| Model Downloader | Auto-download embedding model from HuggingFace |
| Permission System | Capability-based access control |
| Search System | FTS5 + semantic search with intent classification |
| Sync System | Multi-device sync via Supabase |
| Task Runner System | Background task execution |
| User Settings System | Application preferences |
| Write-Effect Coordinator | Side-effect dispatch for writes |
| Command Palette | Keyboard-driven command search |
| First Launch Experience | Onboarding tour |
Storage Architecture
Section titled “Storage Architecture”Per-Workspace Database
Section titled “Per-Workspace Database”Each workspace is a single SQLite database (inklings.db) using WAL mode with a connection pool (1 writer, 4 readers). Key tables:
pages— Page metadata, hierarchy (parent_slug), layout assignmentblocks— Block content:content(TEXT for FTS),content_loro(BLOB, CRDT source of truth)tags,tag_groups,page_tags— Tag taxonomyreferences— Wiki-link index for backlink trackingattachments,attachment_references— File attachments with SHA-256 dedupevent_log— Structural change historybookmarks— Named history anchorsembeddings— Semantic search vectors (768-dim, snowflake-arctic-embed-m-v2.0)
See Database Schema for the complete schema reference.
Agent Databases
Section titled “Agent Databases”Agent memory uses two separate agents.db instances:
- Account-level (
{storage_dir}/agents.db) — User preferences and cross-workspace patterns - Workspace-level (
{workspace_dir}/agents.db) — Workspace-specific knowledge
Scope isolation is enforced by database separation, not row-level filtering.
Settings
Section titled “Settings”Application settings stored as JSON (settings.json). Schema-versioned with migration support.
Development Environment
Section titled “Development Environment”The app uses environment-aware paths to isolate dev/test data from production:
| Environment | Workspaces | Settings |
|---|---|---|
| Dev | {project}/.data/workspaces/ | {project}/.data/settings.json |
| Production | ~/Inklings/Workspaces/ | ~/Library/Application Support/Inklings/settings.json |
Debug builds (pnpm desktop:dev) use dev mode; release builds (pnpm desktop:build) use production mode.
Key Architectural Principles
Section titled “Key Architectural Principles”| Principle | Implementation |
|---|---|
| Dependencies flow inward | Separate crates enforce at compile time — infrastructure cannot import domain directly |
| Vertical slicing | Features implemented end-to-end through all layers, not layer-by-layer |
| CRDT binary passthrough | Never re-serialize a CRDT doc from materialized text — pass the binary through untouched |
| Frontend as dumb pipe | React contains no business logic — only UI/UX behavior |
| Identity vs Auth vs Analytics | Three distinct concerns with separate storage and lifecycle |
| Infrastructure naming discipline | Vendor-specific names (e.g., supabase_*) only in infrastructure layer |
Related
Section titled “Related”- Domain Rules — Business invariants enforced in Rust
- Entity Catalog — Auto-generated domain entity inventory
- Database Schema — Complete schema reference
- Identifier Strategy — Three-tier identifier model: UUID, slug, and ref_code
- Error Handling — Error patterns across layers
Was this page helpful?
Thanks for your feedback!