Skip to content

Mimir Architecture

Architecture documentation following the C4 model: Context → Containers → Components → Code.

Mimir is a local-first desktop application for D&D 5e campaign management. It has no cloud dependencies — all data stays on the user’s machine.

┌─────────────────────┐
│ Dungeon Master │
│ (User) │
└──────────┬──────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Mimir │ │ Claude Code │ │ Dungeondraft│
│ Desktop App │ │ (via MCP) │ │ (Map Tool) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ SQLite │ │ MCP Sidecar │ │ .dd2vtt / │
│ Database │◀────│ Server │ │ .uvtt files │
└─────────────┘ └─────────────┘ └─────────────┘

Users: Dungeon Masters preparing and running D&D 5e campaigns.

External systems:

  • Claude Code — AI assistant that manages campaigns via the MCP sidecar server
  • Dungeondraft — Map authoring tool; Mimir imports UVTT maps and generates .dungeondraft_map files
  • 5etools — Source of D&D 5e catalog data (monsters, spells, items, etc.), imported into the database at runtime by the user from the Sources UI (crates/mimir-core/src/import/)

The system is a Cargo workspace with 5 crates, a Vue 3 frontend, and a SQLite database.

┌──────────────────────────────────────────────────────────┐
│ Mimir Desktop App │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ Vue 3 Frontend │ │ Tauri Shell (mimir) │ │
│ │ TypeScript + Pinia │──│ Rust command handlers │ │
│ │ │ │ │ │
│ │ 4 feature modules: │ │ 16 command modules │ │
│ │ campaigns, modules, │ │ AppState management │ │
│ │ characters, sources │ │ │ │
│ └────────────────────────┘ └───────────┬────────────┘ │
│ │ │
│ ┌───────────────────────────┼───────────┐ │
│ │ │ │ │
│ ▼ ▼ │ │
│ ┌────────────────────────┐ ┌──────────────────┐ │ │
│ │ mimir-core │ │ mimir-print │ │ │
│ │ Domain logic, DAL, │ │ Typst-based │ │ │
│ │ services, migrations │ │ PDF generation │ │ │
│ └───────────┬────────────┘ └──────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌────────────────────────┐ │ │
│ │ SQLite Database │ │ │
│ │ 56 tables + FTS5, │ │ │
│ │ WAL mode │ │ │
│ └────────────────────────┘ │ │
└──────────────────────────────────────────────────────┘ │
│
┌──────────────────────────────────────────────────────┐ │
│ MCP Sidecar (mimir-mcp) │ │
│ Separate binary, launched by Tauri externalBin │◀──┘
│ 54 tools across 8 categories │
│ Depends on: mimir-core, mimir-mapgen │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ Map Generator CLI (mimir-mapgen) │
│ Standalone binary + library │
│ Procedural terrain, rooms, polygons → Dungeondraft │
│ No database dependency │
└──────────────────────────────────────────────────────┘
Container Technology Purpose
Vue 3 Frontend TypeScript, Pinia, Tiptap, Tailwind UI: campaign dashboard, character sheets, play mode, player display
Tauri Shell (mimir) Rust, Tauri 2 IPC command handlers, AppState, window management
mimir-core Rust, Diesel ORM Domain models, services, data access layer, migrations
mimir-print Rust, Typst PDF generation: character sheets, spell cards, map prints
mimir-mcp Rust, rust-mcp-sdk MCP server exposing campaign management to AI assistants
mimir-mapgen Rust, noise-rs, clap Procedural map generation with YAML config or biome presets
SQLite Database SQLite 3, WAL mode 56 schema tables plus FTS5 indexes: campaigns, characters, maps, D&D 5e catalog
mimir ──────► mimir-core
│
└─────────► mimir-print
mimir-mcp ──► mimir-core
│
└─────────► mimir-mapgen
(mimir-core, mimir-print, mimir-mapgen have no internal crate dependencies)

The shared library containing all business logic and data access.

