← All workSAAS · PRODUCT·2026

Nightshelf

Writing app for long-form fiction — Nightshelf is a writing application for novelists that refuses to separate the manuscript, the codex (characters, places, objects) and the story's chronology. You write a sentence, check a character's eye colour, and see where they appear across the book — without breaking flow. It's my own product, and the architecture is the point.

Symfony 7PHP 8.3API PlatformDoctrineSymfony MessengerPostgres 16MeilisearchReact 19TypeScriptTipTapViteDocker
Nightshelf
Sector
SaaS · Product
Year
2026
Role
Architect · full-stack build
Outcome
Symfony 7 modular monolith · React 19 + TipTap editor · my own product
The problem

What needed fixing.

Serious writers assemble a toolchain — one app for prose, another for the wiki, a third for sharing — and none of them know about the others. The silver amulet vanishes between chapter 3 and chapter 11 and nobody notices until a reader does. The three surfaces a novelist actually needs belong in one app; keeping them apart is the bug.

The approach

How I built it.

The backend is a Symfony 7 modular monolith — one application, bounded contexts (publishing, codex, search) that talk through domain events and narrow interfaces, with row-level tenancy enforced by voters on every resource. API Platform exposes it; Doctrine and Postgres store it, with prose held as JSONB. The front end is React 19 and TypeScript around a TipTap editor, where @mentions are live nodes — rename a character in month seven and every mention updates on read, never stale. Search is Meilisearch across prose and codex at once. It's the kind of system where the next feature is days of work, because the boundaries were drawn on day one.

A novelist types @Marta mid-sentence and sees her character card inline, without leaving the page. Three things most writing tools keep apart — manuscript, codex, chronology — in one app.

Objectives
  • 01Unify manuscript, codex and chronology in a single writing application
  • 02Keep prose and the character codex linked at the schema, not bolted together
  • 03Build a backend whose bounded contexts can grow without entangling
  • 04Make @mentions live references that never go stale on rename
  • 05Search prose and codex together, with chapter-level facets
Key elements built
  • Symfony 7 modular monolith — bounded contexts via domain events and service interfaces
  • API Platform + Doctrine + Postgres 16 (prose stored as JSONB)
  • Row-level tenancy enforced by Symfony voters on every resource
  • React 19 + TypeScript front end with a TipTap rich-text editor
  • Live @mention nodes with inline card lookup — rename once, update everywhere
  • Meilisearch global search across prose, codex and notes with facets
  • Autosave with rolling revision history; codex CardSets; writing stats

The page where three tools become one

Open a chapter and the manuscript fills the page. Type @Marta and her character card surfaces inline — eye colour, the town she's from, the chapter she first appeared in — without a second window, a second app, or a broken sentence. That single interaction is the whole thesis: prose, codex and chronology belong on the same surface.

Editor with inline @mention card popover
Spec
Editor
TipTap 2
Mentions
Live nodes, never stale
Autosave
Every 2s + rolling history
Frontend
React 19 · TypeScript

Rename a character in month seven and every mention updates on read.

The editor: prose in Newsreader, an @mention card surfacing inline.

What greets a new writer

Before any of the machinery, there's a page that has to make a novelist feel at home — warm paper, a big serif headline, the unmistakable sense that this was built by someone who reads. The public face of Nightshelf is its own small product: an editorial marketing site that explains the idea in the same quiet voice the app speaks in, rather than the usual feature-checklist shouting. First impressions are a design problem too, and most software treats them as an afterthought.

Nightshelf public site
Spec
Type
Newsreader + IBM Plex Sans
Build
Server-rendered for SEO
Tone
Editorial, not feature-checklist
The public face: "The novel, in one manuscript." Editorial, paper-feel, on-brand.

The author's room

The editor is the centre of a whole studio, not a text box with ambitions. Six kinds of card — characters, locations, objects, factions, events, lore — each with its own colour and its own short form, all reachable from a single create dialog that never makes you choose a screen first. Select a sentence and a sidenote opens in the margin, anchored to the exact words, not floating somewhere near them. Mention a character who doesn't exist yet and the autocomplete offers to make them on the spot. None of it asks you to leave the page you're writing on.

Cards live in card sets — a writer's world bible, the place a series keeps its facts consistent. One set can attach to several books at once, so the lore of a trilogy lives in a single source that every volume reads from; rename a faction once and three books agree. Sets can be shared with a named team or kept private, made public as a worldbuilding showcase or unlisted behind a link. A keyboard-first search reads across chapters, cards, sets, guides and people at once, with a live preview beside the results. And because people write for hours, the whole thing themes — warm paper by day, dusk and e-ink for the late shift.

Writing in the open

Writers like to be read, so the studio has a public side. Every author gets a profile at their pen name — published books, the world bibles they've chosen to show, a quiet calendar of a year's writing, and only the stats they opt into. It's closer to a writer's public shelf than a social network: no follower counts to perform for, no feed to feed. A world bible or a finished chapter can be handed to a beta reader as a link that opens cleanly on a phone, which is where half of reading actually happens.

And because a tool for a craft attracts people who want to talk about the craft, there are guides — a knowledge base that's part official and part written by the writers themselves. How to keep a series bible that pays off, how to take a manuscript from blank page to export, contributed in Markdown and read like the rest of the product: serif prose, no clutter. The best documentation for a writing tool turns out to be writing.

A backend drawn on day one

All of it runs on one Symfony 7 backend — a modular monolith, organised into bounded contexts (publishing, codex, search, sharing) that speak through domain events and narrow interfaces. The data model is where the foresight shows: a card belongs to a card set, and a card set attaches to many books, which is the single decision that turns "a codex per book" into "a shared universe across a series" without a rewrite. Prose is stored as JSONB in Postgres, Meilisearch indexes prose, cards, guides and people together, and tenancy is enforced on every resource. The boundaries were drawn before the first feature, which is why the next one is days of work, not weeks.

Modular-monolith architecture diagram
Bounded contexts inside one Symfony application — events between, interfaces across.
Full stack
Symfony 7PHP 8.3API PlatformDoctrineSymfony MessengerPostgres 16MeilisearchReact 19TypeScriptTipTapViteDocker
Next projectEternalgarage v2 Headless car-community platform

Got something with similar shape? I read every message personally.

Start a project →