- TypeScript 98.5%
- CSS 0.7%
- Python 0.4%
- Shell 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .github | ||
| assets | ||
| backend | ||
| docs | ||
| Examples | ||
| frontend | ||
| nginx | ||
| scripts | ||
| .dockerignore | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| .nvmrc | ||
| CHANGELOG.md | ||
| docker-compose.dev.yml | ||
| docker-compose.override.example.yml | ||
| docker-compose.yml | ||
| FORK.md | ||
| LICENSE | ||
| README.md | ||
| SECURITY.md | ||
| SRD_ATTRIBUTION.md | ||
| SRD_LICENSE.txt | ||
| THIRD_PARTY_LICENSES.md | ||
| THIRD_PARTY_NOTICES.txt | ||
A self-hosted, browser-based Virtual Tabletop (VTT) platform. Designed for ease of use and narrative-driven tabletop RPG campaigns where atmosphere matters more than mechanics.
Self-hosted — you own your data. One instance can host multiple concurrent campaigns run by different GMs for different player groups.
⚠️ Self-Hosted Reality Check
CozyVTT is a self-hosted, community-maintained project run by you on your hardware. It is provided as-is, with no warranty, no SLA, and no central support — see the AGPL-3.0 License for the legal version of this.
You are responsible for the security and uptime of your instance. We do our best (Argon2id passwords, MFA, magic-byte file validation, per-endpoint rate limiting, security headers on the app page, non-root Docker containers — the full list is under Features → Security), but the operating environment is yours.
Recommended deployment posture for a public-facing instance:
- Run on a small VPS rather than your home network
- Front it with a Cloudflare Tunnel (free, hides your origin IP, no open ports needed, automatic DDoS protection) or a similar reverse proxy / WAF
- Keep the instance updated as security patches land — watch this repo
- Set a strong
SESSION_SECRET(32+ char random) and your ownDATABASE_PASSWORD— the production build refuses to start while either is still the placeholder - Use the built-in MFA on the admin account at minimum
- Configure regular database backups (
backend/scripts/backup.sh+ cron — see DEPLOYMENT.md) - Review the Hardening Checklist in docs/DEPLOYMENT.md before flipping public
If those words mean nothing to you, run CozyVTT only on your local network (e.g. via a Tailscale node, ZeroTier, or just LAN-accessible) until you've leveled up your sysadmin chops. That's a perfectly valid way to play — your campaigns aren't worse for being LAN-only, and you skip the whole attack-surface conversation.
If you find a security issue, please report it privately per SECURITY.md rather than filing a public issue.
Features
Platform
- Multi-campaign — one server, unlimited campaigns, isolated from each other
- Role-based access control — Platform Admin, Campaign DM, Player, and Spectator roles; the DM seat can be handed to another member without losing ownership of the campaign
- Setup wizard — guided first-run initialization; no manual database seeding required
- Admin dashboard — user management, system settings, activity logs, database backups
Campaign Tools
- Interactive map canvas — upload map images, place tokens, drag to move in real time
- Token system — player, NPC, and object token types with disposition (friendly/neutral/hostile), HP bars, conditions, stat blocks, and notes; three display modes (pog, top-down, full-art); colored-letter placeholders for tokens without images; the DM can hide a token from players or obscure what it is, and players are sent only what they may see of each token
- Creature library — browse, search, and place creatures from the SRD bestiary (auto-imported from Open5e) or custom campaign-specific templates; per-campaign favorites with star toggle; duplicate SRD creatures to customize; edit custom creatures in-place; save token images back to templates
- Token templates — save reusable token configurations (image, stats, HP, size, disposition, full NPC stat block); place from library or save from map context menu; copy templates between campaigns the DM owns
- NPC right-click rolls — DMs right-click any NPC token to roll abilities, saves, skills, attacks, and damage parsed from its stat block; advantage/disadvantage selector for d20 systems; free-form custom roll fallback for non-5e systems
- Campaign export/import — export campaigns as portable
.cozyvttarchives; import on any CozyVTT instance; includes maps, tokens, creatures, templates, and assets; multi-step preview flow; optional audio toggle; secured against path traversal, zip bombs, and malicious files - Walls & dynamic lighting — DM-drawn wall segments (walls, doors, windows) with raycasting visibility; draw, polygon, and brush drawing modes; snap-to-grid and snap-to-endpoint; split, select, erase, and merge point tools; multi-select with Shift, a drag-box or Ctrl+A, then move or delete the lot together; snap-to-wall door/window placement (auto-splits existing walls); players only see what their character can: bright light clearly, dim light and the token's darkvision at half, nothing beyond, and light never reveals through a wall — a lit room is visible only to someone with line of sight into it; per-map Global Illumination lights everything in line of sight for tables that prefer it; explored areas are remembered per player and shown in grey until seen again; door interactions for both DM and players; bright/dim light radii matching D&D 5e and PF2e rules; named light presets (Candle, Torch, Lamp, Lantern, Campfire); overlapping dim zones combine to bright
- Universal VTT import/export — bring in a
.uvtt,.dd2vttor.df2vttmap from Dungeondraft, Dungeon Alchemist, Czepeku and others, with its walls, doors and lights already placed, and export any map back out. One file is one map, as the format intends; if a file's walls reach outside its own picture, CozyVTT says so before importing rather than leaving you to wonder - Fog of war — drag a box to reveal or hide chunks of the map; the selection snaps to whole grid squares, with animated fade transitions
- Spirit layer — a second canvas layer for ethereal / astral / out-of-body scenes, hidden from players by default
- Initiative tracker — real-time combat turn order that follows each token; the DM runs it and players roll their own initiative. Players see every combatant the DM has not hidden, on the map the table is looking at and on their own plane, even one they cannot yet find in the dark or under fog, and a creature's hit points only when the DM turns its HP bar on. The acting token is ringed on the map for everyone, and hovering a name highlights its token (and vice versa)
- Map pings — press Tab to mark a spot for the whole table; a dot with radiating rings in your own colour, labelled with your name
- Vibe tracker — time-of-day periods (dawn, day, dusk and night by default, all editable) that tint the map with a colour filter
- Ambient atmosphere — six visual effects (rain, mist, leaves, sparkles, snow, wind) and ambient audio independently per campaign
- Session management — start, pause, resume, and end sessions with full state capture (token positions, map, vibe)
- Resizable session workspace — drag to resize or collapse the roster, map, and side panels; a tabbed sidebar keeps Chat, Dice, Initiative, Notes and Session full-height, with an unread-message badge; layout persists per browser
Character Sheets
- D&D 5th Edition — full sheet with stats, skills, saving throws, attacks, spells, inventory
- Pathfinder 2nd Edition — attributes, saves, perception, skills, lore skills, strikes
- Call of Cthulhu 7th Edition — characteristics, skills, weapons, sanity, luck
- Flexible / Custom — JSON-based freeform character sheets for any system
Dice System
- Full dice notation —
1d20+5,2d6,4d6kh3(keep highest), advantage/disadvantage - Real-time results — rolls appear in the Dice panel for the whole table
- Secret rolls — hidden from the other players; your DM can still see them, and they stay in your own list marked as secret
- Dice history — a running log of the session's rolls that survives a refresh, filtered per person by the server
- Saved rolls — name a dice expression and it becomes a one-click button in the dice panel; private to you and scoped to one campaign, with the expression checked when you save it so a saved roll always works
Communication
- Campaign chat — in-session messaging between all members
- System messages — automatic notices when a session starts, pauses, resumes or ends, when the DM changes the time of day, and when the spirit layer is revealed or hidden
- Personal notes — private per-campaign notes in Markdown, with a rendered preview and autosave; readable only by their author, enforced server-side
- Documents — upload a PDF, plain text or Markdown rulebook or handout, or write one in the app, and read it without leaving CozyVTT; a DM shares documents with a campaign, and every member reads them from inside the session; text and Markdown documents can be edited by their uploader
- Session history — every finished session with its date, length and the recap the DM wrote; the DM can edit or clear any past recap
Theming & Customization
- 16 built-in color themes — warm, cool, dark, neutral, and vibrant palettes
- Per-user themes — each account picks its own theme and font from the profile page; preferences persist across logout/login
- Custom theme — pick primary, accent, background, and text colors to create a unique look
- 8 font families — open-source Google Fonts including medieval, elegant, modern, handwritten, and scholarly options
- Live preview — theme and font changes preview instantly before saving
- Default theme — admin selects the theme shown on the login page and applied to brand-new users
- Custom branding — the instance logo, mascot, and favicon can be replaced (swap the images in
frontend/public/before building; an admin upload UI is planned)
Security
- Argon2id password hashing (64 MB memory cost, timeCost 3)
- TOTP multi-factor authentication with hashed backup codes
- Session-based authentication with rolling expiry and "remember me"
- Password reset via email (SMTP configurable)
- Admin-approval registration (optional)
- File upload validation — magic-byte content checks (not just MIME header), size limits by type; text uploads must genuinely be text, and documents are served as plain text or PDF, never as a web page
- Per-endpoint rate limiting — global API limit, strict auth limit, asset upload limit (configurable via
ASSET_UPLOAD_RATE_LIMIT), and per-account limits on everything sent to the live table, set far above what a game sends - Security headers on the app page — a Content-Security-Policy that allows script only from your own instance, plus
X-Frame-Options,nosniff,Referrer-PolicyandPermissions-Policy. Injected script cannot run, the app cannot be framed by another site, and the browser will not send data to an address CozyVTT does not use - WebSocket campaign isolation — server-authenticated campaign membership; no client-spoofing
- The backend runs as an unprivileged user — the container starts as root only long enough to fix ownership on your mounted folders, then drops to
appuserbefore the app itself runs - Production refuses to start with a placeholder
SESSION_SECRETorDATABASE_PASSWORD - See SECURITY.md for the vulnerability disclosure policy
Game Systems
| System | Character Sheet | Status |
|---|---|---|
| D&D 5th Edition | Full | Available |
| Pathfinder 2nd Edition | Full | Available |
| Call of Cthulhu 7th Edition | Full | Available |
| Flexible / Custom | JSON freeform | Available |
| Shadowrun 6th Edition | — | Planned |
Tech Stack
Backend
- Node.js + Express + TypeScript (strict mode)
- PostgreSQL 15 + Prisma ORM
- Socket.io for real-time WebSocket events
- Argon2id password hashing
- Multer + Sharp for file uploads and thumbnail generation
- Nodemailer for transactional email
- Zod for runtime request validation
- Jest for testing
Frontend
- React 18 + TypeScript (strict mode)
- Vite build tooling
- Tailwind CSS with custom design tokens
- Zustand for live session state; TanStack Query (React Query) for REST data
- Socket.io client
- Axios for REST API calls
- react-resizable-panels for the session workspace
- Framer Motion for animations (reduced-motion aware)
- Vitest + Testing Library
Quick Start
Requirements
- Node.js 20+
- PostgreSQL 14+
- A modern browser (Chrome, Firefox, Edge, Safari)
- SMTP server (optional — required for password reset and invitation emails)
Docker (Recommended)
git clone https://github.com/CheekyChinchilla/CozyVTT.git
cd CozyVTT
# Copy it, then set at least DATABASE_PASSWORD and SESSION_SECRET (see docs/DEPLOYMENT.md)
cp .env.example .env
# Production stack (hardened, what end-users run)
docker compose up -d --build
Navigate to http://localhost and complete the setup wizard.
Local Development
If you've cloned the repo to hack on CozyVTT itself, use the dev compose file instead — it enables hot reload, exposes the backend port directly, and runs NODE_ENV=development:
docker compose -f docker-compose.dev.yml up
See docs/DEVELOPMENT.md for the full dev setup (including running outside Docker).
See docs/DEPLOYMENT.md for production deployment with Nginx and SSL.
Configuration
Most runtime configuration is managed through the Admin dashboard after setup. Email (SMTP) and upload limits are set in .env; the dashboard shows them and can send a test email:
| Tab | Setting | Description |
|---|---|---|
| Users | Invite User | Email someone a link to join and choose their own password (needs SMTP; link valid 7 days) |
| Users | Create User | Generate a temporary password to hand over yourself — for instances without email |
| Settings | Instance Name | Displayed in the browser title and emails |
| Settings | Allow Registration | Whether new users can self-register |
| Settings | Require Admin Approval | New registrations must be approved before login |
| Settings | Timezone | Server timezone for session timestamps |
| Settings | SMTP | Shows the email settings from .env, with Send Test Email |
| Settings | Upload Limits | Shows the per-type file size limits from .env (maps, tokens, audio, avatars, documents) |
| Appearance | Default Theme | Theme shown on the login page and used for new users (each user can override from their profile) |
| Appearance | Default Font | Default font family applied alongside the default theme |
| Appearance | Custom Theme | Build a palette from primary, accent, background, and text colors, with a live readability check |
However an account is added, its first sign-in ends with the person choosing their own password — an admin-issued temporary password cannot be used for anything else.
Instance branding (logo, mascot, favicon) is not set from the dashboard yet — see Appearance & Theming for how to change it today.
Default Upload Limits
| Type | Default | Environment variable |
|---|---|---|
| Map images | 50 MB | MAX_MAP_SIZE_MB |
| Token images | 5 MB | MAX_TOKEN_SIZE_MB |
| Audio files | 20 MB | MAX_AUDIO_SIZE_MB |
| Avatar images | 2 MB | MAX_AVATAR_SIZE_MB |
| Documents (PDF, text, Markdown) | 50 MB | MAX_DOCUMENT_SIZE_MB |
Set these in .env and apply them with docker compose up -d; no rebuild is needed, but docker compose restart keeps the old values. If you raise one, raise your reverse proxy's body limit to match (NGINX_MAX_BODY_SIZE for the bundled Nginx). See Upload Size Limits.
User Roles
Platform Roles
| Role | Description |
|---|---|
ADMIN |
Full platform access — manage users, campaigns, system settings |
USER |
Can create and join campaigns |
Campaign Roles
| Role | Description |
|---|---|
DM |
Full campaign control — manage maps, tokens, sessions, members |
PLAYER |
Can view maps, move their own tokens, roll dice, chat |
SPECTATOR |
Watches the map, chats and pings; keeps private notes and dice macros; cannot roll dice or initiative, move or edit tokens, or edit a character |
Documentation
| Document | Description |
|---|---|
| User Guide | End-user guide for all platform features |
| DM Guide | DM-specific features and workflows |
| Player Guide | Quick-start guide for players |
| Development Setup | Local development guide |
| Architecture | System architecture and design decisions |
| Deployment | Production deployment guide |
| Contributing | How to contribute to CozyVTT |
| Game Systems | How to add new game system support |
| API Reference | REST API and WebSocket event reference |
| WebSocket Events | Detailed Socket.io event schema |
| Future Features | Backlog of planned work |
| Security Policy | How to report vulnerabilities |
| Changelog | Release history |
| Third-Party Licenses | Font and content license attributions |
Community Projects
Built by other people, on their own terms. They are not part of CozyVTT, are not maintained by this project, and are listed because they are useful — check their own documentation and licence before running them.
| Project | What it does |
|---|---|
| cozyvtt-mcp | An MCP bridge that lets an AI assistant act in a campaign — post to chat, roll dice, move tokens, change maps and run initiative. MIT licensed, independent of this codebase. |
A note on building things like this. CozyVTT's HTTP and WebSocket surfaces are not a public API: they are what the web client calls, they are not versioned, and they carry no compatibility promise, so they can change in any release. They can be driven by a program — signing in with your own email and password returns a session cookie that authenticates both, and a script can do exactly what the web client does, as that user with that user's permissions. Unsupported is the honest word for it, not impossible.
That is why cozyvtt-mcp pins to a CozyVTT version and keeps a compatibility table. Check that table before pairing the two: the surface it builds on is unversioned and can change in any release.
Asset Storage
Uploaded files are stored in backend/uploads/ organized by type:
uploads/
maps/ Map images and thumbnails
tokens/ Token images and thumbnails
audio/ Ambient audio files
avatars/ User profile avatars
documents/ PDF, text and Markdown documents
Backups made from the Admin Panel are not kept here: they go in backend/backups/.
License
GNU Affero General Public License v3.0 — see LICENSE for details.
CozyVTT is free to use, self-host, and fork. The AGPLv3 requires that any modified version you run as a network service must also make its source code available under the same license.
For third-party font and content licenses, see THIRD_PARTY_LICENSES.md.