Aetheria GM is an AI-backed role-playing game (RPG) engine for solo and shared-table play. By defining any genre or setting, the AI Game Master (GM) procedurally designs a coherent 2-4 hour questline divided into three structured acts. As you play, the GM logs progress, tracks character stats/level/inventory, and presents AI-generated scene art when configured, with procedural SVG fallbacks and deterministic maps.
The engine stores all events in a persistent SQLite database, which double-functions as a Model Context Protocol (MCP) server endpoint. This allows external LLMs or future game sessions to connect directly to the database to retrieve memories, character status, and adventure logs!
- Infinite Genres: Play anything from Gothic Eldritch Horror to Cyberpunk Detective Noir or High Fantasy.
- Coherent Game Outline: At startup, the AI designs an Act-by-Act blueprint and sticks to it as a guideline, preventing the story from running off-track or concluding prematurely.
- Adaptive Visual Theme: The campaign's Setup agent generates the full visual theme — color palette and a font pairing chosen for the genre's atmosphere — applied the moment the campaign loads.
- Scene Visualizer, Heroics & Tactical Map: The visualizer slot shows AI-generated heroic renders of the current focal subject when an image provider is configured (see
/admin→ Scene Images; without one, the procedural SVG path is used). Locations are persistent structured state: the GM keeps a layout and occupancy record per place, shown as a deterministic top-down map in the Situation panel whenever position matters — alongside, never instead of, the written scene grounding. - Interactive Controls: Suggests quick action choices or accepts free-form text input for full player agency.
- Reusable Player Characters: Characters are stored as persistent profiles with stats, inventory, AI-managed abilities, progression notes, and checkout status. Available characters can be reused in new campaigns, active characters can be copied into a new branch, and campaign cards can release a character profile while preserving the campaign snapshot.
- Shared Tables (multiplayer): A campaign seats multiple characters, each bound to its own revocable seat token. A seat can act only as — and see only — its own character (partymates are silhouettes; the GM's outline, NPC dossiers, and memories stay server-side), and the server enforces round-robin turns — committed actions wait for your turn, while questions and table talk are always open to everyone.
- Council GM Pipeline: Player turns run through ordered interaction, continuity, referee, continuity archive, and final narration context calls. The player still sees one GM voice, and only the final checked response can update canonical state.
- Voice Narration: Optional AI-generated text-to-speech narration plays only for the final player-facing GM response, never for internal council context calls.
- Model Context Protocol (MCP): Hosts a Server-Sent Events (SSE) MCP server exposing tools (
list_campaigns,list_characters,get_campaign_outline,get_campaign_history,get_character_state,search_memories) for semantic query search of past sessions. - Production Hardened Security & Concurrency:
- DOMPurify Sanitization: All HTML narrative dialogues and SVG frames are scrubbed using locally bundled DOMPurify and Marked assets, avoiding CDN script trust at runtime.
- Content Security Policy: Served pages include a restrictive CSP and browser hardening headers.
- Transaction Isolation: Multi-table writes execute within queued SQLite immediate write transactions (
BEGIN IMMEDIATE) to prevent transaction interleaving without serializing unrelated full LLM requests. - Enforced Uniqueness: Features unique constraints on
turns(campaign_id, turn_number)and query indexing for performance. - WAL (Write-Ahead Logging): Configured for improved concurrent read/write throughput.
- Optional Authentication: Supports a gateway authorization token (
ACCESS_SECRET) to prevent unauthorized game turns or deletion events.
- Google Gemini
- OpenAI GPT
- Anthropic Claude
- Claude Code (the server process's logged-in Claude.ai subscription; no API key)
- xAI Grok
- Ollama (local models)
- Custom OpenAI-Compatible (LM Studio, OpenRouter, Groq, etc.)
Provider model catalogs change. Configure an available model explicitly in /admin instead of
treating documentation examples as durable defaults.
Aetheria GM runs on any machine (Windows, macOS, Linux) with Node.js installed.
The themed UI uses CSS color-mix(), so the supported browser floors are
Chrome/Edge 111, Firefox 113, and Safari/WebKit 16.2; see the
MDN browser-compatibility table.
The Linux desktop shell renders through the machine's installed WebKitGTK rather
than a separately declared WebKitGTK floor; verify the shell on the target machine.
Clone or copy the directory and run:
npm installThe browser harness uses Chromium through Playwright. Install that browser once on each development machine:
npx playwright install chromiumAI configuration belongs to the server operator — players never supply keys or model choices. Configure it either way:
- Admin panel: open
/admin(not linked from the game UI). Add each provider connection once, refresh its live model catalog where available, create reusable model entries (with an optional per-model custom key), then assign a primary and fallback to each Council role. Catalog suggestions are editable and manual entry remains available. Voice and image credentials stay in their separate sections. Settings persist in the server database and take precedence over environment variables. Gate the panel withADMIN_SECRETin.env; if unset,/adminis open for single-operator localhost use (production refuses to serve it without the secret). - Environment: create a
.envfrom the template (cp .env.example .env) and fill in API keys (e.g.GEMINI_API_KEY,OPENAI_API_KEY,ANTHROPIC_API_KEY, orXAI_API_KEY).
Claude Code subscription provider: install Claude Code and log the server process in with
claude auth login, then set AI_PROVIDER=claude-code. Set AI_MODEL to any model alias or full
identifier available to that login, or leave it blank/use default to let Claude Code choose its
configured default. If claude is not on the server's PATH, set CLAUDE_CODE_PATH to its absolute
path. CLAUDE_CODE_TIMEOUT_MS may override the 240-second request limit from 1,000 through 900,000
milliseconds. This transport deliberately ignores API-key/cloud-provider authentication and runs
Claude Code without repository tools, settings, MCP servers, browser access, or session persistence.
In /admin, the Claude Code provider row reports safe install/login/plan status without making a
generation call. It has no API-key control and accepts a manually entered model id because Claude
Code does not expose a documented machine-readable account model catalog.
Fallback tier: transient provider errors (overload, rate limit, timeout) retry once, then fail
over per-call to the fallback assigned to that Council role. A blank stored fallback retains the
environment fallback tier (FALLBACK_AI_PROVIDER / FALLBACK_AI_MODEL / FALLBACK_API_KEY).
To lock the server endpoints from unauthorized third-party users, add a secret token in your .env file:
ACCESS_SECRET=your_secret_access_passwordIf this is configured, click AI Settings in the top right of the game interface and paste this token into the Server Access Token field to authorize play.
The in-game settings panel holds player preferences only (access token, voice narration choice, diagnostics); AI provider, models, and keys are configured at /admin or via environment variables.
If the server is behind a trusted reverse proxy, configure Express proxy handling:
TRUST_PROXY=1Use the number of trusted proxy hops appropriate for your deployment.
npm startOpen your browser to: http://localhost:3000
One person runs the server (the host); every other player gets a seat token — a private credential bound server-side to exactly one character. A seat can act only as its own character and sees only its own sheet: partymates appear as name/class/level/HP silhouettes, and the GM's private record (campaign outline, NPC dossiers, memories) never leaves the server.
- Host: set
ACCESS_SECRET(your own credential) andADMIN_SECRETin.env, start the server, and load the campaign. Use the + Join button in the party strip to create each player's character, then click the key icon next to that character to mint their seat token. The token is shown once — send it to that player only. Minting again replaces the old token (the recovery move if one leaks); a seat can also be revoked outright:DELETE /api/campaigns/:id/characters/:cid/seat. - Players: open the URL, paste your seat token into Settings → Server Access Token, and save. Your character loads automatically — there is no campaign list and nothing to pick.
- Turns: committed actions go round-robin — the input tells you whose turn it is. Questions and table talk ("what do I see?", in-character banter) are always open to everyone and never advance the world.
- Leaving the table: the host releases a character from their campaign card, which frees the underlying profile; the campaign's history keeps them.
Meta-actions (creating/deleting/forking campaigns, export/import, table
style dials, minting seats) are host-only and rejected for seats
server-side. The AI configuration, keys, and image/voice providers all
belong to the host (/admin); players never enter any of that.
Hosting beyond your LAN: the app serves plain HTTP — put a TLS-terminating layer in front of it (reverse proxy, tunnel, or VPN) before sending seat tokens across the internet, and always set both secrets.
A standalone Tauri window for quicker local testing — same server, same UI, same
database; the browser path stays canonical. One-time setup (needs the Rust
toolchain and webkit2gtk-4.1):
npm install --prefix desktopThen launch with:
npm run desktopThe shell reuses a server already running on port 3000, or starts node server.js
itself and shuts it down again on exit (a reused server is left running). Set
AETHERIA_SERVER_DIR if the shell binary is run from outside the repo.
To deploy Aetheria GM securely on a Linux server:
# Build and run in background (runs under rootless node user)
docker-compose up -dThe server will be available on port 3000 with the SQLite database persisted inside the rpg-data docker volume.
To query a local Ollama instance from the game client, you must enable CORS (Cross-Origin Resource Sharing) permissions in Ollama.
- Linux / macOS:
Run Ollama in your terminal with the
OLLAMA_ORIGINSenvironment variable:OLLAMA_ORIGINS="*" ollama serve - Windows:
- Quit Ollama from the Taskbar Tray.
- Open PowerShell and run:
[System.Environment]::SetEnvironmentVariable('OLLAMA_ORIGINS', '*', 'User')
- Relaunch Ollama from the Start Menu.
Select Ollama in the game's AI Settings panel, verify your Ollama URL (default: http://localhost:11434), and enter the exact model name (e.g. llama3).
Player turns use the Council GM Pipeline. It presents as one GM to the player while the server runs ordered context calls behind the scenes:
- Interaction interprets the player's exact input and proposes intent.
- Continuity approves, denies, or revises that proposal against established facts, pacing, NPC memory, and campaign archive.
- Referee adjudicates whether the proposal is approved, denied, or needs clarification, and defines allowed state changes.
- Continuity final check verifies the ruling and prepares archive notes.
- Interaction narration relays the final result in in-world terms as a single GM response.
The engine has five first-class AI roles, each independently configurable in /admin (or via SETUP_*, INTERACTION_*, CONTINUITY_*, REFEREE_*, NARRATION_* env variables): Setup designs the campaign outline and opening scene (once per campaign — use your strongest model), Interaction classifies player input every turn (fast/cheap wins), Continuity grounds everything against the campaign record, Referee adjudicates actions and dice, and Narration writes the final player-facing prose (your best stylist). A blank primary uses the role environment/default chain; configured entries are reusable across roles and may share a provider key. Council turns make 2 model calls for table talk and 5 for committed actions.
Voice narration can be enabled or previewed in the player settings panel. Voice identity and delivery belong to the campaign, not to each listener: the server resolves the GM narrator and NPC voices, and identical host/seat playback shares one synthesis result instead of multiplying provider calls.
The operator chooses OpenAI or xAI Grok and stores that provider's voice key at /admin.
Environment alternatives are TTS_PROVIDER=openai with OPENAI_API_KEY and optional TTS_MODEL, or
TTS_PROVIDER=grok with XAI_API_KEY. Grok batches adjacent lines from the same speaker while
preserving each line's delivery tone; OpenAI uses one line per request. The app labels playback as
AI-generated voice narration for user disclosure.
Aetheria GM acts as an MCP server using Server-Sent Events (SSE). External LLM clients (like Claude Desktop or Cursor) can connect to the server to look up campaign history.
Add the following configuration to your claude_desktop_config.json (located at %APPDATA%\Claude\claude_desktop_config.json on Windows or ~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"aetheria-gm": {
"command": "node",
"args": ["/absolute/path/to/ai-rpg-engine/server.js"],
"env": {
"NODE_ENV": "production",
"ACCESS_SECRET": "your_secret_access_password"
}
}
}
}(Alternatively, you can connect using SSE transport by providing the URL: http://localhost:3000/api/mcp/sse?token=your_secret_access_password if the client supports SSE connections).
list_campaigns: Returns IDs, titles, and genres of all games.list_characters: Returns reusable character profiles, checkout status, stats, inventory, abilities, and progression notes.get_campaign_outline: Returns the full story blueprint and NPC index.get_campaign_history: Retreives the full dialogue log of what happened.get_character_state: Returns campaign character stats (HP/Mana/XP), items, abilities, and progression notes.search_memories: Runs search queries on campaign memories.