┌─────────────────────────────────────────────────────────┐
│ mimir-core │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Models │ │ Services │ │ DAL │ │
│ │ │ │ │ │ │ │
│ │ campaign/ │ │ Campaign │ │ Repository │ │
│ │ catalog/ │◀─│ Character │──│ traits │ │
│ │ character/ │ │ Module │ │ │ │
│ │ │ │ Document │ │ Diesel │ │
│ │ Domain │ │ Map, Token │ │ queries │ │
│ │ types and │ │ Archive │ │ │ │
│ │ enums │ │ Asset │ └──────────────┘ │
│ │ │ │ Homebrew │ │
│ └──────────────┘ │ │ ┌──────────────┐ │
│ │ 24 catalog │ │ Migrations │ │
│ │ services │ │ 29 migrations│ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘

Service layer pattern (per ADR-0005):

Services are stateful structs that borrow a database connection:

pub struct CampaignService<'a> {
pub conn: &'a mut SqliteConnection,
}
impl<'a> CampaignService<'a> {
pub fn new(conn: &'a mut SqliteConnection) -> Self {
Self { conn }
}
pub fn create(&mut self, input: CreateCampaignInput) -> ServiceResult<Campaign> {
// Business logic + database access
}
}

Service inventory:

Category Services
Campaign CampaignService, ModuleService, DocumentService, ArchiveService, AssetService
Character CharacterService, HomebrewService
Map MapService, TokenService
Catalog (24) SpellService, MonsterService, ItemService, ClassService, RaceService, BackgroundService, FeatService, ConditionService, SubclassService, and 15 more

The mimir crate bridges the Vue frontend to core services via Tauri IPC commands.

┌──────────────────────────────────────────────────────────┐
│ mimir (Tauri) │
│ │
│ ┌─────────────┐ ┌──────────────────────────────────┐ │
│ │ AppState │ │ Commands (16 modules) │ │
│ │ │ │ │ │
│ │ AppPaths │ │ campaign character map │ │
│ │ (data_dir, │◀───│ module document print │ │
│ │ db_path, │ │ archive homebrew*3 token │ │
│ │ is_dev) │ │ asset source dev │ │
│ │ │ │ dm_map player_display │ │
│ └─────────────┘ │ catalog/ (8 sub-modules) │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Sidecar Management │ │
│ │ Launches mimir-mcp as externalBin process │ │
│ │ Configured in tauri.conf.json │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘

Request flow:

Vue Component
│ invoke('get_campaign', { id: 42 })
▼
Tauri IPC
│ Deserialize params, inject State<AppState>
▼
Command Handler (commands/campaign.rs)
│ let mut conn = state.get_connection()?;
│ let mut svc = CampaignService::new(&mut conn);
▼
CampaignService (mimir-core)
│ svc.get_by_id(42)?
▼
Diesel Query → SQLite
│
▼
Result<ApiResponse<Campaign>, ApiError> → JSON → Frontend

Feature-based organization with 4 domain modules.

frontend/src/
├── app/ # App bootstrap, router, root layout
├── features/
│ ├── campaigns/ # Dashboard, module listing, homebrew tab
│ │ ├── views/ # CampaignDashboardView, etc.
│ │ ├── components/ # WorldTab, ModulesTab, PCsTab, NPCsTab, HomebrewTab
│ │ └── composables/ # useDashboardState
│ ├── characters/ # Creation wizard, character sheet, level-up
│ │ ├── views/ # CharacterListView, CharacterSheetView
│ │ └── components/ # CharacterCreationWizard, LevelUpDialog
│ ├── modules/ # Module prep, player display
│ │ ├── views/ # ModulePrepView
│ │ └── components/ # ModuleMonsters, ModuleNPCs, ModuleMaps
│ └── sources/ # D&D 5e catalog browser
│ └── composables/ # useCatalogSearch (instantiated 22×)
├── components/ # Shared: TokenPalette, DmMapViewer, print dialogs
├── stores/ # Pinia: campaigns, characters, theme
├── services/ # Tauri command wrappers
└── types/ # TypeScript interfaces (generated from Rust)

Key architectural patterns:

  • Composition API with <script setup> exclusively
  • Pinia setup stores (function-based, not options-based)
  • Tauri invoke() for all backend communication
  • ApiResponse<T> wrapper for consistent error handling
  • 3 themes: light, dark, hyper (CSS variables + Pinia persistence)

Runs as a separate process, sharing the same SQLite database.

