Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lazy Lands

Campaign Companion for Dungeon Masters

Your campaign, without the amnesia. Persistent, reviewable memory for NPCs, factions and consequences.

Lazy Lands Landing Page

Problem

Campaign preparation becomes harder as context accumulates. A DM must track NPCs, factions, consequences, open arcs, and forgotten details while preparing a coherent next session. There is no good tool that connects what happened to what comes next.


Solution

Lazy Lands helps a DM:

  1. Capture campaign context from free text.
  2. Record what happened after each session.
  3. Review and validate AI-suggested memories.
  4. Generate a coherent next-session proposal using only accepted memories.

The AI is the Scribe. It proposes, never decides. The DM always has the last word.


Main MVP Flow

Full flow: PRODUCT.md section 4.

  1. The DM signs up or logs in.
  2. Creates a campaign from free text.
  3. Reviews AI-extracted NPCs, factions and world state before saving.
  4. Records what happened after a played session.
  5. Reviews and accepts/edits/dismisses AI-suggested memories.
  6. Asks Lazy Lands to prepare the next session.
  7. Reviews and copies or exports the generated session proposal.

Tech Stack

Layer Technology
Frontend Next.js 16 (App Router), React, TypeScript, TailwindCSS
UI shadcn/ui, Lucide, React Hook Form, Zod
Backend FastAPI, Python 3.12, Pydantic, pydantic-settings
Package mgr uv (backend), pnpm + Turborepo (monorepo)
Database Supabase (PostgreSQL + Auth + Row Level Security)
AI LLM Provider abstraction (Gemini, Groq, Mistral or Cerebras; fake in dev)
Testing Vitest + React Testing Library, Playwright, pytest
Quality ESLint, Prettier, Ruff, mypy, Husky, lint-staged
Deployment Vercel (frontend), Railway (backend)

Architecture Overview

Backend follows a Modular Monolith with nested Clean/Hexagonal layers per module (see ADR-05). Feature modules live under services/api/app/modules/ and encapsulate their own domain/, application/, infrastructure/, routes, schemas, and prompts. Transversal concerns live in the shared/ kernel.

Full architecture reference: docs/04-architecture.md.

Public Demo

Try the complete campaign flow without creating an account at lazy-lands.com/demo. The guided tour runs entirely in the browser with seeded, in-memory data: it does not require credentials and does not affect production campaigns.

Product Snapshot

Generated-session draft in Lazy Lands

Security Headers

The web application and API send CSP, anti-framing, nosniff, referrer, and permissions-policy headers. The API generates a server-side X-Request-ID for safe correlation. HSTS is intentionally configured at the TLS ingress or hosting platform, not by the application: enabling it before HTTPS is enforced can lock users out of an HTTP development or misconfigured deployment.

CI audits Node dependencies from pnpm-lock.yaml and the installed Python environment with pip-audit==2.10.0. Adding pip-audit to the API dependency group would add 304 transitive lockfile lines, exceeding the bounded security work unit; uvx --from keeps the audit tool version explicit while uv sync --locked remains the reproducible application dependency install.


Repository Structure

lazy-lands/
|-- apps/web/                # Next.js frontend (App Router)
|   |-- app/                # Routes and layouts
|   |-- components/         # Shared UI components
|   |-- lib/                # Utilities (Supabase, etc.)
|   |-- tests/              # Vitest unit tests + Playwright E2E
|-- services/api/           # FastAPI backend
|   |-- app/main.py
|   |-- app/shared/         # Config, security, errors, logging, dependencies, shared adapters
|   |-- app/shared/llm/     # LLM provider port and fake implementation
|   |-- app/modules/        # Health + feature modules (campaigns, sessions, memory, generation)
|   |-- tests/              # pytest test suite
|-- supabase/               # Migrations, config, seed
|-- docs/                   # SDD technical documentation
|-- .github/workflows/      # CI pipeline
|-- PRODUCT.md              # Product source of truth
|-- DESIGN.md               # Design system and tokens
|-- AGENTS.md               # AI agent instructions

