chronicle GitHub ↗
v0 · experimental — API may change before v1

Every change, sealed into a chain you can verify.

A durable, database-agnostic changelog (audit-log) library for Go. It's intentionally git-shaped: stage edits, seal them into content-addressed commits hash-chained to their parents, keep a history per document. Store it all behind a 3-method Log port.

Commits(ctx, "invoice-42") — newest-first
pending — not yet sealedstaged
status: sent → paid · put · bob
at 09:21:44Z — stamped by Append · hashed at Commit
5c81d40send invoicesealed · Head
status: draft → sent · put · bob
At 2026-08-05 09:14:08Z · Authors [bob]
parent 9f3ab12 · ID = SHA-256(parent, message, changes)
9f3ab12create invoiceroot
amount: ∅ → 1200 · create · alice
status: ∅ → draft · create · alice
At 2026-08-04 17:02:31Z · Authors [alice]
parent "" — history begins at the first commit
01 quick start

Stage, seal, read back.

One import for the core (standard library only — the package is changelog), one for a backend. Swap the in-memory dev backend for MySQL or ClickHouse later; the Recorder doesn't change.

// go get github.com/zdirnecamlcs96/chronicle/core
// log := changelogmemory.New() — dev/test; swap for a durable adapter in prod

rec := changelog.NewRecorder("invoice-42", log)
rec.Append(changelog.Change{Actor: "alice", Path: "status", Kind: "put", From: "draft", To: "sent"})
commit, err := rec.Commit(ctx, changelog.WithMessage("send invoice"))
// commit.ID = SHA256 over length-framed (parent, message, canonical(changes)), chained onto Head

history, _ := log.Commits(ctx, "invoice-42", 0) // newest-first

$ go get github.com/zdirnecamlcs96/chronicle/core@latest

02 what's in the box

Small surface, strong guarantees.

Tamper-evident hash chain

Commit.ID is SHA-256 over length-framed parent, message, and canonicalized changes. Rewrite history and VerifyChain breaks.

One 3-method port

The whole library hangs off Log: AppendCommit, Commits, Head. Implement it and any store becomes a backend.

stdlib-only core

The core module imports nothing beyond the standard library. Database drivers live in the adapters' own go.mods.

Incremental verification

VerifyAfter checks only commits past a persisted cursor — no full-chain replay on every audit.

Idempotency keys

At-least-once delivery seals exactly one commit: a retried key returns the original commit instead of a duplicate.

Time travel

StateAt reconstructs a document at any commit, snapshot-backed — O(commits since snapshot), not O(all history).

JSON-Patch interop

The kit module diffs states, converts change batches to and from JSON-Patch, and reconstructs state from history.

Dual-audience changes

The record stays machine-shaped; kit.Explain derives label trails, element names, and per-field value breakdowns at read time — structure only, the styling stays yours.

Conformance as contract

A backend is correct when it passes the suite — that's what makes “database-agnostic” trustworthy, including for a Log you write.

Graceful degradation

Four optional capabilities — Indexer, Deduper, TailReader, Snapshotter. A backend without them still works.

03 documentation

Then read the rest.

The mental model, the design patterns, the kit walkthrough, and what running it costs — all under documentation.

Start

Why It Exists

The problem it solves: every incident, bug or dispute, reduces to what the data was, what it is now, when it changed, and who changed it — and why that answer belongs behind a port rather than in one database.

Getting Started

One program, grown five times: record a change, read the document back, verify the chain, then swap the dev backend for a real database by changing one line.

Understand

Git Model

The side-by-side with git: what Append, Commit, and Head correspond to, the one inversion (git stores snapshots, chronicle stores diffs), and the two git reflexes that deliberately don't exist here.

Architecture

The map — how the repo is laid out, for readers who know Clean Architecture or ports-and-adapters. The rings are go.mod files, not folders.

Design Decisions

The rationale behind that map — one decision per section, each with the alternative that was rejected and what the choice costs. Read it to judge, extend, or argue with the design; you do not need it to use chronicle.

Build

The Kit

chroniclekit end to end — diff a document into changes, seal them, replay them back into display-ready rows, driven by one schema vocabulary.

Sealing Patches

Optional: the RFC 6902 write path, nine stages from request to commit. Only if a client sends you JSON Patch — a patch carries no before-values, and sealing one directly records that gap permanently.

Operate

Operations

Running it for real: picking a backend by consistency model, pool tuning, migrations, pruning, backup and restore, observability.

Reference

Reference

The normative contract — every exported call's inputs, guarantees, and failure modes, the error sentinels, and what the library deliberately does not do.

Concepts

The lookup table for names — hash chain, event sourcing, snapshots, LCA — what each pattern is called, which file implements it, and the prior art it comes from.

Contributing

The invariants a change must not break, the file-by-file reading order for the source, and what the conformance suite expects of a new backend.

04 caveats

Read it before you run it.

What the library does not promise, stated before you depend on it. The full list — no merges, no retention policy, no auth anywhere — is in reference.

  • v0, experimental. Pin a specific module version; modules are versioned independently (core/vX.Y.Z, adapters/sql/vX.Y.Z, …).
  • The memory adapter is reference/test only — never production.
  • The kit/httpapi layer ships no authentication, authorization, or rate limiting — those are the deployer's responsibility. Review against your own threat model before exposing it to untrusted input.