# newideas99/open-dungeon architecture

> Architecture diagram of the GitHub repository newideas99/open-dungeon, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-16). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/newideas99/open-dungeon
- Repository: https://github.com/newideas99/open-dungeon

## Overview

1. **Overall shape**
   Open Dungeon is a local-first Next.js application with three major runtime pieces: the browser UI, the Next.js server/API layer, and optional local model workers. The app is designed so the browser never talks directly to model backends or the database; those interactions go through server routes.

2. **Primary user interface**
   The UI lives under `src/app` and uses the App Router. `src/app/page.tsx` is the main experience, with `layout.tsx` and `globals.css` providing app shell and styling. The sidebar, composer, story view, character management, settings, and image controls are all client-side features backed by API calls.

3. **Next.js server as orchestration layer**
   The `src/app/api/*` routes are the central coordination boundary. They expose chat CRUD, message edits, character CRUD, story generation, settings defaults, uploads, local-data cleanup, health checks, and image-worker proxying. This server layer owns validation, persistence, prompt assembly, and forwarding requests to external local services.

4. **Local persistence boundary**
   Chats, messages, characters, settings, and story state are stored locally in SQLite via `src/lib/db.ts`. The default database path is `data/local-roleplay.sqlite`. The README also shows filesystem-backed storage for uploaded and generated media under `public/uploads/`, `public/generated/`, and `public/generated/refs/`. Deleting a chat or clearing local data is handled through the API and cascades through this local storage boundary.

5. **Story generation flow**
   The main loop is: user action in the composer → server assembles the story prompt → chosen text backend generates the next passage → response is written back to SQLite and rendered in the chat. `src/lib/story-prompt.ts`, `src/lib/defaults.ts`, `src/lib/runtime-defaults.ts`, `src/lib/text-models.ts`, and `src/lib/types.ts` support this orchestration. The app also keeps a rolling “story so far” summary when older context is compacted out.

6. **Text model backends**
   Text generation is abstracted behind two provider modes: bundled local Ollama and “Connect a server” OpenAI-compatible backends. The defaults and environment wiring in `src/lib/server-env.ts` and related lib files determine base URLs, model selection, context size, and timeouts. This makes the model backend a replaceable external dependency rather than part of the app itself.

7. **Image generation architecture**
   Image generation is optional and isolated behind a separate Python HTTP worker. The Next.js app sends image requests to this worker through `src/app/api/image-worker/route.ts` and related image routes. The worker lives in `image_server/` and `workers/flux_worker.py`, wrapping `ultra-fast-image-gen` backends for FLUX-based scene rendering. The app treats image generation as a service boundary, not an in-process feature.

8. **Narrator tool-to-image bridge**
   The story model can call a `generate_image` tool, and the app converts that tool invocation into a local image request. If the worker is unavailable or the backend does not support tools, the app degrades gracefully and keeps the story flowing. This is an important cross-subsystem integration point between text generation and image generation.

9. **Characters and visual continuity**
   Character portraits are first-class local data. The app stores them in the database and passes them both to the narrator as vision context and to the image worker as reference images. This makes characters a shared asset across the text and image subsystems rather than just UI metadata.

10. **Local file and upload handling**
    User uploads and generated assets are written to disk and served from the Next.js `public/` tree. The `src/app/api/upload/route.ts` and `src/app/api/images/route.ts` endpoints likely mediate this filesystem flow, keeping browser access simple while preserving local-only storage.

11. **Operational scripts and platform launchers**
    The repository includes platform-specific launchers and diagnostics for macOS and Windows, plus smoke tests and image-server checks in `scripts/`. These scripts are not core product logic, but they materially define deployment and runtime topology: one Node app process, optionally one Python image-worker process, and optionally Ollama or another model server.

12. **External dependencies that matter architecturally**
    The app depends on Node.js 22+, SQLite, Ollama or any OpenAI-compatible text backend, and the `ultra-fast-image-gen` repository for image generation. On Apple Silicon the image worker uses a resident MFLUX/MLX path; on Windows/Linux it uses the PyTorch SDNQ path. These backend choices affect the service boundaries and runtime matrix more than the UI code does.

13. **How to diagram it**
    A good architecture diagram should show: browser UI at the top; Next.js server/API in the middle; SQLite and filesystem storage as local persistence; an optional Ollama/OpenAI-compatible text backend on one side; and a separate optional Python FLUX image worker on the other side. The key data flows are chat/state CRUD, story prompt generation, tool-driven image requests, and local media persistence.