Local Setup

Prerequisites

  • Node.js 22+ and pnpm 11+
  • Python 3.12 and uv
  • Docker Desktop + WSL2 (required for supabase start)

1. Clone and install

git clone https://github.com/danilopgon/lazy-lands.git
cd lazy-lands
pnpm install

2. Configure environment

cp .env.example .env
# Fill in the values from your Supabase project dashboard

For frontend-only local development, also copy the frontend values into apps/web/.env.local.

3. Start Supabase (requires Docker)

pnpm supabase start
# Outputs local URLs and keys -- copy them into .env

Note: Supabase requires Docker Desktop with WSL2 on Windows. Alternative: connect to a remote Supabase project.

4. Start the frontend

pnpm --filter web dev
# http://localhost:3000

5. Start the backend

cd services/api
uv run uvicorn app.main:app --reload
# http://localhost:8000

6. Start everything (Turborepo)

pnpm dev

Environment Variables

Copy .env.example to .env and fill in your values.

Variable Description Default
NEXT_PUBLIC_SUPABASE_URL Supabase project URL for the web app
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY Supabase publishable key for the web app
NEXT_PUBLIC_APP_URL Public frontend URL for auth redirects http://localhost:3000
NEXT_PUBLIC_API_URL Backend API URL http://localhost:8000
APP_ENV Backend environment development
API_CORS_ORIGINS Allowed origins; accepts a comma-separated list or JSON array http://localhost:3000
SUPABASE_URL Supabase URL for the backend
SUPABASE_PUBLISHABLE_KEY Supabase publishable key for the backend
SUPABASE_SERVICE_ROLE_KEY Supabase service role key for backend data access
LLM_PROVIDER fake, gemini, groq, mistral, or cerebras fake
GEMINI_API_KEY Required when Gemini is the primary or fallback provider
GROQ_API_KEY Required when Groq is the primary or fallback provider
MISTRAL_API_KEY Required when Mistral is the primary or fallback provider
CEREBRAS_API_KEY Required when Cerebras is the primary or fallback provider
LLM_FALLBACKS Optional comma- or space-separated fallback providers
AI_GENERATION_RATE_LIMIT Maximum AI-generation requests in the configured window 5
AI_GENERATION_RATE_WINDOW_SECONDS Duration of the AI-generation rate-limit window, in seconds 60

Supabase Local Setup

# Start local Supabase stack (requires Docker Desktop + WSL2)
pnpm supabase start

# Copy output into .env:
# API URL           -> NEXT_PUBLIC_SUPABASE_URL and SUPABASE_URL
# publishable key   -> NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY and SUPABASE_PUBLISHABLE_KEY
# service_role      -> SUPABASE_SERVICE_ROLE_KEY

Docker Desktop must be running before pnpm supabase start.

If startup fails with failed to read signing keys, generate them with:

pnpm setup:keys

This creates supabase/signing_keys.json (gitignored) with the correct format.

See supabase/README.md for full setup instructions.

Local Auth smoke testing

  1. Start Supabase with Docker Desktop running: pnpm supabase start.

  2. Copy the local URL and publishable key into apps/web/.env.local:

    NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
    NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=<local publishable key>
    NEXT_PUBLIC_APP_URL=http://localhost:3000
  3. Restart the frontend dev server after changing env vars.

  4. Request a password reset from the local web app.

  5. Open Mailpit at http://127.0.0.1:54324 to inspect auth emails.

The Supabase API gateway at http://127.0.0.1:54321 is not the inbox. If you open the wrong URL or path and see {"message":"no Route matched with those values"}, use the Mailpit URL above.


How to Run the Frontend

pnpm --filter web dev      # development server
pnpm --filter web build    # production build
pnpm --filter web start    # serve production build

How to Run the Backend

cd services/api
uv sync
uv run uvicorn app.main:app --reload         # dev server (port 8000)
uv run uvicorn app.main:app --host 0.0.0.0  # production

