Architecture — BYOK, Streaming, and the AI-Blind Firewall

Three tables and one principle: how bring-your-own-key, streamed Claude calls, the bot registry, and the snapshot boundary fit together.

Architecture: BYOK, Streaming, and the AI-Blind Firewall

UnboundProse is a manuscript feedback platform — authors share chapters, readers leave inline annotations, and AI editorial bots run on top. The interesting architecture isn't the CRUD; it's the parts that make AI safe to put in front of writers: who pays for the tokens, what the model is allowed to read, and how a new bot gets added without new plumbing.

The stack

The API is Hono + Drizzle ORM + PostgreSQL, written in TypeScript. The web app is Next.js (App Router). AI calls go to Anthropic's Claude via the official SDK, server-side, with responses streamed to the browser over Server-Sent Events. The web app deploys to Cloudflare Pages; the database lives on a shared Postgres host.

Bring your own key

Authors supply their own Anthropic API key. The cost of every scan lands on their account, and the data path is theirs. Keys are encrypted at rest with AES-256-GCM using a server-side secret; only the last four characters are stored in the clear, so the Settings page can show which key is saved without ever exposing it. The server refuses to boot if the encryption secret is missing — a misconfiguration that would weaken key storage fails loudly at startup instead of silently at runtime.

BYOK does two things at once: it removes the platform's per-token cost risk for a feature with unbounded usage, and it gives the author a credible promise about where their manuscript goes.

The bot registry: adding a bot is one entry

The first bot (POV Consistency) was built as the proof-of-concept for a framework, not a one-off. Every chapter-level bot is a single entry in a TypeScript registry — an id, a label, the human-facing copy, and a function that builds its system prompt from the chapter's metadata and content. Adding the second bot (Chapter Opening & Ending) was exactly that: one new module plus one map entry. No new tables, no new routes, no new streaming code. The conversation schema, the streaming endpoint, the key storage, and the drawer UI were all already there.

That discipline — build the second instance's worth of generality the first time, but no more — kept the surface area small while making the platform genuinely extensible.

Snapshot on first turn

Each bot conversation is keyed to a (chapter, bot) pair and, on its first turn, snapshots the resolved metadata and the chapter content it reasoned about. The thread stays meaningful even after the author edits the chapter later: you can always see what the bot was actually looking at when it made a finding. Token counts for every model turn are persisted alongside the messages, so usage is auditable per conversation.

Token budgeting as a first-class concern

The manuscript-level Workshop can put many chapters in context at once, so token budget stops being an afterthought. The client shows a live estimate as chapters are selected and disables sending past the model's window. The server independently recomputes the real request size before calling Anthropic and rejects an over-budget turn with a clear error rather than letting the upstream call fail. The estimate is advisory; the server-side guard is authoritative. Neither silently drops content.

The firewall: snapshots are never sent to a model

The single most important architectural rule sounds like a UI detail but is enforced in the data layer: every bot reads only the live, current content of a chapter. Author-created snapshots are never sent to any model.

Snapshots — the author's private, named, versioned drafts — live in their own surface, walled off from every AI call. The Workshop's message route loads chapter text exclusively from the live chapters table; it has no path to the snapshot store. And because the open-ended Workshop is a different shape from the chapter bots, it's registered as a catalog-only entry that cannot resolve through the chapter-bot message route at all — the separation is structural, not just a convention someone has to remember. A reviewer can prove the firewall holds by following the imports.

Why these choices

Each decision answers a trust question a writer would actually ask. Who sees my manuscript and pays for it? You do — BYOK, encrypted. Can I keep private drafts the AI never reads? Yes — snapshots are firewalled. Will it pretend it read my whole book? No — the token budget is visible and enforced. The architecture is, in effect, a set of promises made structural enough that they can't quietly break.