Early alpha Chronicler is pre-release software and changes often. Join the waitlist
Architecture

Platform, API & Deployment

The cross-cutting shape every context shares - how responses are built, how lists behave, how a context is laid out, and how the app is shipped.

These are the decisions that are not about any one feature but about the shape everything shares. They matter because consistency is what lets a new context feel familiar and a new screen behave predictably. When every list sorts the same way and every response is built the same way, there is one thing to learn rather than fifty.

Responses are built for any consumer, not one screen

A response never tailors itself to the screen asking for it. It returns a stable base with enough for any consumer, and richer data is opted into with ?with=. The extra data is a sidecar, computed when it is asked for and never stored, so it cannot go stale. Some sidecars are counts computed from a table, others are things like the caller's own permissions resolved through a port. The rule against shaping a response to one screen is what stops the API fragmenting into a different view per page.

Lists behave the same everywhere

Filtering, searching, and sorting always happen server-side, in the query, never client-side over a page that has already been fetched. The reason is not performance but correctness: a client-side sort only sees the loaded page, so counts and ordering go wrong the moment there is a second page. Every list declares its allowed sort fields as a typed enum, takes a shared direction, and a time field defaults to newest-first while a text field defaults to A-to-Z. Facet counts are computed server-side for the same reason.

One layout for every context

Every bounded context follows one package layout, the same handful of roles in the same places: the API, the domain, the service, the store, the reactors, plus a few optional roles. Anything that implements a port with a specific technology lives in an infra package and is wired up in one composition root. A directory and its package share a name, plural for a countable thing and singular for a mass or acronym name. The payoff is that finding your way around a context you have never opened takes no thought.

Shipped as one binary, or two halves

The build produces two artifacts from the same code. For self-host, it is a single binary with the whole web app embedded, so running Chronicler is running one file. For the hosted edition, it is an API-only binary paired with the web app on a CDN, a split origin. The frontend is served by mounting as the fallback handler, so it answers only what no API route claimed. One wrinkle is worth knowing: the SQLite full-text build needs a C compiler, so the release image cross-compiles with zig to produce the binary without dragging a full toolchain along.

Where this connects

The dual-artifact build serves the two editions from Modules and editions, and the dual-dialect rule that shapes the store is the same one behind Search.

Read the record for the detail

This page is the guide. The full records under docs/adrs carry the sidecar conventions, the context-layout rules, and the alternatives that were weighed and rejected.