Loratone is an immersive reading platform that combines ebooks, narration, and soundscapes into a single reader experience.
- Next.js (App Router) + React + TypeScript
- Prisma + PostgreSQL (Supabase-compatible)
- Better Auth
- Supabase Storage for PDFs/audio/media
- Replicate for narration generation
- Vitest + Testing Library for tests
- Node.js 20+
- npm 10+
- Docker Desktop (for the local Postgres container) or a PostgreSQL database/Supabase project
- Supabase storage bucket(s)
- Replicate API token (for narration features)
./bin/bootstrap.shbootstrap.sh is idempotent. It installs npm dependencies, provisions .env.local and .env from STORIA_ENV_FILE, an existing main-worktree env file, or .env.example, then runs prisma generate when available. Use npm install directly only when you intentionally want to bypass the repo setup script.
Create .env.local in the project root (or run ./bin/bootstrap.sh to create/link .env.local and .env automatically):
# Database (required by Prisma + auth)
DATABASE_URL=postgresql://...
DIRECT_URL=postgresql://...
# App URL
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Supabase (client + server)
NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key>
SUPABASE_URL=https://<project>.supabase.co
SUPABASE_SERVICE_ROLE_KEY=<service-role-key>
SUPABASE_STORAGE_BUCKET=storia-storage
# Optional dedicated soundscape paths
SUPABASE_SOUNDSCAPE_BUCKET=storia-storage
SUPABASE_SOUNDSCAPE_BASE_PATH=audio/curated
NEXT_PUBLIC_SUPABASE_SOUNDSCAPE_BUCKET=storia-storage
NEXT_PUBLIC_SUPABASE_SOUNDSCAPE_BASE_PATH=audio/curated
# Narration / AI
REPLICATE_API_TOKEN=<replicate-token>
# Optional fallback key name used in some routes
REPLICATE_KEY=<replicate-token>
# Auth / email
RESEND_API_KEY=<resend-key>
RESEND_FROM_EMAIL=Loratone <onboarding@resend.dev>
GOOGLE_CLIENT_ID=<google-client-id>
GOOGLE_CLIENT_SECRET=<google-client-secret>
# Optional analytics
NEXT_PUBLIC_UMAMI_WEBSITE_ID=<website-id>
NEXT_PUBLIC_UMAMI_URL=https://cloud.umami.is/script.jsFor local development, npm run dev now auto-starts a Dockerized Postgres instance on localhost:5433, recreates the storia_dev database if it was deleted, and applies Prisma migrations.
npm run devUseful database commands:
npm run db:up # start local Postgres only
npm run db:down # stop the local Postgres stack
npm run db:logs # tail Postgres logs
npm run db:seed # seed local dataIf you prefer managing the database manually instead of Docker:
npx prisma generate
npx prisma migrate dev
npm run db:seednpm run devOpen http://localhost:3000.
# One-shot setup for humans and agent worktrees
./bin/bootstrap.sh
# Pre-handoff verification gate (tsc + lint + vitest)
./bin/verify.sh
# Playwright E2E tests (mandatory for symphony PRs)
./bin/e2e.sh
# Development
npm run dev
# Production build
npm run build
npm run start
# Linting
npm run lint
# Testing
npm run test
npm run test:watch
# Linear/Symphony runner (requires LINEAR_API_KEY)
./bin/pi-symphony.sh --once
# Database seed
npm run db:seed# Prerequisites (start these before running e2e.sh)
docker compose up -d # starts Postgres + Mailpit
npm run dev & # starts Next.js on :3000
# Run E2E tests (exits non-zero if any spec fails)
./bin/e2e.sh
# Run headed / with UI
npx playwright test --ui
# Run a specific spec
npx playwright test e2e/admin-login.spec.ts --headedServices required by e2e.sh:
- Postgres at
localhost:5433(storia_dev DB) - Mailpit at
localhost:8025(web UI) /localhost:1025(SMTP) - Next.js dev server at
localhost:3000
SMTP fallback: Auth OTPs route via Mailpit when RESEND_API_KEY is empty. Set RESEND_API_KEY="" in .env.local to use Mailpit locally instead of Resend.
Test artifacts: test-results/ (videos on retry), playwright-report/ (HTML report). Both are gitignored.
./bin/pi-symphony.sh polls Linear for Todo tickets, creates isolated git worktrees, runs the full validation pipeline, opens a PR, and moves the ticket to In Review.
./bin/bootstrap.sh → npm ci + prisma generate + playwright install
./bin/verify.sh → tsc + lint + vitest (blocker if fail → no PR)
./bin/e2e.sh → playwright specs (blocker if fail → no PR)
REQUIRE_VIDEO=1 → recordings/*.webm must exist (blocker if missing → no PR)
PR opened
Every symphony ticket must produce at least one .webm video that exercises the changed flow. Files go in recordings/<TICKET-ID>-<flow>.webm.
npm run dev &
# wait for server...
mkdir -p recordings
playwright-cli open http://localhost:3000
playwright-cli tracing-start
playwright-cli video-start
# drive the changed flow end-to-end
playwright-cli video-stop --filename=recordings/<TICKET-ID>-<flow>.webm
playwright-cli tracing-stop
playwright-cli closeSet REQUIRE_VIDEO=0 in the runner env only for tickets with genuinely no reachable UI — document the justification in the Linear workpad comment. Without that, a missing recording blocks the PR.
Do not commit
recordings/unless asked — it is for symphony artifacts and PR review only. It is gitignored.
AGENT_RUNNER=pi PI_CHAIN=plan-build-review ./bin/pi-symphony.sh --onceSupported runners: pi, claude, opencode. Fallback runners are tried in sequence if the primary fails:
AGENT_RUNNER=pi AGENT_FALLBACKS=claude,opencode ./bin/pi-symphony.shFor full configuration options, see WORKFLOW.md.
Useful commands:
pi chain plan-build-review "Plan/build/review: <ticket>"
pi chain plan-build "Plan/build only: <ticket>"
pi chain scout-flow "Explore: <area>"For Claude Code fallback, install and authenticate the claude CLI. For OpenCode fallback, install and authenticate the opencode CLI. Fallback prompts instruct the alternate runner to follow AGENTS.md and WORKFLOW.md in the current worktree.
For long-running narration/audio jobs, run the worker separately:
API_BASE_URL=http://localhost:3000 \
WORKER_SECRET_TOKEN=dev-secret \
node worker/audio-worker.jsDocker-based local setup is available in worker/docker-compose.yml.
src/app- App Router pages and API routessrc/components- Shared UI componentssrc/lib- Server/client utilities (auth, Prisma, Supabase, storage)prisma- Prisma schema, migrations, and seed scriptsworker- Background audio preprocessing workerdocs- Product, architecture, and implementation docsbin- Bootstrap, verification, and Linear/Symphony runner scripts.pi- Minimal tracked Pi Coding Agent harness (runtime data ignored)
docs/IMPLEMENTATION_QUICKSTART.mddocs/AUDIO_PREPROCESSING_ARCHITECTURE.mddocs/TEXT_OVERLAY_SETUP.mddocs/front-end-spec.mddocs/prd.md
- This repository currently contains legacy docs that reference previous architecture choices; prefer the docs linked above for the current Next.js stack.
- Keep secrets in local/hosted environment variables and never commit them to git.
.env.exampleis a template only; local.env*files remain ignored.