Every session is a story worth keeping. This guide covers every feature of the Versyr chronicle platform — from submitting a note during a session to running the full GM loop.
Versyr is a chronicle platform for tabletop RPG campaigns. Every campaign gets a public blog, a GM backstage CMS, a collaborative session room, AI narrative tools, a gallery for generated scene art, and a lootbox that structures treasure from voice or text. The AI tools only see what your group has lived through — no source-book spoilers.
The platform is multi-tenant: each campaign lives on its own subdomain (e.g. hells-vengeance.versyr.io) with its own data, members, and published chronicles. The Hell's Vengeance Pathfinder campaign is the first customer deployment.
Every login is mapped to a role. Your role determines which navigation items appear, what you can write, and whether AI-triggering buttons are visible.
| Role | What you can do | Typical user |
|---|---|---|
| Admin | Everything. Manage users, API keys, rules, campaign config, all GM powers. | Platform operator |
| Spielleiter (master / GM) | Create/close sessions, write chronicles, run analysis, manage knowledge, assign loot. Cannot manage users or API keys. | The GM |
| Spieler (player) | Read blog, submit session notes, view loot, view gallery, edit own profile, file bugs. | Everyone else at the table |
rolle = admin | master | player (legacy). The label GM / Spielleiter maps to master. If you ever touch the user data directly, know that master and gm mean the same thing.The blog lives at {campaign}.versyr.io (e.g. hells-vengeance.versyr.io). It is public — anyone can read published chronicles without logging in.
Every published chronicle follows the same structure:
## Abschnittstitel Markdown-like sectionsThe blog header offers four views: Chronicles (default), Characters, NPCs, Locations. Use the search box top-right to filter chronicles by keyword; clicking a character pill in the sidebar filters the feed to sessions featuring that character.
Once signed in, the four old header buttons (⇄ switch view, campaign badge, ☾ theme, 🐛 bug) collapse into a single avatar in the top-right. Tap it to open a dropdown with: your name + email + active campaign at the top; Zur CMS / Zum Blog as a smart switch; Mein Profil; theme toggle; Bug melden; Abmelden; admin extras for admin accounts. Same dropdown on every page; sidebar redundancies are gone.
Every view encodes itself in location.hash: #blog, #post/{id}, #cms/{page}, #gallery, #login. Refreshing the page restores exactly where you were. Browser-back walks one in-app step at a time. CMS hash links opened without a session redirect to the blog so you don't strand yourself behind a sign-in wall.
On tablets the site header is retired entirely. The hamburger top-left opens a vertical icon+label sidebar pinned to the viewport (scrollable independently of main content); the avatar floats top-right at the same offset as the bottom-right Oracle. Tap a tile to navigate; tap the hamburger again to close.
The chat bubble in the bottom-right of the blog opens the Oracle — an AI chatbot with access to your campaign's characters, NPCs, locations, factions, and the last five published chronicles. It does not have access to source books; it won't tell you the stats of a Ring of Protection, but it will tell you what happened the last time your party met the Magister of Kantaria.
An admin invites you by email from Benutzerverwaltung. You'll receive a Versyr-branded email with a one-tap magic link from mail@versyr.io (subject Einladung zur Versyr-Plattform). Click the link — it signs you in directly, then a small modal asks you to set a password so you can sign in with email + password from then on.
If the email landed in spam, mark it as "Not spam" and add mail@versyr.io to your contacts; deliverability improves with send volume. Need a fresh link? The admin can press the ↻ resend button next to your row in the user table.
Tap the avatar / login icon in the top-right of the blog. Enter email + password. After sign-in the CMS sidebar appears with the nav items relevant to your role.
Open Mein Profil from the avatar dropdown. You can change your password, select your theme (dark / light / high contrast), and set your character name — this is the label that appears on your session notes.
When the GM starts a session, it becomes the "active session" for everyone. Open 🎲 Sitzungsraum — you'll see a green pulsing dot and the session title. Below it:
Notes appear in real-time for all participants. The contributor bar at the top shows how many notes each character has submitted.
Click the 🎤 button above the input to speak your note instead of typing. Works in Chrome and Edge only (Web Speech API). Click again to stop; the transcript lands in the textarea for you to review before submitting.
Hover any note you submitted — a small ✕ appears top-right. Click to remove. You can only delete your own notes; only the GM can delete anyone's.
The 🐛 icon in the header opens a bug-report modal. Describe what went wrong; GM/admin see it in their Bug-Reports panel.
The Session Room orchestrates the full lifecycle of one play session: live notes, GM-controlled lifecycle phases, AI analysis, gap-filling Q&A, and chronicle drafting.
| Phase | What happens | Who can act |
|---|---|---|
open | Live. Players submit notes in real time. Pulsing green dot. | All players + GM |
closed | GM clicked "Sitzung schließen". Notes locked. AI analysis ready to run. | GM only (analysis) |
reviewing | AI analysis in progress (~15–40s). Cancel button available. | GM only |
closed + gaps | Analysis produced a draft + a list of gaps/contradictions. GM answers each gap, then finalises. | GM only |
published | Chronicle saved as a blog post. Session appears in the history list. | GM only (publish) |
#16), optional title, optional locationPlayers submit notes. The GM's view shows a running list, filterable by character. The GM can delete any note (not just their own) if a player posted in error or duplicated content.
When play is done, GM clicks ⛔ Sitzung schließen. A "Narrative Analyse" panel appears. Click 🤖 Analyse starten & Chronik entwerfen ↗ to trigger Claude:
closed.
reviewing after a browser reload or logout. Force-flips status back to closed.
closed session with no draft, reverts to open so more notes can be added. Disabled once a draft exists.
Each gap appears as a card with a question and a textarea. Answer them one by one. When all are answered, ✨ Finale Chronik generieren ↗ runs a second AI pass that weaves your answers into the draft and produces the final publishable chronicle. You can re-analyse at any time with ↺ Neu analysieren.
The finalised draft drops into the chronicle editor (✍ Neue Chronik). The session-number field is pre-filled with the next number based on the highest existing chronicle (you can override). Review, tweak, tick "Veröffentlicht", click Speichern ↗. The chronicle appears on the public blog immediately — and Versyr runs the AI lexicon sync on the text (see §09).
From Alle Beiträge, the GM can drag-reorder rows or use the inline ↑ / ↓ controls. The session number is what drives blog ordering, so this is the place to fix a mis-numbered or out-of-sequence chronicle without re-editing the post.
In the session history list under the main session panel, each row has a small ✕ button (GM only). Click, confirm, the session and all its notes are removed. Published chronicles are not deleted — those live in the chronicle table independently.
The Versyr Lootbox (💰 Lootbox in the sidebar) eliminates the chaos of tracking party treasure. Speak or type what you found, and the AI structures it into items with official names, types, values in GP, weights, rarity tags, and Nethys search links.
At the top of the page, four stats summarise the vault: total GP, unassigned group loot, number of items, and already-distributed value. These always reflect the full vault and aren't affected by the filters below.
| View | When to use |
|---|---|
| 📇 Karten (default) | Browsing, assigning to characters, visual inventory |
| 📋 Tabelle | Scanning many items, sorting mentally by column, bulk review |
Toggle in the top-right of the control row. Your choice persists across reloads (stored in localStorage).
Filters combine. The bottom line of each card/row respects all active filters.
Card view: the chip row at the bottom of each card lets you click a character name to assign, click 🗃 Gruppe to unassign, or click 💸 Verkauft to mark as sold. Assigning is instant and synced in real time to all participants.
Each item has a 📖 Nethys badge (card view) or 📖 link (table view). Clicking opens a Google search scoped to aonprd.com with the item's English name. This always returns usable results, unlike direct deep-links which the AI used to hallucinate.
Click ⇩ CSV to download the currently-filtered vault as lootbox-{campaign}-{date}.csv. File is UTF-8 with BOM so Excel handles umlauts cleanly. Columns: Anzahl, Name, NameEn, Typ, Seltenheit, Magisch, GP/Stk, Gesamt-GP, Gewicht/Stk, Besitzer, Verkauft, Beschreibung.
The 🖼 Galerie stores AI-generated scene images organised by session, tags, and characters.
The mood selector now spans a wider range than the original dark-fantasy mandate — heroic, contemplative, festive, comedic, mundane, etc. Choose the closest fit; Claude tunes the optimised prompt accordingly. Admins can flip the Pro-Modus toggle to route the request through gpt-image-2 for higher fidelity (slower, costs more); off by default.
From any generated or saved image, click Anpassen to enter a mask-based editor. Paint the area you want changed with the brush; type the change in the prompt; submit. Each adjustment stacks linearly — use the ↶ Rückgängig and ↷ Wiederholen buttons to step through. Quality is preserved across iterations (low / medium / high stays as you set it on the first generation).
Every image saved to the gallery gets the italic V stamped in the bottom-right corner at ~10% of the shorter side. A faint parchment halo sits behind it so the ember mark reads against dark fantasy scenes. The watermark is baked in at upload time — downloads, shares, and social embeds all carry the mark.
In the chronicle editor (✍ Neue Chronik), next to the Header-Bild URL field there's a ◈ Galerie button. Clicking opens a thumbnail picker with images sorted so the active session's images appear first, then by recency. Click any thumbnail to set it as the chronicle's header.
Click any gallery thumbnail to open the lightbox. From there: copy the direct URL, download the file, or use it in the chronicle editor.
Four sidebar entries manage the living world:
After every chronicle save, Versyr runs a background AI sync: Claude reads the body text and identifies:
If anything matches, a Lexikon-Vorschläge aus Chronik modal pops up. Each suggestion is pre-checked — tick/untick to curate, then click Ausgewählte übernehmen ↗. Approved entries are written to the directories; character arc notes are appended to their Errungenschaften. Nothing is written without your approval.
◈ Chronik-Studio is a simpler flow for when you want AI drafting without a live session. Paste rough notes, click Chronik schreiben, get a narrative draft. Lands in the editor for publishing.
⚖ Kontinuität scans any text against the current campaign state and flags warnings, errors, or confirmations. Useful before publishing a chronicle to catch contradictions you might have missed.
Admins (not GMs) manage users under 👥 Benutzerverwaltung. Bug reports from players appear at 🐛 Bug-Reports — admin-only — with a counter badge in the sidebar when new ones arrive.
All AI text generation runs through Anthropic's Claude. Every call is logged to the Activity Log with a module label so you can see exactly what fired and when.
| Module label | Where it fires | Purpose |
|---|---|---|
Chronik-Studio | ◈ Chronik-Studio → Chronik schreiben | Draft a narrative chronicle from rough notes |
Bild-Prompt | ◇ Bildgenerator → Generieren | Optimise the user prompt before image generation |
Kontinuitätsprüfung | ⚖ Kontinuität → Prüfen | Flag contradictions against prior sessions |
Beuteextraktion | Lootbox → 🤖 KI strukturieren | Parse loot text into structured items |
Sitzungsanalyse | Session Room → Analyse starten | Draft chronicle + detect gaps from session notes |
Chronik-Finalisierung | Session Room → Finale Chronik generieren | Weave gap answers into the draft |
Charakter-Beschreibung | Character edit modal → Aus Beschreibung generieren | Produce visual description + image anchor for a character |
Lexikon-Sync | Automatic after every chronicle save | Detect new NPCs, places, and main-character arc changes |
Oracle | Blog chat widget | Answer visitor questions against campaign state |
Every call logs three events: ⚡ Anfrage gesendet, ✓ Antwort erhalten, or ⚠ …Fehler. If something feels stuck, check the Activity Log first.
Max output tokens: 4000. Model: claude-sonnet-4-6. Temperature: default (model-set). The prompt for each module is tuned for its output format (JSON vs prose); if responses get truncated or malformed, the JSON-parse error surfaces as an alert with a raw-response dump in the browser console.
Secrets are stored in Firebase under /config and loaded at login — never hardcoded in the client. Admins set them under ⚙ Einstellungen:
anthropicKey — powers all AI text featuresopenaiKey — reserved for experiments; not currently used in production pathscloudinaryConfig — cloud name + upload preset for the gallerygoogleKey — optional (image generation variant)githubToken — used to mirror bug reports as GitHub issues (optional)Open 👥 Benutzerverwaltung. Create profiles with email, name, character, and role. Saving fires the inviteUser Cloud Function which sends a Versyr-branded magic-link email via Resend (sender mail@versyr.io). The user clicks the link, sets a password on first sign-in, and is then a fully active member.
The status column shows ✓ Aktiv for users who've completed first sign-in, ⌛ Eingeladen DD.MM.YY for pending invites, and — for legacy profiles created before the email flow. The ↻ button next to a pending row resends the invite (and quietly upserts the studio membership entry as a side effect). Magic-link sign-in must be enabled in Firebase Auth → Sign-in method, and the Resend API key must be present at config/resendApiKey.
The inviteUser and listOrphans callables run on Cloud Run via Firebase Functions Gen 2. On a fresh project they need an explicit roles/run.invoker grant for allUsers (otherwise calls return a bare "Fehler: internal"). Set with gcloud run services add-iam-policy-binding. config/resendApiKey must be populated under each Firebase project's /config.
Rules live in database.rules.json at the repo root — version-controlled. Deploy with:
firebase deploy --only database
Current rules: root catch-all is .read: false / .write: false; auth-only paths (studios, campaigns inside studios, gallery, characters, bugs, etc.) have explicit overrides. Public blog reads are served via the studio path with platform/studiosIndex/.read: true letting unauth visitors resolve slug→studio. Don't replace the root catch-all without auditing every path the app reads — it will break things silently.
Pushes to develop deploy to versyr-dev.netlify.app; pushes to main deploy to versyr.netlify.app and the campaign subdomain. Never push files directly to Netlify bypassing git — that caused a 503KB corruption incident in the past.
platform/index.html is the whole frontend — HTML, CSS, and JS inline. This is intentional for simple deployment. A CI workflow (.github/workflows/validate.yml) checks on every PR that there's exactly one <!DOCTYPE html>, one <body>, and that the main script block parses as valid JS. A refactor into modules is backlog (#63) but not scheduled.
#post/{id}, #cms/posts, etc.). Refresh restores the same view, browser-back walks one step at a time.mail@versyr.io; first-time password setup modal; ✓ Aktiv / ⌛ Eingeladen / ↻ resend in the user tablegpt-image-2; brush-mask Anpassen with linear undo + redo; quality respected across adjustments; per-image diagnostics strip (model · quality · WxH · file size)scripts/audio.js as a no-build proof-of-patternmax_tokens raised to 4000 to fix silently-truncated JSON responsesstudios/{sid}/campaigns/{cid}/… (studio prefix added by Slice 5 of M2; legacy campaigns/ root decommissioned in Slice 7b 2026-05-03)#C8923A, chronicle brand bar, site co-brand header)versyr.gg to versyr.io; apex redirects to campaign subdomainMilestones from the project tracker, in chronological order. Dates are targets, not commitments.
/campaigns/{cid} mirror on prod + staging (with backup), removed the legacy DB rule. App reads/writes go solely through the studio path now./c/{slug}. Member management page per campaign.pf1 / pf2 / dnd5e / custom. System registry drives AI prompts (loot parsing, continuity, oracle), item type vocabulary, currency rules, and external reference linkouts (Nethys, D&D Beyond, admin-pluggable).platform/index.html has crossed the warn cap; the inline script is being chipped out one cluster at a time, no build step (plain <script defer src>). Audio cluster shipped first as proof-of-pattern; Gallery and others follow when their boundaries justify the work.