Skip to content

Repository files navigation

Beholden

A TTRPG Campaign Tracker for DMs and Players

TypeScript React Node SQLite


What Is Beholden?

Beholden is a fast, self-hosted campaign tracker for tabletop RPGs. It has two distinct interfaces that share a single backend:

  • DM App (web-dm) - for Dungeon Masters to manage campaigns, encounters, combat, NPCs, treasure, notes, and the compendium
  • Player App (web-player) - for players to manage their characters, view shared campaign info, and follow along in real time

Everything runs on a single Node.js server with a local SQLite database. No cloud account required.


Features

DM App

  • Campaign, adventure, and encounter management
  • Live combat tracker with initiative, HP, conditions, spell slots, and legendary actions
  • Player roster with HP, AC, conditions, and death saves
  • INPCs (in-party NPCs) with full stat tracking
  • Native JSON monster, item, spell, and rules compendium
  • Treasure and notes per campaign and adventure
  • Real-time sync across all connected clients via WebSocket

Player App

  • Character creation and management
  • Character sheet with stats, HP, AC, speed, and abilities
  • Campaign dashboard showing your assigned campaigns
  • Real-time updates from the DM

Auth & Multi-user

  • JWT-based login with per-user accounts
  • Role-based access: Admin, DM, Player
  • Admins manage users and campaign memberships
  • Players are redirected to the player app automatically; DMs access the full DM interface
  • During active combat, players can open an Engaged Enemies drawer showing damaged hostile combatants as Damaged, Bloodied, or Down without exposing exact HP.

Project Structure

beholden/
|- server/        -> Express API, SQLite, WebSocket
|- web-dm/        -> DM React app (Vite)
|- web-player/    -> Player React app (Vite)
|- shared/        -> Shared styles
|- .env           -> Local configuration
`- start.bat      -> Windows quick-start script

Getting Started (Local)

1. Install

Requires Node.js 18+.

git clone https://github.com/cbgfx/beholden.git
cd beholden
npm install

2. Configure

Create a .env file in the repo root:

BEHOLDEN_SUPPORT=true
BEHOLDEN_RATE_LIMIT_WINDOW_MS=900000
BEHOLDEN_RATE_LIMIT_MAX=5000
WEB_PORT=5173
SERVER_PORT=5174

3. Dev mode

npm run dev
  • DM app: http://localhost:5173
  • Player app: http://localhost:5175
  • API: http://localhost:5174

4. Production build

npm run build
npm start

The server serves both apps and the API from a single port (default 5174):

  • DM app: http://localhost:5174/
  • Player app: http://localhost:5174/player/
  • API: http://localhost:5174/api/

Both app footers display the running server version. Beholden checks the version on the repository's main branch once per day. When a newer version is available, an administrator can select Update Available to launch update-beholden.bat, which performs a fast-forward-only pull, installs dependencies, and rebuilds the applications. Restart the running server after the batch file finishes. Local tracked changes that conflict with an update are left untouched and cause the pull to stop instead of being overwritten.

5. First login

On first run a default admin account is created:

Username: admin
Password: admin

Change the password immediately via Admin -> Users, or set BEHOLDEN_ADMIN_USER / BEHOLDEN_ADMIN_PASS in .env before the first run.


Environment Variables

Variable Default Description
PORT 5174 Server port (Railway injects this automatically)
SERVER_PORT 5174 Dev-mode server port
WEB_PORT 5173 Dev-mode DM app port
WEB_PLAYER_PORT 5175 Dev-mode player app port
BEHOLDEN_DATA_DIR Platform default Directory for the SQLite database and uploaded images
BEHOLDEN_DB_PATH <data_dir>/beholden.db Override the database file path
BEHOLDEN_ADMIN_USER admin Initial admin username (used only on first run)
BEHOLDEN_ADMIN_PASS admin Initial admin password (used only on first run)
BEHOLDEN_JWT_SECRET dev secret Secret for signing JWTs - change this in production
BEHOLDEN_SUPPORT false Show a support link in the UI
BEHOLDEN_RATE_LIMIT_WINDOW_MS 900000 Rate limit window in ms
BEHOLDEN_RATE_LIMIT_MAX 5000 Max requests per window
BEHOLDEN_LOG_EGRESS - Set to true to log large per-request wire egress after compression
BEHOLDEN_LOG_EGRESS_MIN_BYTES 1048576 Minimum compressed response bytes logged when egress logging is enabled; use 0 temporarily to log every response
BEHOLDEN_DEBUG - Set to true for verbose server logs

Deployment (Railway)

Beholden is designed to deploy to Railway as three services from the same repo:

Service Start command Domain
server npm start api.yourapp.com
web-dm npm -w web-dm start dm.yourapp.com
web-player npm -w web-player start player.yourapp.com

Set BEHOLDEN_DATA_DIR=/data and mount a Railway volume at /data to persist the database across deploys.

Set VITE_API_ORIGIN on both web services to point at your server's public URL (e.g. https://api.yourapp.com) so the frontend knows where to connect.


Compendium

Beholden Grand Schema JSON is the strict, portable native format. Editable exports contain one independently manageable category:

  • monsters, items, spells, classes, species, backgrounds, and feats;
  • deck cards;
  • bastion spaces, orders, and facilities.

From Compendium -> Admin, export any loaded category to a Grand Schema JSON file, export all ten categories as one ZIP, or import a single-category document or flat multi-category document. Matching IDs are always replaced.

Legacy XML and conversion-oriented source shapes are not product inputs. Grand Schema entries use explicit structured fields, reject legacy-shaped fields, and never depend on parsing descriptive prose to discover mechanics.

Adventure version 2 files can embed native compendium batches. Their entries are installed before the adventure, allowing a portable adventure to bring its own monsters, items, spells, and other rules content. The compendium is shared across all campaigns.


Tech Stack

  • Backend: Node.js, Express, better-sqlite3, WebSocket (ws), JWT auth, sharp
  • DM Frontend: React, TypeScript, Vite, React Router
  • Player Frontend: React, TypeScript, Vite, React Router
  • Database: SQLite (single file, no external server)

License

MIT - free to use, modify, and self-host.

About

Beholden TTRPG Campaign Tracker

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages