tln-db is the Go-native embedded fact store and query
engine for Tln. It sits behind the FactStore interface (see Plugins), so the
language core stays storage-agnostic while tln-db provides a fast, durable, self-contained backend.
Facts are the entity–attribute records Tln reasons over — loaded from your systems, never written
in .tln. tln-db stores, indexes, and queries them.
Two ways to run it
- Embedded — a Go library, in-process (
bboltstore.Open()). - Sidecar — a standalone
tlndb-serverover gRPC (Unix socket or TCP) with an HTTP/JSON debug endpoint, so several processes can share one store (Postgres-style local socket).
Select it as your store
With the bundle system, tln-db is a store plugin you declare in
mod.tln and point at the sidecar in config/store.tln — Active-Record style, no Go host:
# mod.tln
plugin "db" "v0.1.0" store
# config/store.tln
store db { target env "TLNDB_ADDR" } # e.g. unix:///tmp/tlndb.sock
tln bundle wires it in through the store factory; the bundled tlnstore client is thin — it
just dials tlndb-server (no bbolt / HNSW / roaring pulled in), so a bundle stays light while the
heavy engine lives in the sidecar.
Choosing a store is opt-in and code-free. With no config/store.tln, Tln uses the built-in
in-memory store — delete those two lines and you’re back to memory, same rules.tln. A project
has at most one store, resolved by precedence: a host’s WithFactStore wins → else the manifest
store plus config/store.tln → else in-memory. Config values resolve from the environment at run
time (env "TLNDB_ADDR"), so no address or secret lives in the source.
What’s inside
Built on proven Go building blocks, tuned for rule evaluation:
- Document store — snappy-compressed JSON in per-tenant buckets (strict isolation), ACID, SIGKILL-durable, on bbolt (B+ tree).
- Inverted index — roaring-bitmap-backed lookups: equality, numeric ranges, temporal windows, group-by, closure tables, running stats (Welford), and absence queries.
- Vector search — per-(entity, scope) HNSW index with cosine / Euclidean distance, for the
language’s
find similar/ retrieval needs. - Composite queries —
Query(pattern / predicate / or / not / full-text + aggregates + group-by),SequenceJoin,ClusterQuery, and a streamedSubscribefor reactive consumers.
Queries run in two phases: narrow (intersect docID bitmaps from the inverted index) then evaluate (decode candidates and check the remaining clauses) — index-fast where it can be, exact where it must be.
Swappable and tested
tln-db ships a conformance suite that any FactStore backend runs against, so alternatives
drop in without language-level changes. Core itself ships only the built-in in-memory store
(tests / REPL); every other backend is a store plugin. The other one today is
tln-datalevin — the HTTP client for the JVM
datalevin-server (Clojure Datalog), a sidecar like tln-db but a different transport. It’s selected
the same Active-Record way:
# mod.tln
plugin "datalevin" "v0.1.0" store
# config/store.tln
store datalevin { url env "DATALEVIN_URL" } # e.g. http://localhost:8898
Timestamps are clock-injectable for deterministic tests, and a mutation event stream (assert / change / retract) makes changes auditable — the same determinism-and-explainability story as the language itself.
Source: github.com/opentalon/tln-db.