┌──────────────────────────────────────────────────────────┐
│ mimir-mcp │
│ │
│ ┌──────────────┐ ┌──────────────────────────────────┐ │
│ │ MCP Server │ │ Tools (54 total) │ │
│ │ │ │ │ │
│ │ rust-mcp- │ │ character (13) campaign (10) │ │
│ │ sdk 0.8 │ │ module (8) map (8) │ │
│ │ │ │ document (6) homebrew (5) │ │
│ │ stdio + │◀───│ mapgen (3) catalog (1) │ │
│ │ streamable │ │ │ │
│ │ HTTP │ │ │ │
│ └──────────────┘ └──────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────────────────────────┐ │
│ │ Database │ │ Claude Code Plugin │ │
│ │ Context │ │ plugin/ directory with: │ │
│ │ │ │ - plugin.json (manifest) │ │
│ │ Shares same │ │ - skills/ (mapgen, mimir-dm) │ │
│ │ SQLite DB │ │ - agents/ (campaign authoring) │ │
│ │ as Tauri app │ │ - hooks/ (event handlers) │ │
│ └──────────────┘ └──────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘

Sidecar lifecycle:

  1. Tauri launches mimir-mcp binary via externalBin configuration
  2. Binary built to crates/mimir/binaries/mimir-mcp-{target-triple}
  3. Build scripts: scripts/build-sidecar.{sh,ps1,mjs}
  4. Must be built before cargo build -p mimir (Tauri requirement)

Standalone library + CLI for generating Dungeondraft-compatible maps.

┌──────────────────────────────────────────────────────────┐
│ mimir-mapgen │
│ │
│ CLI Interface (clap) Pipeline │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ generate │ │ YAML Config │ │
│ │ --config / --preset │ │ │ │
│ │ --output │────────│ ▼ │ │
│ │ --seed │ │ Noise Generation │ │
│ │ validate │ │ │ │ │
│ │ list-presets │ │ ▼ │ │
│ └──────────────────┘ │ Terrain Blending │ │
│ │ │ │ │
│ Core Modules (15) │ ▼ │ │
│ ┌──────────────────┐ │ Object Placement │ │
│ │ noise_gen │ │ (trees, clutter) │ │
│ │ terrain │ │ │ │ │
│ │ objects │ │ ▼ │ │
│ │ paths (roads, │ │ Paths & Water │ │
│ │ rivers, meander) │ │ (exclusion zones) │ │
│ │ water │ │ │ │ │
│ │ rooms │ │ ▼ │ │
│ │ polygons │ │ Rooms / Polygons │ │
│ │ elevation │ │ │ │ │
│ │ contour │ │ ▼ │ │
│ │ biomes (12 │ │ Dungeondraft │ │
│ │ presets) │ │ Format Output │ │
│ │ format │ │ (.dungeondraft_map) │ │
│ │ pipeline │ └──────────────────────┘ │
│ │ distribution │ │
│ │ curves │ │
│ │ assets │ │
│ └──────────────────┘ │
└──────────────────────────────────────────────────────────┘

No database dependency. Operates entirely on YAML config input → file output.

Typst-based document builder with pluggable sections.

┌──────────────────────────────────────────────────────────┐
│ mimir-print │
│ │
│ ┌──────────────┐ ┌──────────────────────────────────┐ │
│ │ PrintService │ │ Renderable Sections │ │
│ │ │ │ │ │
│ │ Template │ │ CharacterSection (2-page sheet) │ │
│ │ resolution + │ │ CharacterBattleCardSection │ │
│ │ PDF compile │ │ SpellCardsSection (3×3 grid) │ │
│ │ │ │ EquipmentCardsSection │ │
│ └──────────────┘ │ MonsterCardSection │ │
│ │ TrapCardSection │ │
│ ┌──────────────┐ │ TiledMapSection + MapPreview │ │
│ │ DocumentBuilder │ TokenCutoutSection │ │
│ │ │ │ MarkdownSection │ │
│ │ Assembles │ └──────────────────────────────────┘ │
│ │ sections into│ │
│ │ multi-part │ ┌──────────────────────────────────┐ │
│ │ documents │ │ Support Modules │ │
│ └──────────────┘ │ markdown → Typst converter │ │
│ │ map_renderer (grid, LOS, tokens) │ │
│ │ Typst World (font/file resolver) │ │
│ └──────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘

Detailed rationale lives in ADRs. Summary:

Decision ADR Rationale
Denormalized catalog data ADR-0001 Child entities (subclasses, subraces) embed full parent data to avoid joins on read
Multi-ruleset content ADR-0002 Content organized by rule system and source for future expansion
JSON columns ADR-0003 Variable/complex fields (spell components, monster traits) stored as JSON
Stateful services ADR-0005 Services borrow &mut SqliteConnection for lifetime-scoped transactions

Database Schema (56 schema tables plus FTS5 indexes)

Section titled “Database Schema (56 schema tables plus FTS5 indexes)”
Category Tables Examples
Campaign (7) campaigns, modules, documents, campaign_sources, campaign_assets, campaign_homebrew_* Core user data
Character (8) characters, character_classes, character_spells, character_inventory, character_feats, character_features, character_proficiencies, character_sources PC/NPC data
Map & Tokens (8) maps, token_placements, light_sources, fog_revealed_areas, map_pois, map_traps, module_monsters, module_npcs Spatial data
D&D Catalog (28) spells, monsters, items, classes, races, backgrounds, feats, conditions, subclasses, etc. Immutable 5etools data
Management (5) catalog_sources, books, spell_classes, spell_subclasses, item_attunement_classes Relationships & metadata
Environment Database Assets
Production ~/Library/Application Support/com.mimir.app/data/mimir.db ~/Library/Application Support/com.mimir.app/assets/
Development ~/Library/Application Support/com.mimir.app/dev/data/mimir.db ~/Library/Application Support/com.mimir.app/dev/assets/

Dev mode activated by debug assertions or MIMIR_DEV environment variable. Paths shown are macOS; Linux uses ~/.local/share/, Windows uses %APPDATA%.

mimir/
├── Cargo.toml # Workspace manifest (5 crates)
├── crates/
│ ├── mimir/ # Tauri desktop application
│ │ ├── src/
│ │ │ ├── main.rs # Entry point, plugin registration
│ │ │ ├── state.rs # AppState, AppPaths, dev/prod separation
│ │ │ └── commands/ # 16 Tauri command modules
│ │ │ └── catalog/ # 8 sub-modules for D&D catalog queries
│ │ ├── frontend/ # Vue 3 + TypeScript application
│ │ │ └── src/
│ │ │ ├── features/ # campaigns/, characters/, modules/, sources/
│ │ │ ├── components/ # Shared: TokenPalette, DmMapViewer, print dialogs
│ │ │ └── stores/ # Pinia: campaigns, characters, theme
│ │ ├── binaries/ # Sidecar output (mimir-mcp-{target})
│ │ └── tauri.conf.json # Tauri config with externalBin sidecar
│ │
│ ├── mimir-core/ # Domain logic library
│ │ └── src/
│ │ ├── models/ # Domain types (catalog/, campaign/)
│ │ ├── services/ # 9 core + 24 catalog services
│ │ │ └── catalog/ # Spell, Monster, Item, Class, Race, etc.
│ │ ├── dal/ # Data access layer
│ │ └── migrations/ # 29 Diesel migrations
│ │
│ ├── mimir-mcp/ # MCP sidecar server
│ │ ├── src/
│ │ │ ├── main.rs # Server bootstrap (stdio + HTTP)
│ │ │ └── tools/ # 8 tool modules (54 tools total)
│ │ └── plugin/ # Claude Code plugin definition
│ │ ├── plugin.json # Manifest
│ │ ├── skills/ # mapgen, mimir-dm, mimir-campaign
│ │ └── agents/ # Campaign authoring agent
│ │
│ ├── mimir-print/ # PDF generation library
│ │ └── src/
│ │ ├── service.rs # PrintService
│ │ ├── builder.rs # DocumentBuilder
│ │ ├── sections/ # 9 renderable section types
│ │ └── map_renderer.rs # Map image rendering
│ │
│ └── mimir-mapgen/ # Map generation library + CLI
│ ├── src/
│ │ ├── main.rs # CLI (generate, validate, list-presets)
│ │ ├── pipeline.rs # MapConfig, generation orchestration
│ │ ├── biomes.rs # 12 biome presets
│ │ └── (13 more modules) # noise, terrain, paths, water, rooms, etc.
│ └── examples/ # YAML configs + generated maps
│
├── docs/ # Documentation (Astro Starlight)
├── scripts/ # Build scripts (sidecar, CI)
└── .metis/ # Project management (ADRs, initiatives)