It remembers recurring lore, escalates into a Y2K reality collapse, launches an eight-second pigeon paperwork arcade, and ends with a five-question contradiction check.
Technology
- Next.js 16 App Router, React 19, and TypeScript
- CSS custom properties and locally bundled variable fonts
- SEA-LION via the OpenAI-compatible chat completions API
- Zod structured-response and request validation
- Drizzle ORM with PostgreSQL
- Railway deployment configuration and health checks
- Vitest, Playwright, and Axe accessibility checks
Run locally
Requirements:
- Node.js 22 or newer
- pnpm 11 or newer
- PostgreSQL only when testing persistent storage locally
pnpm install
cp .env.example .env.local
pnpm dev
Open http://localhost:3000.
Do not commit .env or .env.local; both are ignored by Git. SEA-LION credentials and prompts remain server-side.
Configuration
| Variable | Required | Purpose |
|---|---|---|
SEALION_API_KEY |
For live AI | SEA-LION API credential. |
SEALION_BASE_URL |
No | Defaults to https://api.sea-lion.ai/v1. |
SEALION_MODEL |
No | Defaults to aisingapore/Gemma-SEA-LION-v4-27B-IT. |
SEALION_INITIAL_TIMEOUT_MS |
No | Initial-generation deadline; defaults to 25000. |
SEALION_TRANSFORM_TIMEOUT_MS |
No | Transformation deadline; defaults to 15000. |
DATABASE_URL |
Production | Railway/private PostgreSQL connection URL. |
DATABASE_PUBLIC_URL |
Local only | Public Railway PostgreSQL URL when developing outside Railway. |
DATABASE_REQUIRED |
No | Set to 1 to reject startup/runtime storage access without PostgreSQL. |
Minimal live-generation configuration:
SEALION_API_KEY=your_key
SEALION_BASE_URL=https://api.sea-lion.ai/v1
SEALION_MODEL=aisingapore/Gemma-SEA-LION-v4-27B-IT
Without SEALION_API_KEY, the entire demo remains usable through clearly labeled deterministic fixtures. Local development without a database uses an in-memory store; production refuses temporary memory storage.
Railway deployment
- Create an app service from this repository and add a Railway PostgreSQL service to the same project.
- In the app service, create a reference variable named
DATABASE_URLpointing to the database service’sDATABASE_URL. - Add
SEALION_API_KEYand any optional SEA-LION overrides to the app service. - Deploy.
The included railway.json performs:
pnpm buildduring the build phase;pnpm db:migratebefore deployment;pnpm starton Railway’s injectedPORT;- a
/api/healthreadiness check before the deployment is marked healthy.
For local access to Railway PostgreSQL, enable the database service’s public TCP endpoint and set DATABASE_PUBLIC_URL. Services running inside the same Railway project should use the private DATABASE_URL.
Run migrations manually when needed:
pnpm db:migrate
Architecture
The application stores narrative state rather than treating raw model chat history as its database.
device_sessionsstores only a SHA-256 hash of the anonymous browser token.casesstores ownership, status, version, timestamps, and the authoritativeExcuseStateJSONB document.case_eventsstores append-only actions, idempotency keys, versions, source, latency, token usage, and sanitized error categories.- Every mutation supplies an
expectedVersionandidempotencyKeyso stale tabs cannot silently overwrite newer state. - Older JSONB cases are normalized during reads and persist their updated shape on the next ordinary mutation.
Provider output is parsed into Zod schemas and merged into deterministic state only after validation. The server preserves canonical lore and arcade state even when the live model response omits them.
API routes
| Method | Route | Purpose |
|---|---|---|
POST |
/api/cases |
Validate a scenario, create a case, and generate the initial excuse. |
GET |
/api/cases |
List recent cases for the current browser. |
DELETE |
/api/cases |
Clear the current browser’s case history. |
GET |
/api/cases/:id |
Restore an owned case and its authoritative state. |
POST |
/api/cases/:id/actions |
Apply a versioned transformation, interrogation answer, or arcade result. |
GET |
/api/health |
Check application and database readiness. |
Safety and privacy
- Cases are linked to this browser. No account required.
- Serious fabricated emergencies, crimes, fraud, impersonation, fake evidence, and official documents are rejected.
- Harmless high-chaos comedy remains allowed even when SEA-LION labels it as implausible or suspicious.
- Genuine provider refusals and unsafe generated material still switch to the labeled fallback.
- Raw prompts and generated text are not copied into application logs.
- Logs contain identifiers, lengths/latency, token usage, source, and sanitized error categories.
- Safety text, navigation, forms, and the excuse document are never distorted by the chaos presentation.
Built With
- ai
- chatgpt
- codex

Log in or sign up for Devpost to join the conversation.