Health check: GET http://localhost:8000/health

See services/api/README.md for backend docs.


How to Run with Docker Compose

Note: Not verified locally -- WSL2 + Docker Desktop required.

cp .env.example .env
pnpm supabase start     # Supabase separately
docker compose up       # web (3000) + api (8000)

Available Scripts

Root (Turborepo)

Script Command Description
dev pnpm dev Start all dev servers
build pnpm build Build all packages
lint pnpm lint Lint all packages
typecheck pnpm typecheck TypeScript check all packages
test pnpm test Run all unit tests
format pnpm format Format all files with Prettier
format:check pnpm format:check Check formatting

Frontend (apps/web)

Script Command Description
dev pnpm --filter web dev Start dev server
build pnpm --filter web build Build for production
lint pnpm --filter web lint ESLint
typecheck pnpm --filter web typecheck TypeScript check
test pnpm --filter web test Vitest unit tests
test:e2e pnpm --filter web test:e2e Playwright E2E

Backend (services/api)

Script Command Description
Run uv run uvicorn app.main:app FastAPI dev server
Test uv run pytest pytest suite
Lint uv run ruff check app/ tests/ Ruff linter
Format uv run ruff format --check app/ Ruff formatter
Type check uv run mypy app/ mypy (non-blocking)

Testing

# Frontend unit tests (Vitest)
pnpm --filter web test

# Frontend E2E (Playwright -- requires dev server)
pnpm --filter web test:e2e

# Backend tests (pytest)
cd services/api && uv run pytest

Quality Checks

pnpm format:check
pnpm lint
pnpm typecheck
cd services/api && uv run ruff check app/ tests/
cd services/api && uv run mypy app/

Pre-commit hooks (Husky + lint-staged) run Prettier on staged files automatically.


Deployment Notes

Frontend -- Vercel

  1. Connect the GitHub repository to Vercel.
  2. Set apps/web as root directory.
  3. Add all required NEXT_PUBLIC_* environment variables.
  4. Set NEXT_PUBLIC_API_URL to the production backend URL.

Deployment URL: lazy-lands.com

Backend -- Railway

  1. Build with services/api/Dockerfile.
  2. Set backend env vars (APP_ENV=production, Supabase keys, LLM keys).
  3. Set API_CORS_ORIGINS to the Vercel frontend URL.
  4. Health check: GET /health.

Note: Dockerfiles are structurally correct but not verified locally (WSL2 + Docker required).


AI Usage

  • The backend abstracts the LLM behind an LlmProvider port.
  • In dev/test: FakeLlmProvider returns deterministic JSON without API calls.
  • In production: a configured Gemini, Groq, Mistral, or Cerebras provider calls its model.
  • All LLM outputs are validated with Pydantic before storage or return.
  • Prompts are versioned inside their owning feature module when implemented.

Project URLs

Resource URL
Production application lazy-lands.com
Public demo lazy-lands.com/demo
Slides (spanish) Presentation PDF
Video Demo

Documentation

Document Purpose
PRODUCT.md Product principles, flow, entity model
DESIGN.md Design system, tokens, components, motion
docs/README.md Documentation index and reading guide
AGENTS.md Instructions for AI coding agents

Post-MVP Roadmap

The MVP includes PDF export; its generated-session export deliberately excludes private notes. The following opportunities remain deferred:

  • Richer session logging, including private notes and additional entity-change fields.
  • Timeline and advanced filtering for larger campaigns.
  • A conversational Scribe that still proposes rather than changing canon automatically.
  • RAG, embeddings, and vector search across campaign history.
  • Advanced relationship graphs and memory compilation.
  • Dark theme and theme settings.

Billing, shared campaigns, a mobile app, and Obsidian sync are not currently planned. See docs/11-backlog.md for rationale and implementation constraints.

About

Your campaign, without the amnesia. Persistent, reviewable memory for NPCs, factions and consequences.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages