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.
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
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.
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.
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/httpapilayer 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.