Under the story
How it's built
A reader-first writing system: long-running work streams in, durable chapters land as they finish, and your story stays yours.
Architecture
Request → work → storyBrowser PWA
Service worker shell, IndexedDB offline books, sync on reconnect.
FastAPI on Vercel
Jinja2 pages, JSON endpoints, server-sent events (SSE).
Postgres / Supabase
ll_* tables · Alembic migrations.
The API and job runner persist jobs, chapters, and story memory here.
Job runner
Lease and claim, resume, cancel, and stream progress.
pydantic-ai
- Outline designer
- Chapter writer
- Parallel chapter workers
- Continuity keeper
- Choice suggester
- Manuscript reviewer
- Character role-play
- Reading assistant
OpenRouter
Bring your own key (BYOK); choose models for your work.
From premise to page
create_bookdesigns and startsThe job shapes Story DNA and an outline, then writes and saves Chapter 1.
The reader opens
Chapter 1 is ready while the rest of the manuscript grows in the background.
grow_chaptersworks concurrentlyAsyncio workers write chapters 2+, persist each as it finishes, and stream
chapter_doneevents to the reader.Continuity catches up
Saved chapters are summarized into story memory so later work can stay aligned.
Reliability
- Idempotency keys prevent duplicate requests.
- In-flight growth jobs are reused per branch.
- Each finished chapter is persisted independently.
- Unique
(branch, index)constraint protects chapter order. - Failed chapters can be retried; disconnected clients release the job lease.
Security
- Fernet / MultiFernet encrypted keys; only the last four characters are shown.
- CSRF double-submit cookie for state-changing requests.
- Rate-limited authentication and a Content Security Policy (CSP).
- Allowlist HTML sanitizer on import.
Stack · FastAPI · Jinja2 · SQLAlchemy · Postgres / Supabase · Alembic · pydantic-ai · OpenRouter · vanilla JavaScript · Tailwind CDN · PWA
← Back to the library