A full-stack web application for Dungeons & Dragons and TTRPG campaign management. Features comprehensive character management, campaign tracking, combat tools, world-building, and AI-assisted content generation.
- Full Character CRUD — Create and manage characters with all D&D 5e stats (abilities, skills, HP, AC, speed, hit dice)
- Multi-Class Support — Characters can have multiple classes with individual levels
- Spellcasting — Full spell slot tracking, spell preparation, and known spells management
- Inventory — Equipment, weapons, armor, and item management with currency tracking
- Features & Traits — Class features, racial traits, and feats with descriptions
- Proficiencies — Armor, weapon, tool, skill, and saving throw proficiencies
- HP Auto-Calc — Automatic HP calculation based on class, level, and CON modifier
- Rest System — Short and long rest with resource recovery
- Level Up — Level advancement with automatic HP and feature progression
- Exhaustion Tracking — Track and update exhaustion levels
- Conditions/Ailments — Condition management with tick system for duration tracking
- Concentration Checks — Automatic concentration save handling
- Character Resources — Class-specific resources (ki points, rage, etc.) with rest recovery
- Character Export/Import — Export characters as JSON, import from JSON
- Character Comparison — Side-by-side comparison of multiple characters
- Character Graph — Visual relationship graph for characters
- Campaign CRUD — Create and manage multiple campaigns
- Campaign Members — Add/remove members with role-based permissions (DM, player)
- Campaign Dashboard — Overview of campaign status, members, and recent activity
- Campaign Wiki — Editable wiki pages per campaign
- Campaign Maps — Upload maps with fog of war, pins, and active map setting
- Campaign Recaps — Write and AI-generate session recaps with email sending
- Campaign NPCs — Link NPCs to campaigns with custom relationships
- Party Inventory — Shared campaign item tracking
- Factions & Reputation — Track factions, reputation scores, and relationships
- Campaign One-Shots — Link one-shot adventures to campaigns with reordering
- Campaign Graph — Relationship mapping for campaign entities
- Combat Tracker — Initiative order, turn management, HP tracking, status effects
- Encounter Builder — Build encounters with monsters from compendium/library
- Encounter XP Calculation — Auto-calculate XP difficulty thresholds
- Combat Log — Detailed combat event logging with stats
- Monster Library — Custom monster entries with full stat blocks
- Locations — CRUD for locations with character and NPC associations
- NPCs — Full NPC management with interaction logging
- Quests — Quest tracking with status updates
- Journal — Character journal entries for session notes
- Timeline — Campaign timeline events
- Conditions Library — Manage custom conditions and ailments
- Companions — Animal companions, familiars, and mounts
- Feats — Custom feat creation and management
- Downtime Activities — Track character downtime with day advancement
- Full Adventure CRUD — Create one-shot adventures with acts and scenes
- Scene Management — Scene durations, dialog ordering, and reordering
- Pacing System — Start/pause/resume pacing sessions, track time per scene
- Clue/Mystery Tracker — Create clues with dependencies, NPC/location links, reveal/hide
- Prep Checklist — Track preparation tasks per adventure
- DM Screen — Quick reference for adventure details during sessions
- DM Notes — Private notes per adventure
- Linked Entities — Link NPCs, locations, encounters, characters, items, monsters, shops per adventure
- NPC-Item Links — Track which NPCs carry which items
- Act-Level Monsters — Assign monsters to acts and scenes
- Shop Management — Create shops with item listings and pricing
- Buy/Sell System — Characters can buy from and sell to shops
- Transaction Log — Complete purchase/sale history
- NPC generation
- Name generation
- Encounter generation
- Loot generation
- Random character generation
- Adventure hook generation
- Dungeon dressing
- Tavern generation
- Urban and road encounters
- Weather generation
- Text Generation — AI-powered content generation for recaps, descriptions, etc.
- Image Generation — AI image generation for characters, locations, etc.
- AI Endpoint Management — Configure multiple AI providers with API keys
- Campaign Recap Generation — AI-generated session recaps from notes
- Full Dice Engine — Roll any dice notation (3d6, 2d20+5, etc.)
- Check Rolls — Ability checks with automatic success/failure
- Roll History — View past dice roll results
- Initiative Rolling — Automatic initiative for combat
- Dynamic Schemas — Define custom compendium types with flexible field schemas
- Compendium Entries — Full CRUD with FTS5 full-text search
- Bulk Operations — Batch delete, batch update compendium entries
- Import/Export — CSV/JSON import with field mapping, auto field type detection, export to JSON
- Import Logs — Track import history with rollback support
- D&D 5e API Fallback — Fetch equipment, spells, races, classes, monsters, features, magic items
- Preloaded Compendium — JSON seed data in
data/directory - Compendium Schemas — Built-in schemas for Races, Classes, Spells, Equipment, Monsters, Feats, Backgrounds
- User Management — Create, edit, delete users with password reset
- Email Settings — SMTP configuration, test emails, campaign highlight emails
- AI Endpoint Management — Full CRUD for AI provider endpoints with testing
- OTel Settings — OpenTelemetry configuration
- Umami Analytics — Analytics settings
- Application Logs — Central log viewer with level controls
- Shop Management — Global shop and shop item management
- Compendium Schema Management — Define schemas, manage entries, bulk operations
- OpenTelemetry Tracing — Request-level tracing to OTLP backends
- Prometheus Metrics —
/metrics/prometheusendpoint - Structured Logging — slog-based logging with OTel log export
- Health Check —
/healthzendpoint
- Media Uploads — Image upload with cropping support, upload links
- Share Links — Create shareable links for characters and entities
- WebSocket — Real-time updates for collaborative sessions
- Backup System — Configurable automated backups with scheduler
- Pregenerated Characters — Create and manage pregens with party balance checker
- Level-Up Planner — Plan future levels with suggestions
- Character Print View — Print-optimized character sheet view
- Homebrew Content Manager — Custom homebrew content with type-based organization
- Session Plans — Plan upcoming sessions with agenda and notes
- Quick Reference — Rules quick reference for common lookups
- Party View — See all party members' status at a glance
docker compose up -dOpen http://localhost:6280 in your browser.
# Install dependencies
go mod download
# Build with FTS5 support
CGO_ENABLED=1 go build -tags fts5 -o villum-server .
# Run
./villum-server| Variable | Default | Description |
|---|---|---|
PORT |
6270 |
HTTP listen port |
DB_PATH |
villum.db |
SQLite database path |
MEDIA_PATH |
{DB_DIR}/media |
Media upload directory |
DOCKER |
false |
Docker-specific paths (auto in Dockerfile) |
OIDC_ENABLED |
true |
Enable OIDC SSO login via Authelia (set false to disable) |
OIDC_ISSUER_URL |
— | OIDC issuer, e.g. https://authelia.vandijke.xyz |
OIDC_CLIENT_ID |
— | OIDC client id (Authelia client, e.g. dnd) |
OIDC_CLIENT_SECRET_FILE |
— | Path to file holding the client secret (preferred) |
OIDC_CLIENT_SECRET |
— | Client secret (dev only; prefer the _FILE variant) |
OIDC_REDIRECT_URL |
— | Must match Authelia registration, e.g. https://dnd.vandijke.xyz/api/auth/oidc/callback |
OIDC_SCOPES |
openid email profile groups |
Requested scopes |
OIDC_LOGOUT_URL |
{issuer}/logout |
IdP logout override |
Enabled by default once the OIDC_* vars above are set — the login page shows
"Login with Authelia" only when the config is complete. Password login stays
enabled as fallback. Register the client in Authelia (client_id dnd, redirect
https://dnd.vandijke.xyz/api/auth/oidc/callback, scopes openid email profile groups, PKCE S256, explicit consent, one_factor), bypass NPM forward-auth for
the host (it breaks the OIDC callback), then set the vars above. Users link by
verified email on first SSO login; the admins group grants admin. Rollback:
OIDC_ENABLED=false.
dnd/
├── main.go # Application entry point & route setup
├── main_test.go # Tests
├── handlers/ # HTTP handlers organized by domain
│ ├── auth.go # Authentication
│ ├── characters.go # Character CRUD
│ ├── spells.go # Spell management
│ ├── combat.go # Combat tracker
│ ├── campaigns.go # Campaign management
│ ├── compendium.go # Compendium & API fallback
│ ├── npcs.go # NPC management
│ ├── locations.go # Location management
│ ├── shops.go # Shop & economy
│ ├── dice.go # Dice rolling
│ ├── generators.go # Random generators
│ ├── ai.go # AI integration
│ ├── backup.go # Backup system
│ └── ... # Many more handler files
├── db/ # Database initialization & queries
├── models/ # Data structures
├── middleware/ # Auth, CSRF, security, logging
├── dice/ # Dice engine
├── crypto/ # Crypto utilities
├── static/ # Frontend assets (embedded via EmbedFS)
├── ts/ # TypeScript source
├── data/ # Compendium seed data (JSON)
├── scripts/ # Utility scripts
├── Dockerfile # Multi-stage Docker build (Node + Go + Alpine)
├── docker-compose.yml # Docker Compose configuration
└── go.mod / go.sum # Go module dependencies
The data/ directory contains JSON files that seed the compendium database. To customize for any TTRPG system:
- Edit the JSON files in
data/to match your system - Each entry supports
system(e.g., "dnd5e", "pf2e", "generic") andsourcefields - Restart the app on a fresh database, or add a
.forcefile indata/to force reload
The app can fetch compendium data from the D&D 5e API as a fallback:
GET /api/compendium/api/:category?q=searchterm- Supports: equipment, spells, races, classes, monsters, features, magic-items
TRMNL is a dedicated e-ink display that polls JSON endpoints and renders Liquid templates on-device — no browser needed. Villum ships a plugin (in trmnl/) that shows character stats, campaign progress, or the full party roster at a glance.
All endpoints are read-only and public — no token or session is required to poll them:
GET /api/trmnl/character-stats?character_id=<id>— character name, race, class, level, XP, HP, AC, initiative, and the six ability scores with modifiersGET /api/trmnl/campaign-stats?character_id=<id>— session count, total XP/gold earned, quest and rest breakdowns, top NPCs, and dice roll summaryGET /api/trmnl/characters— the full party roster: every character's name, race, class, subclass, level, XP, HP, AC, initiative, ability scores, and character type, ordered by name
Responses are 404 for unknown character_id. Because these endpoints are public, they expose the configured read data by design — do not put sensitive information in character names or stats if you expose your instance publicly.
All mutating API routes (character, campaign, wiki, upload, companion, link, timeline, condition, transfer, and every other create/update/delete) require both a signed-in user session and a bearer API token. Tokens are user-scoped and managed from the app's Settings page (or the admin panel):
- Create a token to receive a one-time secret (
vlt_...). Only the SHA-256 hash is stored; the secret is never shown again. - List tokens to see metadata (name, prefix, created/expires/last-used, status) — never the secret.
- Revoke a token to invalidate it immediately.
- Rotate a token to issue a new secret while keeping its metadata.
Send the token on mutating requests as Authorization: Bearer <secret>. GET requests (including the public TRMNL reads) do not require a token.
- In the TRMNL web app, create a new plugin and upload the files from
trmnl/src/(all four layouts:full,half_horizontal,half_vertical,quadrant) andsettings.yml - Set the custom fields:
url— your Villum instance base URL (e.g.,https://dnd.example.com)character_id— the character to display (not used inrostermode)display_mode—characterfor character stats,campaignfor campaign progress, orrosterfor the full party list
- The plugin polls daily by default (
refresh_interval: 1440minutes); adjust insettings.ymlif needed
The plugin lives in trmnl/ as a trmnlp project (trmnl/src/ + trmnl/.trmnlp.yml). It is linted on every PR (trmnlp lint in the trml CI job) and published to TRMNL on release (trmnlp push --force).
To publish, set the TRMNL_API_KEY GitHub secret (stored as TRML_TOKEN in the release workflow — the same secret name used by the other Martyn van Dijke TRMNL plugins). Without it, the trml release job fails loudly, so set it before the next release.
MIT