Co-authored-by: multica-agent <github@multica.ai>
Dungeons & Ground
An English-first private web alpha for asynchronous text TTRPG campaigns. Players create private worlds with an AI coauthor, submit actions into shared rounds, and play alongside persistent AI companions. The rules engine—not the language model—owns dice and mechanical state.
Local start
cp .env.example .env
pnpm install
pnpm dev:all
Open http://localhost:3000. pnpm dev:all starts the Nuxt site and the AI worker in one terminal. To run them separately, use pnpm dev in the first terminal and pnpm dev:worker in the second.
To verify the complete hosted-Supabase party path (two temporary email accounts, invite, AI character draft, persistent AI companions, shared readiness, atomic AI resolution, memory state, usage telemetry, and the next round), keep dev:all running and execute pnpm e2e:full-party in another terminal. pnpm smoke:multiplayer is retained as an alias for the same scenario.
Before starting either process, the root commands check the Supabase schema. If it is missing and SUPABASE_DB_URL is configured, they transactionally create all D&G tables, functions, triggers, indexes, and policies from supabase/bootstrap.sql. Copy the Session pooler URI from the Supabase Dashboard Connect panel into the root .env and replace the password placeholder with the URL-encoded database password. The database URL is server-only and must never use a NUXT_PUBLIC_ prefix.
Existing campaigns are upgraded automatically to ruleset version 2 by pnpm db:ensure. The migration preserves every round, narration, current HP, inventory, and status; adds the SRD 5.2.1 character rules profile; and records one migrate_ruleset audit entry per campaign. Migrated characters retain compatibility with their previous proficiency hints, while newly created characters use explicit server-owned skill, expertise, and saving-throw proficiencies.
The web and worker commands both load this root .env file explicitly. Environment variables supplied by Railway or CI take precedence over values in the file.
The UI can be built without credentials. A running Nitro server and every worker process require SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, NUXT_PUBLIC_SUPABASE_URL, and NUXT_PUBLIC_SUPABASE_ANON_KEY; startup fails with a clear error when required server credentials are missing. Redis is optional: when REDIS_URL is absent, the worker safely claims jobs from the Supabase ai_jobs outbox using short database leases and retries failed jobs up to three times.
Campaign context is deliberately bounded: the worker sends the current scene, the last two resolved rounds, active characters and goals, relevant world entities and relationships, the latest three-round summary, and up to eight memories selected by PostgreSQL full-text/entity/tag relevance. It never sends the full campaign transcript automatically. AI requests are protected by per-minute, daily-user, daily-campaign, and daily-token quotas; successful provider calls record tokens, estimated cost, and latency for the dashboard.
Both legacy JWT service-role keys and current sb_secret_… Supabase keys are supported. Keep either form server-only; never expose it through a NUXT_PUBLIC_ variable.
Select OpenRouter or the official DeepSeek API with AI_PROVIDER=openrouter|deepseek and provide the matching API key. OpenRouter retains JSON Schema structured output; DeepSeek uses its official JSON mode, followed by the same Zod validation.
Use Node.js 18.20.5 or newer. The lockfile pins the web toolchain to the Node 18-compatible Nuxt 3.15, Nitro 2.10, and Vite 6 line; CI verifies the project on Node 18.20.8.
Workspace
apps/web— Nuxt SSR UI and Nitro APIapps/worker— BullMQ worker for AI world and round jobspackages/shared— shared Zod contracts and domain typespackages/game-engine— deterministic d20 rules and state transition guardssupabase/migrations— PostgreSQL schema, indexes, triggers, and RLS policies
Alpha safety
- Worlds are private by default.
- Content is limited to 13+; explicit sexual content and sexual content involving minors are rejected.
- AI responses are parsed against strict schemas.
- AI can propose checks and events, but cannot directly set dice outcomes, proficiency, temporary HP, death saves, resources, or other mechanical values.
- SRD-derived work must retain the attribution in
LEGAL.md.
Production services
- Copy the Supabase Session pooler connection URI from Connect into
SUPABASE_DB_URL.pnpm dev:allnow detects an empty project and creates the complete schema automatically.pnpm db:ensureruns the same guarded bootstrap without starting the app. Manual SQL Editor installation remains available throughsupabase/bootstrap.sql. - In Supabase Auth settings, enable the Email provider and disable Allow anonymous sign-ins. In Auth URL Configuration, set the Site URL to
http://localhost:3000; addhttp://localhost:3000/auth/callbackandhttp://localhost:3000/auth/reset-passwordas Redirect URLs. Configure SMTP before external testing so confirmation and recovery email is delivered reliably. CAPTCHA and Auth rate limits remain recommended. - Run
pnpm dev:workeralongside the web process. Provision Redis only when BullMQ delivery is desired; otherwise the worker polls the Supabase outbox. - Configure either
OPENROUTER_API_KEY(default modeldeepseek/deepseek-v4-flash) or setAI_PROVIDER=deepseekwithDEEPSEEK_API_KEY(default modeldeepseek-v4-flash, endpointhttps://api.deepseek.com/chat/completions).
If the worker reports POST /rest/v1/rpc/claim_ai_job 404 and /rest/v1/profiles also returns 404, the base schema was never installed. Run the generated supabase/bootstrap.sql in the SQL Editor and restart pnpm dev:all. Do not run only 0002: it depends on the tables created by 0001.
Weeks 3–4 flow
- Click Enter the Alpha to open registration. Every account requires a display name, email, password, and 13+ confirmation. Existing users sign in with email/password; forgotten passwords use the email recovery flow.
- Create a world in the dynamic coauthor chat. Unfinished conversations and generated drafts appear on the dashboard and resume after a reload.
- Review every starting-world field, including the owner-only hidden threat, then confirm it.
- During world creation, the coauthor also creates three editable, universe-specific AI companions. A new campaign starts immediately with those companions and an owner-controlled hero. Additional players create a human character manually or ask the coauthor for an editable draft; owners can add more AI companions the same way.
- The owner creates an expiring private invite link. Signed-in email users join through
/join/:token. - Players save actions and mark them ready. The final ready action queues the round automatically; the owner can also continue without waiting.
- The worker resolves server-owned rolls and state, publishes narration, and opens the next round. Visible campaign tabs synchronize every two seconds and immediately when the tab regains focus.
Weeks 7–8 stability flow
- Every resolved round appends immutable game events and may atomically project scene, quest, relationship, character, and tagged-memory state.
- A compact story summary is generated and committed with every third round. If the provider or transaction fails, no partial narration or state is saved; the leased job retries with the same round idempotency key.
- The campaign screen exposes the current summary, important memories, and active goals. The dashboard shows 30-day tokens, cost, latency, and the current daily request allowance.
- Campaign owners may correct HP, inventory, or statuses through the audited character-state API; the reason and before/after values are retained in
audit_entries. pnpm testincludes twenty long-memory regression scenarios.pnpm e2e:full-partyverifies the hosted two-player/AI-companion flow end to end.