Immutable history
for every entity.
Letopis stores every change to every object in your system — with tamper-evidentSHA-256 hash chains, point-in-time reconstruction, and conditional webhooks. REST + gRPC. Multi-tenant. Self-hosted.
# Set your API key from config.yaml
TOKEN=hm_dev_plaintext
# Ingest entity state — server computes the diff
curl -s -X POST \
http://localhost:8080/api/v1/collections/crm.deals/entities/deal-1/state \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"state": {"title": "Acme Corp", "amount": 5000, "stage": "prospect"}}'
# Read full history
curl -s \
http://localhost:8080/api/v1/collections/crm.deals/entities/deal-1/history \
-H "Authorization: Bearer $TOKEN"
# Point-in-time: state at version 3
curl -s \
"http://localhost:8080/api/v1/collections/crm.deals/entities/deal-1/state?version=3" \
-H "Authorization: Bearer $TOKEN"Features
Everything you need for audit-grade history
From tamper-evident storage to conditional webhooks — Letopis gives you a complete, production-ready audit log infrastructure.
Hash-chain integrity
SHA-256-linked events make silent tampering or deletion immediately detectable. Verify any entity's complete history with the :verify endpoint.
Point-in-time reads
Reconstruct the exact state of any entity at any past version or timestamp. Snapshots keep read latency under 7 ms p99 for 10 000-version histories.
Two ingest modes
POST a full object state and let Letopis compute the diff, or POST a ready-made diff directly. Both produce identical, queryable events.
Three reliability modes
Choose per-request: strict (sync 201), durable (Redis-queued 202), orfast (in-memory 202). Ticket polling confirms async writes.
Rules + webhooks
Define conditional rules that fire signed, retried HTTP callbacks whenever matching changes arrive. Dead-letter queue for failed deliveries.
Multi-tenancy
Hard MongoDB-database isolation per tenant. API-key-based routing — no tenant ID in URLs. Per-tenant config, rules, and reliability defaults.
How it works
Three steps from write to query
Ingest
Your system POSTs the entity's current state or a ready-made diff. Letopis validates, normalizes, and queues the write with your chosen reliability mode. An optional idempotency key prevents duplicate events on retry.
Store
Events are written append-only to MongoDB. The SHA-256 hash-chain plugin links each event to the previous one. Every 100 events a snapshot materializes the current state for fast reads. Async modes use Redis Streams for durability.
Query
Read current state, paginated history, or reconstruct state at any past version or timestamp. Business flows link events across collections into causal DAGs. Export diffs as JSON Patch (RFC 6902) for compatibility.
Quick start
Up and running in minutes
Build from source
Prerequisites: Go 1.25+, MongoDB 7+, Redis 6+.
Configure tenants
Copy config.example.yaml, set your tenant ID and API key (SHA-256 hash preferred).
Ingest & query
Send your first state change via REST. Use GET /history to verify it was stored.
# Or download a prebuilt binary: github.com/max-trifonov/letopis/releases
# Clone and build
git clone https://github.com/max-trifonov/letopis.git
cd letopis && make build
# Configure (edit tenants.*.keys)
cp config.example.yaml config.yaml
# Run
./bin/letopis serve
# Or with Docker Compose
docker compose -f docker-compose.dev.yml up --buildClient SDKs
Typed clients for your stack
Skip hand-rolled HTTP calls — official SDKs wrap the REST API with request builders and framework integration.
Laravel SDK
PHP 8.2+ · Laravel 11/12// Eloquent observer records changes automatically
Letopis::ingest('crm.deals', 'd-1')
->authorId('42')
->state(['amount' => 250]);composer require letopis/laravel-sdk →Node SDK
Node.js 18+ · TypeScript// Zero runtime dependencies, native fetch
await letopis.ingest('crm.deals', 'd-1')
.authorId('42')
.state({ amount: 250 })npm install letopis-node →Use cases
Built for teams that need accountability
CRM & Sales
Full audit trail for every deal, contact, and pipeline stage change. Know exactly who moved the deal — and when.
Contracts & Documents
Tamper-evident version history for every agreement. Reconstruct any clause at any revision for dispute resolution.
Finance & Compliance
Immutable ledger for balance changes, transaction metadata, and account state. Ready for regulatory audit by design.
Config & Infrastructure
Track every change to your configuration, infrastructure state, and deployment parameters with author attribution.
Reliability
Choose the right guarantees per write
Override reliability at the collection level or per individual request with the X-Letopis-Mode header.
| Mode | Response | Guarantee | Use case |
|---|---|---|---|
strict | 201 Created | Written to MongoDB before responding | Audited writes, financial records |
durable | 202 Accepted | Queued to Redis Streams; worker writes async | Default — survives API restarts |
fast | 202 Accepted | In-memory queue; maximum throughput | Bulk imports, non-critical streams |
Async modes return a ticket_id. Poll GET /tickets/{id} to track:accepted → processing → stored. Tickets expire after 24 h by default.
Open source
Built in the open
Letopis is Apache 2.0 licensed. Contributions are welcome — every commit needs a DCO sign-off. Discuss significant changes in an issue before investing in code. The public Go API is pkg/ext; internal packages are not stable.
Ready to track every change?
Install Letopis in minutes and start building audit-grade history into your application.