Main entry points: [`src/app/api/story/route.ts`](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/story/route.ts) (Story API), [`src/lib/db.ts`](https://github.com/newideas99/open-dungeon/blob/main/src/lib/db.ts) (SQLite layer), [`src/app/page.tsx`](https://github.com/newideas99/open-dungeon/blob/main/src/app/page.tsx) (Main app), [`image_server/optimized_image_server.py`](https://github.com/newideas99/open-dungeon/blob/main/image_server/optimized_image_server.py) (Image server)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_client["Browser UI"]
  node_src_app_page["Main app<br/>ui shell<br/>[page.tsx]"]
  node_src_app_layout["Layout<br/>app shell<br/>[layout.tsx]"]
  node_src_app_globals["Global styles<br/>[globals.css]"]
end

subgraph group_server["Next.js Server"]
  node_api_chats["Chats API<br/>crud api<br/>[route.ts]"]
  node_api_chat_detail["Chat detail<br/>chat api<br/>[route.ts]"]
  node_api_messages["Messages API<br/>message api<br/>[route.ts]"]
  node_api_characters["Characters API<br/>character api<br/>[route.ts]"]
  node_api_character_detail["Character detail<br/>character api<br/>[route.ts]"]
  node_api_story["Story API<br/>orchestration api<br/>[route.ts]"]
  node_api_settings_defaults["Defaults API<br/>settings api<br/>[route.ts]"]
  node_api_upload["Upload API<br/>file api<br/>[route.ts]"]
  node_api_images["Images API<br/>media api<br/>[route.ts]"]
  node_api_image_worker["Worker proxy<br/>image proxy<br/>[route.ts]"]
  node_api_local_data["Local data<br/>cleanup api<br/>[route.ts]"]
  node_api_health["Health check<br/>ops api<br/>[route.ts]"]
  node_lib_story_prompt["Prompt builder<br/>prompt assembly<br/>[story-prompt.ts]"]
  node_lib_text_models["Text models<br/>model config<br/>[text-models.ts]"]
  node_lib_defaults["Defaults<br/>[defaults.ts]"]
  node_lib_runtime_defaults["Runtime defaults<br/>runtime config"]
  node_lib_server_env["Server env<br/>env config<br/>[server-env.ts]"]
  node_lib_types["Shared types<br/>domain types<br/>[types.ts]"]
end

subgraph group_storage["Local Storage"]
  node_lib_db[("SQLite layer<br/>storage adapter<br/>[db.ts]")]
  node_sqlite[("SQLite DB<br/>local database")]
  node_local_files["Local files<br/>filesystem storage"]
end

subgraph group_models["External Backends"]
  node_text_backend[("Text backend<br/>external service")]
  node_image_host["Image server<br/>external service"]
end

subgraph group_worker["Image Worker"]
  node_flux_worker["FLUX worker<br/>python worker<br/>[flux_worker.py]"]
end

node_src_app_layout -->|"wraps"| node_src_app_page
node_src_app_globals -->|"styles"| node_src_app_page
node_src_app_page -->|"uses"| node_api_chats
node_src_app_page -->|"advances"| node_api_story
node_src_app_page -->|"edits"| node_api_characters
node_src_app_page -->|"shows"| node_api_images
node_api_chats -->|"persists"| node_lib_db
node_api_chat_detail -->|"persists"| node_lib_db
node_api_messages -->|"persists"| node_lib_db
node_api_characters -->|"persists"| node_lib_db
node_api_character_detail -->|"persists"| node_lib_db
node_api_story -->|"builds"| node_lib_story_prompt
node_api_story -->|"selects"| node_lib_text_models
node_api_story -->|"reads"| node_lib_defaults
node_api_story -->|"reads"| node_lib_runtime_defaults
node_api_story -->|"configures"| node_lib_server_env
node_api_story -->|"calls"| node_text_backend
node_api_settings_defaults -->|"exposes"| node_lib_defaults
node_api_settings_defaults -->|"exposes"| node_lib_runtime_defaults
node_api_upload -->|"writes"| node_local_files
node_api_images -->|"serves"| node_local_files
node_api_local_data -->|"cleans"| node_lib_db
node_api_local_data -->|"cleans"| node_local_files
node_api_image_worker -->|"proxies"| node_image_host
node_image_host -->|"runs"| node_flux_worker
node_flux_worker -.->|"optional refs"| node_text_backend
node_api_health -->|"checks"| node_image_host
node_api_health -->|"checks"| node_text_backend
node_lib_text_models -->|"types"| node_lib_types
node_lib_story_prompt -->|"types"| node_lib_types
node_lib_db -->|"stores"| node_sqlite

click node_src_app_page "https://github.com/newideas99/open-dungeon/blob/main/src/app/page.tsx"
click node_src_app_layout "https://github.com/newideas99/open-dungeon/blob/main/src/app/layout.tsx"
click node_src_app_globals "https://github.com/newideas99/open-dungeon/blob/main/src/app/globals.css"
click node_api_chats "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/route.ts"
click node_api_chat_detail "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/route.ts"
click node_api_messages "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/messages/[messageId]/route.ts"
click node_api_characters "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/characters/route.ts"
click node_api_character_detail "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/characters/[characterId]/route.ts"
click node_api_story "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/story/route.ts"
click node_api_settings_defaults "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/settings/defaults/route.ts"
click node_api_upload "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/upload/route.ts"
click node_api_images "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/images/route.ts"
click node_api_image_worker "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/image-worker/route.ts"
click node_api_local_data "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/local-data/route.ts"
click node_api_health "https://github.com/newideas99/open-dungeon/blob/main/src/app/api/health/route.ts"
click node_lib_db "https://github.com/newideas99/open-dungeon/blob/main/src/lib/db.ts"
click node_lib_story_prompt "https://github.com/newideas99/open-dungeon/blob/main/src/lib/story-prompt.ts"
click node_lib_text_models "https://github.com/newideas99/open-dungeon/blob/main/src/lib/text-models.ts"
click node_lib_defaults "https://github.com/newideas99/open-dungeon/blob/main/src/lib/defaults.ts"
click node_lib_runtime_defaults "https://github.com/newideas99/open-dungeon/blob/main/src/lib/runtime-defaults.ts"
click node_lib_server_env "https://github.com/newideas99/open-dungeon/blob/main/src/lib/server-env.ts"
click node_lib_types "https://github.com/newideas99/open-dungeon/blob/main/src/lib/types.ts"
click node_local_files "https://github.com/newideas99/open-dungeon/tree/main/public"
click node_image_host "https://github.com/newideas99/open-dungeon/blob/main/image_server/optimized_image_server.py"
click node_flux_worker "https://github.com/newideas99/open-dungeon/blob/main/workers/flux_worker.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_src_app_page,node_src_app_layout,node_src_app_globals toneBlue
class node_api_chats,node_api_chat_detail,node_api_messages,node_api_characters,node_api_character_detail,node_api_story,node_api_settings_defaults,node_api_upload,node_api_images,node_api_image_worker,node_api_local_data,node_api_health,node_lib_story_prompt,node_lib_text_models,node_lib_defaults,node_lib_runtime_defaults,node_lib_server_env,node_lib_types toneAmber
class node_lib_db,node_sqlite,node_local_files toneMint
class node_text_backend,node_image_host toneRose
class node_flux_worker toneIndigo
```

## Components

### Browser UI

App Router UI and client-facing experience

- [Main app](https://github.com/newideas99/open-dungeon/blob/main/src/app/page.tsx): ui shell · Primary chat and story experience · `src/app/page.tsx`
- [Layout](https://github.com/newideas99/open-dungeon/blob/main/src/app/layout.tsx): app shell · Shared app shell and metadata · `src/app/layout.tsx`
- [Global styles](https://github.com/newideas99/open-dungeon/blob/main/src/app/globals.css): styles · App-wide styling layer · `src/app/globals.css`

### Next.js Server

API routes and orchestration

- [Chats API](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/route.ts): crud api · Chat creation, updates, and listing · `src/app/api/chats/route.ts`
- [Chat detail](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/route.ts): chat api · Single-chat operations and lifecycle · `src/app/api/chats/[chatId]/route.ts`
- [Messages API](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/messages/[messageId]/route.ts): message api · Message edits and chat message operations · `src/app/api/chats/[chatId]/messages/[messageId]/route.ts`
- [Characters API](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/characters/route.ts): character api · Character management within chats · `src/app/api/chats/[chatId]/characters/route.ts`
- [Character detail](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/chats/[chatId]/characters/[characterId]/route.ts): character api · Single-character operations · `src/app/api/chats/[chatId]/characters/[characterId]/route.ts`
- [Story API](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/story/route.ts): orchestration api · Assembles prompts and advances the story · `src/app/api/story/route.ts`
- [Defaults API](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/settings/defaults/route.ts): settings api · Exposes default settings and runtime defaults · `src/app/api/settings/defaults/route.ts`
- [Upload API](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/upload/route.ts): file api · Receives local uploads and media assets · `src/app/api/upload/route.ts`
- [Images API](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/images/route.ts): media api · Serves generated and uploaded images · `src/app/api/images/route.ts`
- [Worker proxy](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/image-worker/route.ts): image proxy · Forwards image requests to the separate worker · `src/app/api/image-worker/route.ts`
- [Local data](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/local-data/route.ts): cleanup api · Clears local app data and stored state · `src/app/api/local-data/route.ts`
- [Health check](https://github.com/newideas99/open-dungeon/blob/main/src/app/api/health/route.ts): ops api · Runtime and service health endpoint · `src/app/api/health/route.ts`
- [Prompt builder](https://github.com/newideas99/open-dungeon/blob/main/src/lib/story-prompt.ts): prompt assembly · Builds story context and narrative prompts · `src/lib/story-prompt.ts`
- [Text models](https://github.com/newideas99/open-dungeon/blob/main/src/lib/text-models.ts): model config · Text backend selection and model wiring · `src/lib/text-models.ts`
- [Defaults](https://github.com/newideas99/open-dungeon/blob/main/src/lib/defaults.ts): app defaults · Baseline app and story configuration · `src/lib/defaults.ts`
- [Runtime defaults](https://github.com/newideas99/open-dungeon/blob/main/src/lib/runtime-defaults.ts): runtime config · Environment-sensitive defaults and overrides · `src/lib/runtime-defaults.ts`
- [Server env](https://github.com/newideas99/open-dungeon/blob/main/src/lib/server-env.ts): env config · Server-side endpoint and timeout configuration · `src/lib/server-env.ts`
- [Shared types](https://github.com/newideas99/open-dungeon/blob/main/src/lib/types.ts): domain types · Core application data contracts · `src/lib/types.ts`

### Local Storage

SQLite and filesystem-backed persistence

- [SQLite layer](https://github.com/newideas99/open-dungeon/blob/main/src/lib/db.ts): storage adapter · Local database access and persistence · `src/lib/db.ts`
- **SQLite DB**: local database · Default local persistence store
- [Local files](https://github.com/newideas99/open-dungeon/tree/main/public): filesystem storage · Uploaded and generated media on disk · `public`

### External Backends

Optional text and image services

- **Text backend**: external service · Ollama or OpenAI-compatible server
- [Image server](https://github.com/newideas99/open-dungeon/blob/main/image_server/optimized_image_server.py): external service · HTTP host for the Python image worker · `image_server/optimized_image_server.py`

### Image Worker

Separate Python image-generation service

- [FLUX worker](https://github.com/newideas99/open-dungeon/blob/main/workers/flux_worker.py): python worker · Python image-generation worker implementation · `workers/flux_worker.py`

## Connections

- Layout → Main app: wraps · Provides shared app shell for the main experience
- Global styles → Main app: styles · Provides global styling for the UI
- Main app → Chats API: uses · Loads and manages chat state through the server API
- Main app → Story API: advances · Submits user actions to generate the next story turn
- Main app → Characters API: edits · Manages characters from the client UI
- Main app → Images API: shows · Fetches local and generated media
- Chats API → SQLite layer: persists · Reads and writes chat records locally
- Chat detail → SQLite layer: persists · Updates or deletes a single chat and its cascade
- Messages API → SQLite layer: persists · Stores message edits and conversation history
- Characters API → SQLite layer: persists · Stores character records for a chat
- Character detail → SQLite layer: persists · Updates or deletes a single character
- Story API → Prompt builder: builds · Assembles the prompt and story context
- Story API → Text models: selects · Chooses the active text backend and model
- Story API → Defaults: reads · Uses baseline story and app defaults
- Story API → Runtime defaults: reads · Applies runtime-sensitive overrides
- Story API → Server env: configures · Uses server endpoint and timeout settings
- Story API → Text backend: calls · Sends generation requests to the selected text service
- Defaults API → Defaults: exposes · Returns default settings to the client
- Defaults API → Runtime defaults: exposes · Returns runtime-adjusted defaults
- Upload API → Local files: writes · Saves uploaded assets to local disk
- Images API → Local files: serves · Returns stored generated and reference images
- Local data → SQLite layer: cleans · Deletes local persisted app state
- Local data → Local files: cleans · Removes local media and uploads
- Worker proxy → Image server: proxies · Forwards image jobs to the external worker host
- Image server → FLUX worker: runs · Hosts the Python worker implementation
- FLUX worker → Text backend: optional refs · Uses reference imagery and model outputs as inputs to generation
- Health check → Image server: checks · Confirms worker availability when present
- Health check → Text backend: checks · Confirms text backend connectivity when present
- Text models → Shared types: types · Shares model and runtime contracts
- Prompt builder → Shared types: types · Uses shared story data structures
- SQLite layer → SQLite DB: stores · Maps app state to the local SQLite database
