Skip to content
8bit-boomPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1,140 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

N&D World

A self-hosted worldbuilding and lore management system for the Neon & Dragons tabletop RPG campaign. Organize your entire game world — characters, locations, factions, events, items, creatures, and more — with built-in AI assistance, image generation, interactive maps, and visual relationship boards.

New to running the app day-to-day? docs/wiki/Home.md is a full usage guide for GMs and players — worldbuilding, characters, maps, sessions, AI tools, settings — everything below the install steps.

AI agents / assistants working with this repo: see AGENTS.md first — it covers content-creation via the general importer (docs/AI_ENTITY_GUIDE.md, docs/AI_SCHEMATIC_GUIDE.md) and conventions to follow if modifying the app code. For a full list of every HTTP route and MCP tool the app exposes, see docs/API_REFERENCE.md.


Features

  • Multi-world support — create and switch between separate game worlds, each with its own colour accent, font, and font size
  • 10 entity types — Characters, Locations, Organizations, Creatures, Events, Items, Feats, Notes, Races, Professions — each with TTRPG-specific subtypes, plus GM-defined custom kinds
  • Entity relationships — link any entity to any other; navigate connections from the detail page
  • Automatic entity linking — mentioning another entity's name in a body or note wikilinks it automatically, with no manual [Name](url) needed
  • Entity notes — attach multiple discrete notes to any entity, each independently hideable/un-hideable from players regardless of the entity's own visibility (e.g. reveal one detail about a location while keeping the rest secret). Type one directly, or import from a file — .md/.txt/.pdf become plain text, .html/.htm convert to markdown by default (or keep original formatting, sanitized, with "preserve original formatting" checked), images become an embedded note
  • Custom entity fields & stat blocks — Field Templates add structured data (age, title, alive/dead/missing status, a repeatable combat stat block, or any fields you define) to Characters, Creatures, or any other entity kind, on top of the usual free-text body — ships with built-in "NPC Details" and "Stat Block" templates (see Entity Field Templates)
  • Folder organization — hierarchical folders per entity type for large lore collections; the entity form offers a picker of existing folders (still free-text for new ones), and any folder you're browsing can be renamed/moved or removed (entities become Unfiled) from its breadcrumb
  • Image attachments — upload JPG/PNG/GIF/WebP/AVIF images to any entity (SVG is deliberately rejected — it can carry <script> and would be served from the app's own origin)
  • Rules viewer — built-in core rules rendered from Markdown with auto-generated table of contents
  • Interactive maps — add custom markers and region overlays to map images
  • Schematics — SVG-based canvas editor for drawing station/dungeon layouts
  • Investment boards — node-and-edge graph boards for plotting organization structures and story threads
  • AI chat — Ollama LLM integration with streaming responses and world-lore RAG context
  • Talk to NPCs — persistent in-character conversations with any entity (saved per user × NPC, so every player has their own private thread); GMs get model/thinking/lore-retrieval toggles, players get it under the same per-world "players can use Ask AI" opt-in as the other AI surfaces
  • AI Models tab — download, manage, and delete Ollama models directly from the app UI with live progress bars; popular model quick-picks included
  • Ollama tuning from the browser — full per-request generation options (temperature, sampling, context length, and more) plus server-level tuning (flash attention, KV cache quantization, parallelism, GPU selection, ...) from Settings → System, no .env editing required; a "Detected hardware" panel reads the host's CPU/RAM/GPU and recommends a starting-point configuration per installed model (see Deploying nd-world)
  • AI image generation — SwarmUI or ComfyUI backend with sampler/scheduler, LoRA, VAE, CLIP skip, upscaling, img2img, batch output, and generation history with parameter reuse
  • Image Studio — embedded SwarmUI iframe at /imagestudio
  • AI video generation — generate clips through Unsloth Studio's /v1/videos as durable background jobs (survive page close and server restarts — a restart re-attaches to the Studio-side generation instead of restarting it); finished clips land in the Video library as GM-only drafts
  • Universal character sheets — fully configurable stats, skills, and currency (N&D defaults: STR/DEX/BOD/PER/WIL/INT/CHA/ITU); optional secondary resource tracker
  • Rules-driven Player Character creation wizard — guided Race → Profession → Stats (point-buy) → Feats → Equipment flow implementing the Neon & Dragons Core Rules character creation procedure, backed by the same race/profession/feat/equipment catalog used by the NeonDragonsApp Android app and NeonDragonsEditor desktop tool
  • Custom character systems — not locked into N&D: a Sheet Template can fully replace the N&D sheet with its own fields (including repeatable lists like abilities or a session log) for running a different TTRPG entirely in the same world — ships with a built-in Asterion system alongside N&D (see Custom Character Systems)
  • .ndc character export — export any Player Character as a .ndc file importable directly into both the NeonDragonsApp Android app and the NeonDragonsEditor desktop editor (see Character creation wizard & export)
  • GM & player accounts — one GM account per deployment; invite players by link, each managing their own character. GMs can hide spoiler content (from everyone, or from all but a hand-picked subset of players), toggle party-wide character visibility per world, and send private per-player notes (see Accounts, Invites & Going Public)
  • Full-text search — across names, tags, summaries, and body text
  • JSON export / import — complete world backup and restore with embedded images
  • Mobile-responsive UI — hamburger nav, touch-friendly targets, stacking layouts on phones and tablets
  • Dark / Half-dark / Light themes — toggle in the nav bar (next to your account badge); choice is remembered per browser. A world's custom accent color still applies on top of whichever theme you pick

Table of Contents


Requirements

Requirement Notes
Docker Engine + Docker Compose v2 Required for all install methods
Git For cloning the repository
GPU Optional — CPU-only mode works for Ollama and SwarmUI

Ollama and SwarmUI are included in the Docker stack but off by default — see AI Setup to enable one or both; no separate installation needed either way.


Install on Linux

These instructions work on Ubuntu 22.04/24.04, Debian 12, and most other modern distros.

Fast path: if Docker is already installed, bash scripts/setup.sh does Steps 2–6 below for you (creates .env, sets up your GM login, starts the stack) — see Accounts, Invites & Going Public and docs/DEPLOYMENT.md.

Step 1 — Install Docker

# Remove old versions if present
sudo apt remove docker docker-engine docker.io containerd runc 2>/dev/null

# Install prerequisites
sudo apt update
sudo apt install -y ca-certificates curl gnupg

# Add Docker's GPG key and repository
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine and Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Allow running docker without sudo (log out and back in after this)
sudo usermod -aG docker $USER
newgrp docker

Verify the installation:

docker --version          # Docker version 26.x or newer
docker compose version    # Docker Compose version v2.x or newer

Step 2 — Clone the repository

sudo apt install -y git
git clone https://github.com/8bit-boom/nd-world.git
cd nd-world

Step 3 — Configure the stack

nano docker-compose.yml

Key settings to review:

services:
  world:
    ports:
      - "8080:8000"       # change 8080 if that port is already in use
    environment:
      OLLAMA_MODEL: gemma4:26b        # model to use for AI chat
      SWARMUI_EXTERNAL_URL: ""        # set to http://<your-ip>:7801 for Image Studio iframe

Note: OLLAMA_URL is pre-configured to http://ollama:11434 pointing at the bundled Ollama container. You only need to change it if you want to use a different Ollama instance.

Step 4 — Start the stack

docker compose up -d

By default this starts just nd-world (the app) — SwarmUI (image gen) and Ollama (AI chat) are optional and skipped unless enabled (see AI Setup — set COMPOSE_PROFILES in .env, or use bash scripts/setup.sh, which asks).

Watch the logs:

docker compose logs -f

If you enabled SwarmUI, it performs a first-run setup on initial boot (downloads the ComfyUI backend). This can take 5–15 minutes depending on your connection.

Step 5 — Download an AI model

Open the app at http://localhost:8080 and go to AI → 🤖 Models tab. Click any model in the Popular Models grid (e.g. Llama 3.2 3B for a fast 2 GB download, or Gemma 4 26B for the best quality) — the progress bar shows download percentage and GB transferred. Once complete, the model is ready for chat.

Alternatively, pull from the command line:

docker compose exec ollama ollama pull gemma4:26b

Step 6 — Open the app

To access from other devices on your network, replace localhost with your machine's LAN IP:

ip addr show | grep "inet " | grep -v 127.0.0.1
# example: inet 192.168.1.100/24

Also set SWARMUI_EXTERNAL_URL: "http://192.168.1.100:7801" in docker-compose.yml to enable the Image Studio iframe, then restart: docker compose restart world.

Step 7 — Add image generation models

Ollama's text models and SwarmUI's image checkpoints/LoRAs/VAEs both live under one shared host directory — ./ai-models/ next to docker-compose.yml by default, or wherever AI_MODELS_DIR in .env points (see Environment Variables) — instead of separate Docker-managed volumes, so there's one place to drop files, back up, or point at models you already have elsewhere. Just copy checkpoint files in directly:

cp your-model.safetensors ./ai-models/swarmui/Stable-Diffusion/

Then open the AI → Image Gen tab, click the model dropdown refresh button — your model will appear.

Upgrading an existing install: if you already had SwarmUI/Ollama running before this change, your models are sitting in the old nd-world_swarmui-models/nd-world_ollama-data Docker volumes, which docker compose up -d will no longer mount. Copy them into the new location once, then the old volumes can be removed:

mkdir -p ai-models/swarmui ai-models/ollama
docker run --rm -v nd-world_swarmui-models:/from -v "$(pwd)/ai-models/swarmui":/to alpine cp -a /from/. /to/
docker run --rm -v nd-world_ollama-data:/from -v "$(pwd)/ai-models/ollama":/to alpine cp -a /from/. /to/
docker compose up -d
docker volume rm nd-world_swarmui-models nd-world_ollama-data   # once you've confirmed everything still works

Managing the service

docker compose stop          # stop all containers
docker compose start         # start them again
docker compose down          # stop and remove containers (data volumes are kept)
docker compose down -v       # ⚠ also removes volumes — deletes all data
docker compose pull          # pull latest images
docker compose up -d --build # rebuild and restart after code changes

An update no longer discards whatever background jobs were mid-flight — audio transcription/summarization checkpoints per chunk and resumes automatically on the next boot; an in-flight image generation or chat completion restarts from the same saved request. See docs/DEPLOYMENT.md for the details.


Install on Windows

These instructions cover Windows 10 (21H2+) and Windows 11 using Docker Desktop with WSL 2.

Step 1 — Enable WSL 2

Open PowerShell as Administrator and run:

wsl --install

Reboot when prompted. After reboot, a terminal will open to finish Ubuntu setup — create a Linux username and password.

Verify WSL 2 is the default:

wsl --set-default-version 2
wsl --list --verbose

Step 2 — Install Docker Desktop

  1. Download Docker Desktop from docker.com/products/docker-desktop
  2. Run the installer — keep "Use WSL 2 instead of Hyper-V" checked
  3. After installation, start Docker Desktop and wait for the Docker engine to show Running in the taskbar
  4. Open Settings → Resources → WSL Integration and enable integration for your Ubuntu distribution

Verify in a PowerShell or WSL terminal:

docker --version
docker compose version

Step 3 — Install Git and clone the repository

Download and install Git from git-scm.com.

Then in Git Bash, PowerShell, or WSL:

git clone https://github.com/8bit-boom/nd-world.git
cd nd-world

Tip: For best performance, clone inside the WSL filesystem: open WSL terminal → cd ~ → then clone.

Step 4 — Configure the stack

Open docker-compose.yml in VS Code or Notepad:

services:
  world:
    ports:
      - "8080:8000"
    environment:
      OLLAMA_MODEL: gemma4:26b
      SWARMUI_EXTERNAL_URL: "http://localhost:7801"   # localhost works on Windows for iframe

Windows note: host.docker.internal is automatically available on Docker Desktop for Windows — no extra configuration needed. The bundled Ollama container connects internally via http://ollama:11434.

Step 5 — Start the stack

docker compose up -d

Check all three containers started:

docker compose ps
# nd-world, swarmui, ollama should all show "Up"

Step 6 — Download an AI model

Open http://localhost:8080 → AI → 🤖 Models → click any popular model chip to download it with live progress.

Or via terminal:

docker compose exec ollama ollama pull gemma4:26b

Step 7 — Open the app

To make the app accessible to other devices on your network:

  1. Find your LAN IP: PowerShell → ipconfig → look for IPv4 Address under your adapter
  2. Open Windows Firewall for the ports:
    # Run as Administrator
    New-NetFirewallRule -DisplayName "nd-world" -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow
    New-NetFirewallRule -DisplayName "SwarmUI"  -Direction Inbound -Protocol TCP -LocalPort 7801 -Action Allow
    New-NetFirewallRule -DisplayName "Ollama"   -Direction Inbound -Protocol TCP -LocalPort 11434 -Action Allow

Managing the service on Windows

docker compose stop          # stop containers
docker compose start         # start them again
docker compose down          # remove containers (volumes kept)
docker compose up -d --build # rebuild after code changes

Or manage containers visually via the Docker Desktop GUI under the Containers tab.


Install on TrueNAS SCALE

TrueNAS SCALE uses truenas-compose.yml which pulls pre-built images from GitHub Container Registry and stores data in host bind-mount paths on your ZFS pool.

Step 1 — Create dataset structure

In the TrueNAS web UI go to Datasets and create the following datasets under your pool (e.g., DeadPool):

Dataset path Purpose
DeadPool/apps/nd-world nd-world database and uploads
DeadPool/apps/swarmui/data SwarmUI configuration
DeadPool/apps/swarmui/models Checkpoint/LoRA model files
DeadPool/apps/swarmui/dlbackend ComfyUI backend (auto-downloaded)
DeadPool/apps/ollama Ollama model storage
DeadPool/apps/nd-world-ollama-config Shared with nd-world — lets Settings → System save Ollama server tuning without editing this file (see docs/DEPLOYMENT.md)

Or create them all via SSH shell:

mkdir -p /mnt/DeadPool/apps/nd-world
mkdir -p /mnt/DeadPool/apps/swarmui/{data,models,dlbackend}
mkdir -p /mnt/DeadPool/apps/ollama
mkdir -p /mnt/DeadPool/apps/nd-world-ollama-config

If your pool is named differently, search and replace DeadPool throughout truenas-compose.yml.

Step 2 — Edit truenas-compose.yml

nano truenas-compose.yml

Set your TrueNAS IP address:

services:
  world:
    environment:
      OLLAMA_MODEL: gemma4:26b
      SWARMUI_EXTERNAL_URL: "http://192.168.1.xxx:7801"  # your TrueNAS IP

If your datasets are on a different pool or path, update all volume bind mounts (search and replace /mnt/DeadPool).

Step 3 — Deploy via Custom App

  1. In the TrueNAS SCALE web UI, go to Apps → Discover Apps
  2. Click Custom App (top right)
  3. Fill in the form:
    • Application Name: nd-world
    • Custom Config: switch to Compose mode
    • Paste the full contents of your edited truenas-compose.yml
  4. Click Install

TrueNAS will pull the images and start all containers (world, swarmui, ollama, watchtower).

Step 4 — Open the portals

After deployment, TrueNAS registers portal buttons automatically from the net.ix-portals.* labels:

Service Port URL
nd-world 8087 http://<truenas-ip>:8087
SwarmUI 7801 http://<truenas-ip>:7801
Ollama 11434 http://<truenas-ip>:11434

These appear as clickable portal buttons on the app's tile in Apps → Installed Apps.

Step 5 — Download an AI model

Open nd-world → AI → 🤖 Models tab → click a popular model to download it, or:

docker exec -it <ollama-container-name> ollama pull gemma4:26b

Step 6 — Place image generation checkpoints

Copy .safetensors or .ckpt files into the models directory:

cp your-model.safetensors /mnt/DeadPool/apps/swarmui/models/Stable-Diffusion/

Then open the AI → Image Gen tab and refresh the model dropdown.

Step 7 — Install ComfyUI-Manager (optional)

With the legacy SwarmUI stack deprecated, this helper script was removed. Install ComfyUI-Manager manually if you still run the legacy swarmui profile.

Auto-updates (Watchtower)

truenas-compose.yml includes Watchtower, which checks every 5 minutes for new image versions and restarts affected containers automatically. To disable auto-updates, remove or comment out the watchtower service block.


Environment Variables

Variable Default Description
DB_PATH /data/world.db Path to the SQLite database file
OLLAMA_URL http://ollama:11434 URL of the Ollama instance (pre-configured to bundled container) — legacy backend, used only while UNSLOTH_API_KEY is unset
OLLAMA_MODEL gemma4:26b Default LLM model for AI chat (legacy Ollama backend)
UNSLOTH_URL http://unsloth:8000 Unsloth Studio URL — the single AI backend (chat + image gen) when an API key is set
UNSLOTH_API_KEY (empty) Studio API key (create in Studio → Settings → API). Setting this switches AI chat and image generation to Unsloth; blank = legacy Ollama/SwarmUI backends
UNSLOTH_MODEL unsloth/gemma-4-26B-A4B-it-GGUF Chat model — a repo id downloaded in Studio's Model Hub
UNSLOTH_IMAGE_MODEL unsloth/z-image-turbo-GGUF Image model downloaded in Studio's Model Hub
UNSLOTH_STUDIO_PASSWORD (empty) Login password for the Studio web UI (port 8000)
LLM_CONTEXT_TOKENS 16384 The load-time context window the Studio chat model was loaded with — sizes the recap chunking math (llama.cpp fixes context at load, unlike Ollama's per-request num_ctx)
IMAGEGEN_TYPE swarmui Legacy image generator backend: swarmui or comfyui. Ignored while the Unsloth backend is active
IMAGEGEN_URL http://swarmui:7801 Internal URL of the image generator API
SWARMUI_EXTERNAL_URL (empty) Browser-accessible SwarmUI URL for the Image Studio iframe
ND_ALLOWED_HOSTS * Comma-separated allowed Host headers (security hardening)
SECRET_KEY (random each restart) Signs session cookies — set a fixed value in production (openssl rand -hex 32), or logins won't survive a restart
GM_EMAIL / GM_PASSWORD (empty) Bootstraps the GM account on first start. Leave blank if the GM account already exists
GM_NAME GM Display name for the bootstrapped GM account
GM_PASSWORD_RESET (empty) Set (alongside GM_EMAIL) and restart to force-reset a locked-out GM's password — the GM has no one else with admin rights over their own account, unlike a player (who the GM can reset from the world's Members list). Logs out every session/trusted device for that account. Remove it after the restart — it re-applies (and re-logs-everyone-out) on every boot while set
COOKIE_SECURE false Set true once served over HTTPS (see Accounts, Invites & Going Public)
COMPOSE_PROFILES (empty) Not read by the app itself — Docker Compose reads it to decide which optional services to start. Empty starts just world; set any comma-separated combination of unsloth, android, editor — or the legacy pair ollama,swarmui (kept until the migration cutover; don't mix with unsloth)
AI_MODELS_DIR ./ai-models Not read by the app itself — Docker Compose reads it to pick where Ollama's text models and SwarmUI's image checkpoints/LoRAs/VAEs are stored on the host (in ollama/ and swarmui/ subfolders), instead of separate Docker-managed volumes. Only matters if the corresponding profile(s) are enabled

AI Setup

Unsloth Studio is the primary AI backend — it serves both AI chat and image generation — and speech-to-text for audio attachments and session recordings — from a single container. The legacy Ollama (chat) + SwarmUI (image) pair is still shipped for rollback, but everything below marked "legacy" is scheduled for removal after the migration cutover — see docs/UNSLOTH_PHASE0_FINDINGS.md.

Unsloth Studio (chat + images)

Unsloth Studio is defined in all compose files behind the unsloth Compose profile — enable it via bash scripts/setup.sh or set COMPOSE_PROFILES=unsloth in .env and run docker compose up -d. The first boot downloads the Studio image (several GB); chat and image models are then fetched from the Studio's Model Hub.

One-time setup:

  1. Open the Studio UI (port 8000) and log in (username unsloth, password from UNSLOTH_STUDIO_PASSWORD).
  2. Studio → Settings → API → create an API key.
  3. Paste the key into nd-world → ⚙️ Settings → Unsloth (AI backend) → API key. The moment a key is saved, AI chat and image generation switch to Unsloth automatically (no restart); clear the key to fall back to the legacy Ollama/SwarmUI backends.
  4. Download models in Studio's Model Hub. Defaults (overridable via UNSLOTH_MODEL / UNSLOTH_IMAGE_MODEL):
    • Chat: unsloth/gemma-4-26B-A4B-it-GGUF
    • Images: unsloth/z-image-turbo-GGUF

Notes:

  • Context size is fixed at model load (llama.cpp semantics): set Context tokens in Settings to match what the model was loaded with (LLM_CONTEXT_TOKENS, default 16384) so recap chunking math is correct.
  • Idle models can be unloaded from the Studio UI; with an API key set the app reloads on demand.
  • On a Tesla V100 (sm_70), pin the Studio image tag — :latest may not ship sm_70 kernels (see findings doc, I-7) — and use fp16 everywhere (see docs/GPU_SETUP.md).
  • API keys do not survive Studio container recreation — re-create the key and update Settings after a rebuild.

GPU acceleration (incl. the Tesla V100)

Unsloth Studio and the legacy Ollama/SwarmUI pair run much faster with an NVIDIA GPU — see docs/GPU_SETUP.md for the full walkthrough: host driver + nvidia-container-toolkit setup, the ready-made docker-compose.gpu.yml override, TrueNAS SCALE GPU assignment, model recommendations by VRAM (16 GB vs 32 GB V100), the fp16-everywhere / non-flash-attention tuning set for the V100 profile, and the CUDA 13 / driver-580 caveats specific to Volta cards (this same CUDA-13-drops-Volta risk applies to the Unsloth image too — see finding I-7 in docs/UNSLOTH_PHASE0_FINDINGS.md).

Still running the legacy SwarmUI backend? Its own first-run install currently pulls a PyTorch build that cannot run on Volta at all — a device reservation alone doesn't fix this. Run ./fix-swarmui-volta-torch.sh (repo root) after SwarmUI's first start; see GPU_SETUP.md §3b for why and how to check it's still needed after updates.

Ollama (chat) — legacy backend

Ollama is defined in both docker-compose.yml and truenas-compose.yml behind the ollama Compose profile — it only starts if that profile is active.

Downloading models:

The easiest way is via the in-app 🤖 Models tab (AI page):

  • Click any chip in the Popular Models grid to start a download with live progress
  • Or paste a custom model ID (e.g. llama3.2:3b, hf.co/username/model) and click ⬇ Pull & Add
  • Downloaded models appear in the model list with a green status dot and a ✓ Use button to activate them for chat

From the command line:

# Docker Compose
docker compose exec ollama ollama pull gemma4:26b

# TrueNAS (find container name first)
docker ps | grep ollama
docker exec -it <name> ollama pull gemma4:26b

Recommended models by hardware:

Model Size Notes
llama3.2:1b ~0.9 GB Minimum spec — works on anything
llama3.2:3b ~2 GB Good quality, fast on CPU
gemma3:4b ~3 GB Strong reasoning, low RAM
gemma3:12b ~8 GB Best quality under 16 GB RAM
gemma4:26b ~17 GB Best quality — needs 20+ GB RAM or GPU

GPU acceleration:

Uncomment the deploy block in the Ollama service to enable NVIDIA GPU offloading:

  ollama:
    # ...
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

AMD GPU support requires the ROCm image: change image: ollama/ollama to image: ollama/ollama:rocm.

Hybrid RAG + knowledge graph (optional, Obsidian-backed)

AI Chat and the "Ask AI" panel normally answer from the world's own web-edited Entities/Rules text (keyword search — no setup needed, this is always on). A GM who also keeps a separate Obsidian vault of freeform notes can additionally wire that vault in as a parallel knowledge source — it never replaces or requires touching the normal Entity-editing workflow:

  1. Settings → (world edit page): set Obsidian vault path to a folder this server can read (a bind-mounted path for Docker) containing .md notes. Notes can use [[wikilinks]] and simple YAML frontmatter (kind:, tags:, and any relation field whose value is a [[wikilink]], e.g. located_in: "[[Dockside]]").
  2. Pull an embedding model via Ollama — ollama pull nomic-embed-text (the built-in default; a different embedding-capable model can be set instance-wide via app.ai.set_embed_model(), though there's no dedicated settings-page field for it yet).
  3. Click 🔄 Rebuild indexes now on that same settings page whenever the vault changes — a GM action, not automatic — to (re)build two indexes purely derived from the vault (never hand-edited, always safe to rebuild):
    • a semantic vector index (Ollama embeddings, brute-force cosine search) so a question phrased differently from the vault's own wording can still match, and
    • a knowledge graph of typed entity relationships (from wikilinks/frontmatter, resolved by name against existing Entities) for multi-hop questions like "who does this NPC answer to, and who do THEY answer to".

A vault note whose title doesn't match any existing Entity by name still gets indexed for semantic search; it just doesn't contribute graph edges (auto-creating new Entities from unmatched notes may come later). Leave the vault path blank to disable this entirely — a world that never sets it behaves exactly as before.

SwarmUI (image generation) — legacy backend

SwarmUI is defined in all compose files behind the swarmui Compose profile — see AI Setup above to enable it. On first boot it downloads the ComfyUI backend — this takes 5–15 minutes the first time.

Set SWARMUI_EXTERNAL_URL to your host's accessible URL to enable the Image Studio embedded iframe view.

The AI → Image Gen panel supports:

  • Checkpoint model selection with deep subfolder scanning
  • Size presets (512², 768×512, 1024², and more) plus custom W×H
  • Sampler, scheduler, steps, CFG scale, seed
  • Batch generation (1–4 images)
  • LoRA name + weight
  • VAE override, CLIP skip
  • Upscaling — model selector (pulls from SwarmUI Upscale model folder) + ×1.5/×2/×3/×4 scale factor
  • Image-to-image (img2img) — upload an init image and set denoising strength
  • Generation history — last 50 generations stored in browser localStorage with one-click parameter reuse
  • ⚡ Krea 2 GGUF Quick Setup — a one-click panel that downloads a matching Krea 2 Turbo GGUF diffusion model + Qwen3-VL-4B-Instruct text encoder pair, for running Krea 2 in far less VRAM than the full-precision checkpoint (see docs/GPU_SETUP.md §5a for why this matters on a V100). One manual step remains outside nd-world's reach: run install-comfyui-gguf-krea2.sh (repo root) once on the machine where SwarmUI itself runs, to install the ComfyUI custom node Krea 2's GGUF files actually need.

See docs/GPU_SETUP.md for GPU passthrough (incl. sharing one V100 between Ollama and SwarmUI) and Volta-specific performance notes.

Speech-to-text (audio transcription)

Audio is transcribed by Unsloth Studio's /v1/audio/transcriptions — there is no separate transcription service to run. It turns an audio file attached to an AI Chat/Ask AI message into text (so the chat model understands it whether or not it has native audio support), transcribes session recordings (background jobs, one-shot upload and the live-recording panel), and writes transcripts for audio/video library clips.

Download a Whisper model in Studio (Settings → Voice) — large-v3-turbo is a good default — then pick it under ⚙️ Settings → System → Unsloth Studio server → Speech-to-text model and press ▷ Test STT. See docs/DEPLOYMENT.md for the model-name caveats, tuning variables and what was removed with the old whisper.cpp sidecar.

ComfyUI (alternative backend)

Set IMAGEGEN_TYPE=comfyui and IMAGEGEN_URL to your ComfyUI instance address (default port 8188). A checkpoint must already be loaded before sending a request.


Data & Backups

All persistent data lives in the /data volume:

  • world.db — SQLite database with all worlds, entities, and relationships
  • uploads/ — uploaded images and AI-generated images (uploads/ai-images/)
  • diagnostics/ — watchdog evidence: stack dumps captured the moment the app wedges, plus its startup/shutdown journal (see Diagnostics watchdog below)

Export & Backup hub

🎯 Tools → 📦 Export & Backup (GET /export) is the one page for all of this — Full Backup, World Book, World JSON, and Import to World JSON, each with a one-line explanation of when to use it, instead of guessing between similarly-named buttons scattered across the nav bar and the Worlds page.

Export a world

From the hub, download World JSON (single file, or split into separate files for external tooling) — a self-contained JSON export of that world's entities with images embedded as base64.

Import a world

From the hub, use the Import World JSON box to upload a previously exported World JSON file back into the active world.

Bulk image import

On the Import page (🎯 Tools → 📥 Import, separate from World JSON import above), the 🖼 Bulk Image Import section lets you select many portrait/art files at once — each one is matched to an entity in the current world by filename (e.g. Gandalf.png → the entity named "Gandalf"; underscores/dashes and case are ignored, so aragorn_king.png matches "Aragorn" too). Review the matches (a "check match" flag means it wasn't an exact match) and adjust any via the dropdown before importing — files left on "— Skip —" aren't uploaded.

Full backup (recommended)

The World JSON export above only covers one world's entities and lore — it drops characters, combat, quests, parties, sessions, calendar, tables, notes and schematics. For a full-fidelity backup, log in as the GM and download Full Backup from the Export & Backup hub (GET /admin/backup.zip). It streams a zip containing:

  • world.db — a consistent snapshot of the whole database (built with SQLite's VACUUM INTO, so it's safe to download while the app is running — no risk of capturing a half-written page the way copying the live file directly would be)
  • uploads/ — every uploaded and AI-generated image
  • maps/ — all battle-map JSON
  • manifest.json — app name, timestamp, and a row count per table, for sanity-checking a restore

For unattended backups, run scripts/backup.sh (reads GM_EMAIL/GM_PASSWORD from .env, logs in, and downloads the zip), e.g. from cron:

0 3 * * * cd /path/to/nd-world && BACKUP_DIR=/mnt/backups/nd-world KEEP=14 bash scripts/backup.sh >> /var/log/nd-world-backup.log 2>&1

BACKUP_DIR is where zips are written (default ./backups), KEEP is how many to retain before older ones are pruned (default 14).

Restoring from a backup

In-app (recommended): from the Export & Backup hub, use ♻ Restore from Full Backup — upload a previously downloaded Full Backup zip and type the confirmation text shown (the active world's name, since Full Backup spans every world on the instance) to stage it. Staging only extracts and validates the zip; nothing is applied yet, and the app keeps running normally. The next time the container restarts, the staged backup is swapped in automatically — before the app opens its first connection to the database — the current world.db is renamed aside first (never deleted), and uploads//maps/ are merged (files from the backup overwrite same-named files, nothing already on disk is deleted). Restart to actually apply a staged restore:

docker compose restart nd-world

A banner on the Export & Backup hub shows when a restore is staged and waiting, with a button to cancel it before restarting if you change your mind.

Manual (fallback — use if you can't reach the app, e.g. it's already down):

  1. Stop the container: docker compose down (or docker stop nd-world).
  2. Unzip the backup and replace the contents of the /data volume with it:
    # Docker named volume
    docker run --rm -v nd-world-data:/data -v "$(pwd)":/backup alpine sh -c \
      "rm -rf /data/* && unzip -o /backup/nd-world-backup-*.zip -d /data"
    
    # TrueNAS / host bind mount
    rm -rf /mnt/DeadPool/apps/nd-world/*
    unzip -o nd-world-backup-*.zip -d /mnt/DeadPool/apps/nd-world
    Either way, world.db, uploads/, and maps/ should end up directly under /data (not nested inside another folder) — manifest.json can stay alongside them or be discarded, it's only for sanity-checking row counts.
  3. Start the container back up: docker compose up -d.

Raw filesystem copy (fallback)

If you can't reach the app (e.g. it's already down), the old copy-the-volume approach still works but isn't a point-in-time-consistent snapshot if the app was running when you took it:

# Docker named volume
docker cp nd-world:/data ./backup-$(date +%Y%m%d)

# TrueNAS bind mount — just copy the directory
cp -r /mnt/DeadPool/apps/nd-world ./backup-$(date +%Y%m%d)

Diagnostics watchdog

The app freezes silently when its single event loop wedges (a starved DB connection pool, a SQLite write-lock pileup) — unhandled exceptions leave tracebacks, but a hang leaves nothing. An always-on watchdog (app/diagnostics.py) closes that gap:

  • If the event loop stalls past ND_DIAG_STALL_SECONDS (default 15s), or the DB pool saturates, it writes a full dump — every thread's stack, the asyncio task snapshot, pool status — to /data/diagnostics/ at the moment it's stuck, rate-limited by ND_DIAG_DUMP_COOLDOWN_SECONDS.
  • Every startup/shutdown/stall/recovery is journaled to /data/diagnostics/events.log, so after an outage you can tell a kill (startup with no shutdown before it) from a clean restart at a glance.

Read it all from ⚙ Settings → Diagnostics (GM-only): live watcher status, the journal tail, and the captured dumps with one-click viewing — no shell access to the container needed. ND_DIAG_DISABLED=1 turns the whole thing off; see .env.example for every knob.


Character Creation Wizard & Export

/characters/new is a guided, rules-driven Player Character creation wizard implementing the Neon & Dragons Core Rules character creation procedure:

  1. Name — character/player name, portrait
  2. Race — pick from the Standard / Advanced / Exceptional tiers (Consumed by Yellow also picks a Base Race)
  3. Profession — one of the six N&D professions
  4. Stats — 20-point allocation across the 8 stats using one of the three Physical/Mental splits (Eldritch uses a single 16-point pool; Advent AI allocates 10 points across mental stats only, with physical stats locked at 0), with a live derived-stats preview (HP/Shock/CA/Speed/PP/MP)
  5. Feats — required Race, Profession, and Common feat picks plus Free Feat slot(s) (2 for Humans), auto-granted Edge-rank Race/Profession feats, Child of the Black Goat's 16 Ritual feats, and Psyonic's 4 initial Rank 0 psy powers
  6. Equipment — spend a 5000-credit starting budget, with Weapons/Armor restricted to creation-eligible rarities (Simple/Standard)

The wizard is backed by a bundled copy of the same race/profession/feat/equipment catalog (app/game_data/*.json) used by the NeonDragonsApp Android app and NeonDragonsEditor desktop tool (from the sibling UoY-Neon-Dragons rules repo), so characters created here use the same content IDs as those two apps.

Exporting to NeonDragonsApp / NeonDragonsEditor

Every character sheet has an ⬇ Export .ndc button (GET /characters/{id}/export.ndc) that downloads a .ndc file — the same interchange format used by NeonDragonsApp and NeonDragonsEditor for their own character import/export. The file can be imported directly into:

  • NeonDragonsApp (Android) — Character List → import, or via the app's file share/open intent
  • NeonDragonsEditor (desktop) — Manage Characters → Import

No changes are required in either app — both already accept a bare JSON array of character objects in this schema (NeonDragonsApp's legacy decode path, NeonDragonsEditor's native multi-character format), so nd-world only needs to produce a matching file.

Refreshing the bundled game-data catalog

app/game_data/*.json is a point-in-time copy of NeonDragonsApp/app/src/main/assets/data/*.json from the UoY-Neon-Dragons repo. When race/profession/feat/equipment content changes there (after running extract_all_data.py), refresh the copy here:

cp /path/to/UoY-Neon-Dragons/NeonDragonsApp/app/src/main/assets/data/*.json app/game_data/

Custom Character Systems

The Neon & Dragons wizard above isn't the only option — nd-world can run a completely different TTRPG system alongside (or instead of) N&D, using Sheet Templates (world switcher → 🗒 Systems & Templates, or /characters/templates).

A template is one of two modes:

  • Extends the N&D sheet (the original behavior) — every character still has the full N&D sheet (stats, HP, Shock, PP/MP, edges, cyberware, conditions); the template's fields are extra sections layered on top.
  • Fully custom system — the N&D sheet isn't used at all. The character is whatever fields the template defines, rendered on its own page instead of the N&D wizard. Fields can be single values (number/text/text area) or a list field — a repeatable group of sub-fields, for things like a variable-length ability list, inventory, or session log.

A built-in Asterion template (a d10 dice-pool system of Gods and Mythborn — Spark Shield/Flesh/Ichor, Glory, Domain Reclamation) ships as a working example of a fully custom system. To create a character with it, or with any custom-mode template, open 🗒 Systems & Templates and click + Create Character on that template's card — the plain + New Character button on the Characters page always goes to the standard N&D wizard, so existing N&D worlds are unaffected.

To build your own homebrew system: + New Template, choose Fully custom system, then add fields — pick List (repeatable group) as a field's type to define a group of sub-fields (e.g. an "Abilities" list where each entry has a Name, Tier, and Effect). GM-authored templates can be scoped to one world or left global (available everywhere), same as N&D's extra-field templates always were. Custom-system characters skip N&D-only features that don't apply to them (.ndc export, the HP/Speed/CA card badges) since those are Neon & Dragons–specific.

Rules content is per-world too

The Rules page is per-world, not global — a world falls back to the bundled N&D core rules until its GM sets something else. On a world's Edit page, click 📖 Edit Rules to paste in Markdown directly, or upload a JSON file shaped {"rules_md": "...markdown..."} via the import box on that same page (handy for reusing a ruleset across worlds/installs without re-pasting). docs/asterion_rules.md / docs/asterion_rules.json in this repo are the full Asterion rules in both formats, ready to paste or upload for any world using the built-in Asterion character system.

Rules page superpowers & the rules.json overlay

The Rules renderer also supports directive blocks and a statblock fence — :::tip|note|warning|danger|lore|collapse|gm [title] … ::: themed callouts (:::gm content is removed server-side for non-GM viewers), ```statblock cards with a copy button — and an optional rules.json overlay: a per-world JSON file (same import box) that adds icons, custom section titles, reordering, tab groups, and per-section player visibility to the auto-generated Rules page, keyed by the section slugs the TOC generates. Full schema, slug rules, and a worked example: docs/RULES_OVERLAY.md; a step-by-step cheatsheet also ships inside the app on the Rules edit page. The 20 MB MAX_UPLOAD_BYTES default applies to these imports too (raise it via Settings → System → Upload limits if your rules file is bigger).

Want to run a Neon & Dragons world with Asterion's simpler, player-invented-ability engine instead of N&D's fixed feat catalog, while keeping N&D's actual races, professions, origins, and major edges? See docs/nd_asterion_conversion.md — a complete, standalone character-creation and play rulebook built on Asterion's engine: N&D's own Stats and Health/Shock/PP/MP resource layer, a Signature-Stat roll-easing mechanic per race/profession, and every special mechanic each of N&D's 17 races, 6 professions, 5 origins, and 22 major edges grants converted into Asterion abilities.


Entity Field Templates

Character Sheet Templates (above) are for Player Characters specifically. Field Templates are the equivalent for lore Entities — Characters, Creatures, Locations, Organizations, or any other kind — letting you attach structured data on top of the usual name/summary/body, instead of hand-writing it into the body text.

Manage them at 🗒 Field Templates (top of any entity list page). A template has a Kind it applies to (one specific kind, e.g. Creature, or "Any kind" to use it anywhere) and a list of fields:

  • Text / Number / Text Area — simple values
  • Dropdown — a constrained set of options (e.g. Status: Alive/Dead/Missing/Unknown)
  • List (repeatable group) — a variable-length group of sub-fields, e.g. a stat block's "Abilities" list (each entry has its own Name and Effect)

Two built-in templates ship out of the box:

  • NPC Details (Characters) — Title, Age, Gender, Status (Alive/Dead/Missing/Unknown)
  • Stat Block (any kind) — Attack Pool, Defense Pool, Health, Armor, Speed, and a repeatable Abilities list — usable on Characters, Creatures, or anything else that needs quick combat stats

On an entity's create/edit form, pick a template from the Template dropdown (filtered to templates that apply to the currently-selected Kind) and its fields appear immediately — no page reload, and switching templates doesn't lose anything else you've already typed. Filled-in fields show on the entity's detail page, grouped under the section headings the template defines.


Accounts, Invites & Going Public

There is no public signup. One GM account runs the whole deployment, bootstrapped from the GM_EMAIL/GM_PASSWORD environment variables on first start (bash scripts/setup.sh prompts for these interactively and writes them to .env). Every other account is a player, created by redeeming a GM-issued invite link — there's no way to sign up otherwise.

As the GM, open a world's Edit page (world switcher → ⚙ Manage worlds → Edit) to:

  • Create/revoke Invite Links (optionally time- or use-limited) and share the /join/<code> URL with a player — opening it lets them create an account (or log in) and joins them to that world
  • View and remove Members, open 🔒 Notes next to any member for a private note thread with that player (e.g. session hooks meant only for them — visible to you and them, never to the rest of the party), or 🔑 Reset password for a locked-out player/assistant — generates a one-time temporary password shown right there for you to relay to them (Discord, in person, etc.) since there's no email set up to send it automatically. If you (the GM) get locked out yourself, see GM_PASSWORD_RESET above — there's no one else with admin rights over your account to reset it for you.
  • Toggle whether players can see each other's characters (party roster, read-only) for that world
  • Grant Player World Access to read-only browse Maps, Calendar, Quests, Parties, and/or Random Tables — off by default (Maps is on by default, matching how it's always worked), and creating/editing/deleting content in any of these stays GM/GM-Assistant only regardless; a Random Tables player also gets to roll

As a player, once joined to a world you can:

  • Browse its lore, minus whatever the GM has hidden from you — each entity's edit page has a Visibility control with three modes: Everyone (default), GM only (spoilers, secrets, unrevealed content), or Specific players, which hides the entity from the party except for a hand-picked list of players (handy for per-character secrets, faction intel only one PC has access to, etc.)
  • See any notes the GM has attached to a visible entity and un-hidden — the entity's own visibility and each note's visibility are independent, so the GM can show you a location while still keeping a couple of secret notes on it GM-only
  • Create and manage your character(s) — one by default, more if the GM raises the world's Characters per player — via the creation wizard, including live HP/Shock/PP/MP tracking and .ndc export
  • View party members' characters read-only, if the GM has enabled that
  • Read private notes the GM has written to you (🔒 My Notes in the nav)

GM-only tools (AI chat, image generation, world/entity editing, maps, schematics, investigation boards) stay restricted to the GM account regardless. Session logs and campaign-wide notes shared with players don't need a separate feature — use a Note entity (kind "note", subtype "session note"/"lore"/etc.) like any other piece of world content, with visibility set to Everyone or restricted to a subset of the party.

Going public: nd-world is private by default (every page requires login) — see docs/DEPLOYMENT.md for the full walkthrough of running it locally and then exposing it to the internet via a Cloudflare Tunnel (recommended — no router changes needed) so you can send invite links to players who aren't on your home network.


Project Structure

nd-world/
├── app/
│   ├── main.py              # FastAPI routes
│   ├── models.py            # SQLAlchemy ORM models
│   ├── database.py          # DB init and migrations
│   ├── ai.py                # Ollama + image gen integration
│   ├── constants.py         # N&D default stats, skills, currency
│   ├── auth.py              # Password hashing, session/permission dependencies
│   ├── game_catalog.py      # Race/profession/feat/equipment catalog loader for the PC wizard
│   ├── game_data/           # Bundled races/professions/feats/equipment JSON (from UoY-Neon-Dragons)
│   ├── routers/
│   │   ├── ai.py            # /api/ai/* endpoints
│   │   ├── auth.py          # /login, /logout, /join/{code} (invite redemption)
│   │   └── characters.py    # /api/characters/* endpoints, .ndc export
│   └── templates/           # Jinja2 HTML templates
│       ├── ai_chat.html     # AI chat + Image Gen + Models tabs
│       ├── imagestudio.html # SwarmUI iframe page
│       ├── auth/            # Login, join/signup pages
│       └── characters/      # Character sheet, list, creation wizard, edit form
├── static/
│   └── style.css            # All app styles
├── scripts/
│   └── setup.sh             # One-command install: Docker check, .env + GM account, start stack
├── docs/
│   ├── DEPLOYMENT.md        # Local setup + Cloudflare Tunnel walkthrough
│   ├── API_REFERENCE.md     # Every HTTP route + MCP tool: method, path, auth tier, purpose
│   ├── wiki/                # Usage guide for GMs/players (Home.md is the index)
│   ├── AI_ENTITY_GUIDE.md   # For AI agents: entities/templates/characters + the general importer
│   └── AI_SCHEMATIC_GUIDE.md # For AI agents: schematic elements + map overlays
├── AGENTS.md                # Entry point for AI agents/assistants working with this repo
├── docker-compose.yml       # Linux / Windows stack (named volumes)
├── truenas-compose.yml      # TrueNAS SCALE stack (bind mounts)
├── .env.example             # SECRET_KEY / GM_EMAIL / GM_PASSWORD / COOKIE_SECURE template
├── Dockerfile
├── requirements.txt

Ports Reference

Service Port Compose file
nd-world 8080 docker-compose.yml
nd-world 8087 truenas-compose.yml
SwarmUI 7801 both
Ollama 11434 both

Troubleshooting

AI dot stays grey / chat returns errors

  • Open AI → 🤖 Models — the Ollama status indicator shows whether the service is reachable and the URL it's connecting to
  • Check the Ollama container is running: docker compose ps
  • View Ollama logs: docker compose logs ollama
  • If you changed OLLAMA_URL to point at a host Ollama, make sure it's not localhost (which resolves inside the container) — use your LAN IP instead

No models in the Models tab

  • Click ↺ Refresh or go to the Popular Models grid and click a chip to download one
  • The model list is empty until at least one model is pulled

Image generation returns an error

  • Check backend status at /api/ai/imagegen/status in your browser
  • SwarmUI first-run can take 10–15 minutes — check logs: docker compose logs -f swarmui
  • Confirm at least one checkpoint model is present in the models directory
  • The Image Gen model dropdown refresh button (↺) rescans the SwarmUI model folders

Image Studio iframe is blank or shows connection refused

  • SWARMUI_EXTERNAL_URL must be a URL your browser can reach — not an internal Docker hostname like http://swarmui:7801
  • Use your LAN IP: SWARMUI_EXTERNAL_URL=http://192.168.1.xxx:7801
  • On Windows, ensure the Windows Firewall allows port 7801

Models or LoRAs not showing in Image Gen dropdowns

  • The scanner uses depth: 10 for nested subfolders — make sure the files are inside the SwarmUI models directory
  • Click the ↺ refresh button next to the dropdown to force a rescan

Database locked / app won't start

  • Only one nd-world instance should write to the same world.db
  • Recover from a crashed container: docker compose restart world

Port already in use on startup

  • Change the host port in your compose file (left side of the mapping):
    ports:
      - "8090:8000"   # use 8090 instead of 8080

Uploads not persisting after restart

  • Confirm the /data volume is mounted correctly; both world.db and uploads/ must be inside it
  • For TrueNAS: check the dataset path exists and the container has write permissions

SwarmUI models directory is empty after restart (TrueNAS)

  • Make sure the models bind mount is correct in truenas-compose.yml
  • The bind path must exist on the host before the container starts: mkdir -p /mnt/DeadPool/apps/swarmui/models/Stable-Diffusion

License

This repository uses two separate licenses:

  • Application code — MIT. Use it, fork it, self-host it, build on it, no restrictions.
  • Game/lore content (lore/, the core rules, Asterion's rules, the races/professions/feats/equipment catalog, world maps, and schematics) — CC BY-NC-ND 4.0. Free to read and use at your own table with attribution; no commercial use, no redistributing modified versions. This content is planned for future commercial TTRPG book releases for these settings.

See LICENSE and LICENSE-CONTENT.md for the exact scope and full terms.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages