[{"data":1,"prerenderedAt":215},["ShallowReactive",2],{"i-lucide:layers":3,"i-lucide:git-commit-horizontal":8,"i-lucide:refresh-cw":10,"i-lucide:book-open":12,"i-lucide:image":14,"i-lucide:webhook":16,"i-lucide:home":18,"i-lucide:layout-grid":20,"i-lucide:tag":22,"i-lucide:newspaper":24,"i-lucide:map":26,"i-lucide:flask-conical":28,"i-lucide:arrow-right":30,"i-lucide:chevron-right":32,"i-lucide:search":34,"i-lucide:monitor":36,"content:\u002Finternal\u002Farchitecture\u002Ffoundations":38,"i-lucide:info":213,"i-lucide:layers-2":210,"i-lucide:key-round":210,"i-lucide:shield-check":210,"i-lucide:box":210,"i-lucide:share-2":210,"i-lucide:clock":210,"i-lucide:dices":210,"i-lucide:building-2":210,"i-lucide:bell":210,"i-lucide:sprout":210,"i-lucide:shield-alert":210,"i-lucide:puzzle":210,"i-lucide:activity":210,"i-lucide:blocks":210},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z\"\u002F>\u003Cpath d=\"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12\"\u002F>\u003Cpath d=\"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"3\"\u002F>\u003Cpath d=\"M3 12h6m6 0h6\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":11},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M3 12a9 9 0 0 1 9-9a9.75 9.75 0 0 1 6.74 2.74L21 8\"\u002F>\u003Cpath d=\"M21 3v5h-5m5 4a9 9 0 0 1-9 9a9.75 9.75 0 0 1-6.74-2.74L3 16\"\u002F>\u003Cpath d=\"M8 16H3v5\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":13},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 5v16m8.001-2A2 2 0 0 0 22 17V5a2 2 0 0 0-1.999-2L16 3.002A5 5 0 0 0 12 5a5 5 0 0 0-4-2H4a2 2 0 0 0-2 2v12a2 2 0 0 0 1.999 2H8a5 5 0 0 1 4 2a5 5 0 0 1 4-2z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":15},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\"\u002F>\u003Ccircle cx=\"9\" cy=\"9\" r=\"2\"\u002F>\u003Cpath d=\"m21 15l-3.086-3.086a2 2 0 0 0-2.828 0L6 21\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":17},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2\"\u002F>\u003Cpath d=\"m6 17l3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06\"\u002F>\u003Cpath d=\"m12 6l3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":19},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8\"\u002F>\u003Cpath d=\"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":21},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"7\" height=\"7\" x=\"3\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"7\" x=\"14\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"7\" x=\"14\" y=\"14\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"7\" x=\"3\" y=\"14\" rx=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":23},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z\"\u002F>\u003Ccircle cx=\"7.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":25},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M15 18h-5m8-4h-8m-6 8h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-4 0v-9a2 2 0 0 1 2-2h2\"\u002F>\u003Crect width=\"8\" height=\"4\" x=\"10\" y=\"6\" rx=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":27},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0zm.894.211v15M9 3.236v15\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":29},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2M6.453 15h11.094M8.5 2h7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":31},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":33},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m9 18l6-6l-6-6\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":35},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"m21 21l-4.34-4.34\"\u002F>\u003Ccircle cx=\"11\" cy=\"11\" r=\"8\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":37},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"\u002F>\u003Cpath d=\"M8 21h8m-4-4v4\"\u002F>\u003C\u002Fg>",{"id":39,"title":40,"body":41,"description":203,"extension":204,"eyebrow":205,"hostedOnly":6,"meta":206,"navigation":207,"path":208,"seo":209,"short":210,"stem":211,"__hash__":212},"docs\u002Finternal\u002Farchitecture\u002Ffoundations.md","Foundations",{"type":42,"value":43,"toc":192},"minimark",[44,48,62,67,75,78,81,85,92,100,103,107,118,121,125,132,136,151,155,162,165,169,180],[45,46,47],"p",{},"Most applications store the current state of things. A row holds what a resource looks like now, and an update overwrites it. That is simple until you need to know how the resource got there, or you need a second view of the same data, or you need to prove who changed what. Then the overwrite has thrown away the one thing you wanted.",[45,49,50,51,56,57,61],{},"Chronicler makes a different trade. It stores the change, not the state. Every write is recorded as an event, and the current state is something the system computes from the events. The ",[52,53,55],"a",{"href":54},"\u002Finternal\u002Farchitecture\u002Fevent-sourcing","Event sourcing"," and ",[52,58,60],{"href":59},"\u002Finternal\u002Farchitecture\u002Fprojections","Projections"," pattern pages walk through that core loop. This page covers the rules that sit under it, and why each one is there.",[63,64,66],"h2",{"id":65},"why-events-and-not-rows","Why events, and not rows",[45,68,69,70,74],{},"When a write goes through an ",[71,72,73],"strong",{},"aggregate"," and emits an event, the event stream keeps the full history of every change. That history is not a feature anyone had to build. It falls out of the design, and it pays for three things at once.",[45,76,77],{},"A read model can be rebuilt at any time by replaying the events, so a bad projection is a rebuild, not a data-loss incident. The audit trail is a fact in the stream rather than a guess reconstructed later. And a new view of the data is just a new projector reading the same events, so adding a screen never means migrating a table.",[45,79,80],{},"The cost is that you think in events. A write validates against current state, then records what happened. It does not reach in and set a column.",[63,82,84],{"id":83},"one-kind-of-id-with-a-hint-on-the-front","One kind of id, with a hint on the front",[45,86,87,88,91],{},"Every entity is identified by a single ",[71,89,90],{},"UUIDv7",". There is no second, internal id living beside a public one, because two ids for the same thing is a bug waiting to happen: one leaks, the other drifts, and a join eventually uses the wrong one.",[45,93,94,95,99],{},"At the edge of the API the id wears a short typed prefix, like ",[96,97,98],"code",{},"res_"," for a resource. The prefix is a courtesy to whoever reads the id. It says what the id points at, and it turns a pasted-in wrong id into an error the moment it arrives rather than a confusing lookup that returns nothing. The prefix is added and stripped at the edge. It is never stored, so the database keeps clean UUIDs.",[45,101,102],{},"An earlier version of Chronicler used integer keys inside and typed ids on the surface. That design is retired. Its record survives as a superseded ADR, so the reason for the change is not lost to anyone who wonders why.",[63,104,106],{"id":105},"two-databases-on-purpose","Two databases, on purpose",[45,108,109,110,113,114,117],{},"Chronicler runs on ",[71,111,112],{},"SQLite"," or ",[71,115,116],{},"PostgreSQL",", and this is a product decision, not an accident of portability. A person who wants to run Chronicler on their own machine should get one binary and a file, with no database server to install and babysit. That is SQLite. A hosted deployment that serves many people wants Postgres. Supporting both means neither can be treated as the afterthought that quietly breaks.",[45,119,120],{},"The way that stays honest is the test suite. Every persistence test runs against both databases, so a query that works on one and fails on the other fails in the pipeline, not in front of a user. The discipline it demands is real: every migration and every query has to hold on both dialects. The payoff is a self-host story that costs nothing extra to operate.",[63,122,124],{"id":123},"the-event-store-is-nats-and-it-comes-along-for-free","The event store is NATS, and it comes along for free",[45,126,127,128,131],{},"The event log lives in ",[71,129,130],{},"NATS JetStream",". For a self-host install, NATS runs embedded inside the same process, so the whole product is still one binary with nothing else to run. A hosted deployment can point at a NATS cluster instead. Nothing in the code changes between the two. It is a line of configuration.",[63,133,135],{"id":134},"every-event-knows-where-it-came-from","Every event knows where it came from",[45,137,138,139,142,143,146,147,150],{},"An event is more than its payload. Each one carries a stable id, a ",[71,140,141],{},"correlation id"," that ties together everything done in one logical operation, a ",[71,144,145],{},"causation id"," that points back at the event that caused it, and structured ",[71,148,149],{},"provenance"," that says where it originated. These are proper typed values, not a request string that someone reused and hoped stayed unique. Together they let the audit log and the traces reconstruct not just what happened, but why it happened and what set it off.",[63,152,154],{"id":153},"some-things-end","Some things end",[45,156,157,158,161],{},"Not every aggregate lives forever. A ",[71,159,160],{},"terminal event"," closes its aggregate, and once closed, the aggregate refuses every later command. Deletion and other end-of-life steps become a rule the aggregate enforces for itself, rather than a check every caller has to remember and one caller eventually forgets.",[45,163,164],{},"Snapshotting is built into the same contract from the start, even though the first version keeps it simple. Rebuilding state by replaying millions of events is slow, and a snapshot is the shortcut. Wiring the contract in early means the shortcut can arrive later without a redesign.",[63,166,168],{"id":167},"where-this-connects","Where this connects",[45,170,171,172,174,175,179],{},"These rules feed everything above them. ",[52,173,60],{"href":59}," turn the event stream into the read models the app serves. ",[52,176,178],{"href":177},"\u002Finternal\u002Farchitecture\u002Faudit","Security, audit and compliance"," leans on event provenance for a trail that holds up.",[181,182,185],"callout",{"title":183,"tone":184},"Read the record for the detail","note",[45,186,187,188,191],{},"This page is the guide. The full records under ",[96,189,190],{},"docs\u002Fadrs"," carry the exact rules, the dialect gotchas, and the alternatives that were weighed and rejected.",{"title":193,"searchDepth":194,"depth":194,"links":195},"",2,[196,197,198,199,200,201,202],{"id":65,"depth":194,"text":66},{"id":83,"depth":194,"text":84},{"id":105,"depth":194,"text":106},{"id":123,"depth":194,"text":124},{"id":134,"depth":194,"text":135},{"id":153,"depth":194,"text":154},{"id":167,"depth":194,"text":168},"The event-sourcing core and the rules that hold under it - why a write becomes a fact first, and what that buys the rest of the system.","md","Architecture",{},{"title":40},"\u002Finternal\u002Farchitecture\u002Ffoundations",{"title":40,"description":203},null,"internal\u002Farchitecture\u002Ffoundations","n-KCSTrGVJ_CeXmOZrLDRo_wo0xqof0zceRPTzoUOJk",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":214},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"M12 16v-4m0-4h.01\"\u002F>\u003C\u002Fg>",1788784066564]