A dark, modern campaign archive for D&D groups. One Express app serves the frontend and API. Campaigns, party members, stories and NPCs are stored in local JSON with automatic backups.
For a complete production, LAN, HTTPS, backup and password-management walkthrough, see the self-hosting guide.
- Admin-only landing page with multiple isolated campaigns.
- Story Details and NPCs live beside each other in separate columns instead of tabs.
- Story entries can be categorized as events, quests, clues, locations, lore or notes; quests and clues support open/resolved state.
- NPCs carry a living status, last known location and affiliation so the cast remains useful between sessions.
- Tags, pinning and focused filters make long-running campaigns quick to scan and search.
- Campaign-wide search covers stories, quests, locations, NPCs and party members from one keyboard-friendly field.
- Search results are grouped by record type, highlight the match and support
/to focus or Escape to clear. - Record types share a consistent color language across cards, metadata, directories, navigation and linked-record chips: story events are silver-white, quests gold, locations green, clues violet, lore blue, notes rose and NPCs purple.
- Players and the DM can optionally give any individual Story Detail or NPC a visual flourish when creating or editing it. Records use the Classic Chronicle card by default, with Scribbled parchment, Magical note and Druidic note available as opt-in skins.
- Every viewer can collapse individual Story Details and NPCs to a compact type-and-title row. The choice is remembered in that browser without changing campaign data or anyone else’s view.
- Dedicated Quest Board and Location Directory views reuse the shared chronicle instead of duplicating entries.
- Story details, quests, clues, locations and NPCs can be explicitly connected; every connection appears as a navigable backlink on both records.
- Connected campaign tabs update live when entries, party members, tokens, bookmarks or HP change.
- Present characters show current/max HP bars in the party rail. Public D&D Beyond core sheets are checked every 10 seconds and changes are broadcast to connected tabs; companion current HP remains player/DM controlled.
- Players can keep a private bookmark list for stories, NPCs and party members.
- NPC names mentioned in Story Details become inline links to their NPC entry.
- Plain web addresses and Markdown links such as
[Ancient map](https://example.com)are clickable in entry text. - Large character avatars with smaller pets, minions, familiars and companions beneath their owner.
- Players can create, edit and remove their own pet, minion, familiar and summon tokens, upload token art, link public D&D Beyond sheets for hover previews, and maintain current or temporary HP locally. The DM retains the same controls through Party setup.
- Companion tokens also accept public D&D Beyond monster links (with or without
https://) and import token art, HP, AC, initiative, speed, CR, ability scores, senses, skills, languages and notable actions. - Public D&D Beyond sheet sync covers HP, ability scores and modifiers, AC, initiative, proficiency bonus, speed, saving throws, skills, passive senses, armor/weapon/tool proficiencies and languages. Attacks, spells and inventory stay out of the campaign view.
- Character details use separate Overview, Skills and Proficiencies tabs, while forms use collapsible sections and guarded unsaved changes.
- Hovering or keyboard-focusing a party character opens a live quick sheet with HP, AC, initiative, proficiency, speed, abilities and trained skills; pets and monsters use the same richer preview treatment.
- Mobile campaign navigation stays within thumb reach, keyboard focus is visible throughout, archived characters are clearly marked for the DM and live/sheet-sync status reflects the real connection state.
- A public-domain fantasy landscape from the National Gallery of Art appears as a prominent framed library hero and campaign masthead, supported by illuminated-card borders, crests, ornamentation and atmospheric depth throughout the interface.
- Manual character creation or public D&D Beyond sheet import.
- Admin editing and deletion for campaigns, Story Details and NPCs, plus complete party management.
- Every campaign selection opens the identity picker, even when a DM or player session already exists; shareable campaign pages support present-character selection, DM sign-in and signed player sessions.
- Players post as their chosen character and may edit that character's own profile, but no other party member.
- Local JSON storage, image uploads and a timestamped backup before every change.
- Automatic migration from the earlier single-campaign JSON structure.
Requires Node.js 20 or newer.
cd C:\Users\Jimmy\Desktop\SharedChronicle
Copy-Item .env.example .env
notepad .env
npm.cmd install
npm.cmd startOpen http://127.0.0.1:3000. Set at least ADMIN_PASSWORD and AUTH_SECRET in .env. Without an .env, development uses the temporary password dragonfire; production refuses to start without an admin password.
Run tests with:
npm.cmd testOpen a campaign, choose Party setup, paste the public character URL and select Import public sheet. The campaign keeps the non-action reference stats synced and shows them in a readable character popup, with a link to the full sheet.
Choose Manual creation to enter and maintain the character name, avatar, level, classes, species and external link yourself. This is the fallback for private or campaign-only D&D Beyond sheets.
D&D Beyond does not provide an official third-party OAuth or character API. Private sheets are therefore never accessed with stored passwords or account cookies.
Every campaign has a shareable /campaign/<slug> page. It opens an identity picker containing the campaign's present player characters and a small I am the DM option. After choosing a character, the player can:
- add and edit Story Details and NPCs with an audit trail under that character's name;
- edit that character's public profile from the party sidebar;
- browse all campaign content normally.
Mark actionable Story Details as Quest or Clue and resolve them when the party closes the thread. Use Location for places the party may revisit. Pin the handful of details and NPCs that matter for the next game; pinned items stay at the top for everyone who can see them.
Story search includes tags and entry type. NPC search also covers status, location, affiliation and tags. The adjacent filters provide one-click views for open threads, locations, pinned items and common NPC states.
Press / while viewing a campaign to jump to campaign-wide search. Quest and Location views are derived from Story Detail types, so editing an entry updates every view immediately. Player bookmarks are stored against the selected player character and do not appear in other player sessions.
The admin can copy, replace or revoke the link. Replacing or revoking it also invalidates existing player sessions created through the previous link. Admins retain full editing rights throughout the campaign.
Runtime data remains outside the source folders:
storage/
|-- data/
| |-- index.json
| |-- campaigns/
| | `-- <campaign-id>/
| | |-- campaign.json
| | |-- uploads/
| | `-- backups/
| |-- archived-campaigns/
| `-- migration-backups/
`-- uploads/ # legacy uploads retained for compatibility
storage/ is ignored by Git. Each campaign now owns its JSON, uploads and rolling backups. Deleting a campaign moves its folder into archived-campaigns/ instead of erasing it. On first startup after upgrading from combined storage, the app copies the original JSON into migration-backups/, leaves the original file untouched, creates the folder layout, and copies referenced legacy uploads into their campaign folders.
The bundled seed is first-run data only and is never copied over existing runtime data. For self-hosted production installs, always point DATA_DIR outside the application checkout. That makes source updates and even a full replacement of /opt/shared-chronicle independent from campaign data.
The landing-page Admin Control Panel lets the host choose the backup cadence and number retained per campaign, create or download snapshots, and restore an individual campaign through a typed confirmation. The oldest files roll off automatically after the configured limit. A restore and every other existing-campaign write first preserve the current campaign as a safety backup.
For a VM, keep runtime files outside the checkout:
NODE_ENV=production
HOST=127.0.0.1
PORT=3000
ADMIN_PASSWORD=use-a-long-unique-password
AUTH_SECRET=use-a-different-long-random-value
COOKIE_SECURE=true
DATA_DIR=/var/lib/shared-chronicle/data
UPLOAD_DIR=/var/lib/shared-chronicle/uploads
BACKUP_LIMIT=50
BACKUP_CADENCE_HOURS=24Install Node.js 20+, clone the app to /opt/shared-chronicle, run npm ci --omit=dev, and create /var/lib/shared-chronicle/data plus /var/lib/shared-chronicle/uploads for the service account. The repository includes:
deploy/shared-chronicle.servicefor systemd;deploy/Caddyfile.examplefor automatic HTTPS;deploy/nginx.conf.examplefor Nginx.
After an update:
cd /opt/shared-chronicle
sudo -u shared-chronicle git pull --ff-only
sudo -u shared-chronicle npm ci --omit=dev
sudo systemctl restart shared-chronicle
journalctl -u shared-chronicle -n 100 --no-pagerKeep /var/lib/shared-chronicle in the server backup routine as well as the automatic per-campaign JSON backups.
Do not place campaign data inside a versioned release directory or unpack a release archive over /var/lib/shared-chronicle. Back up /var/lib/shared-chronicle before upgrading; the application makes additional timestamped JSON backups whenever stored data changes.