Getting Started

This guide covers installation, configuration, and running your first Letopis instance.

Prerequisites

Dependency Minimum version Notes
Go 1.25 Required to build from source
MongoDB 7.0 One database per tenant is provisioned automatically
Redis 6.0 Used for the durable event queue, idempotency keys, and rule-cache invalidation
Docker any Optional; used for the compose stacks and integration tests

Install

Prebuilt binaries

Download an archive for your platform (linux/darwin/windows, amd64/arm64) from GitHub Releases. Each archive bundles the letopis binary with LICENSE, NOTICE, config.example.yaml, and docker-compose.deps.yml.

From source

git clone https://github.com/max-trifonov/letopis.git
cd letopis
make build          # produces bin/letopis

The binary embeds the version, commit hash, and build date via ldflags; make build sets them from git describe automatically.


Docker

A pre-built image is published at ghcr.io/max-trifonov/letopis. To build locally:

make docker          # tags ghcr.io/max-trifonov/letopis:latest

Two compose flavours are provided:

Development stack

docker compose -f docker-compose.dev.yml up --build

Builds the image from the working tree and brings up MongoDB, Redis, and the service together.

Production stack

cp config.example.yaml config.yaml   # edit before starting
docker compose up -d

Uses the published image. MongoDB and Redis are included for convenience; in production you will typically point mongodb.uri and redis.addr at your managed infrastructure.


Configuration

Letopis is configured by a YAML file. The binary searches for config.yaml in:

  1. The path given by --config (highest priority)
  2. The current working directory
  3. The directory containing the binary

The config file is required; the server will not start without it.

After the file is loaded, any LETOPIS_* environment variable overrides its corresponding key:

Environment variable Config key
LETOPIS_ROLE role
LETOPIS_HTTP_ADDR server.http.addr
LETOPIS_GRPC_ADDR server.grpc.addr
LETOPIS_MONGODB_URI mongodb.uri
LETOPIS_REDIS_ADDR redis.addr
LETOPIS_REDIS_PASSWORD redis.password
LETOPIS_REDIS_DB redis.db
LETOPIS_LOG_LEVEL log.level

Minimal configuration

The minimal production config must set:

Section Key fields
mongodb.uri Connection URI for the MongoDB cluster
redis.addr Redis address
tenants One entry per tenant with id and at least one API key (SHA-256 hash preferred)
webhooks.secrets Named signing secrets referenced by rules

See config.example.yaml for the full reference.


Starting the server

./bin/letopis serve

By default the role is all — HTTP + gRPC + worker in one process. This is correct for development and small installs. For production you can split:

./bin/letopis serve --role=api     # HTTP + gRPC only
./bin/letopis serve --role=worker  # async event processor only

Your first write

# Set your API key from config.yaml
TOKEN=hm_dev_plaintext

# Ingest a state change (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"}}'

Expected response (strict mode returns 201; default durable returns 202 with a ticket):

{
  "ticket_id": "tkt_01hx...",
  "status": "accepted"
}

Read it back

# Current state
curl -s http://localhost:8080/api/v1/collections/crm.deals/entities/deal-1/state \
  -H "Authorization: Bearer $TOKEN"

# Full history
curl -s http://localhost:8080/api/v1/collections/crm.deals/entities/deal-1/history \
  -H "Authorization: Bearer $TOKEN"

System endpoints

Endpoint Purpose
GET /healthz Liveness probe
GET /readyz Readiness — aggregated dependency checks
GET /metrics Prometheus metrics
GET /version Build info (version, commit, date)
gRPC :9090 letopis.v1.SystemService, standard health, reflection

Development

make test             # go test -race ./...
make test-integration # requires Docker — integration + e2e (testcontainers)
make lint             # golangci-lint + buf lint
make proto            # regenerate gRPC bindings
make bench            # diff/pipeline CPU benchmarks