Versioned · Fast · Branchable
The memory model for intelligence
When people or agents change shared data, you need to understand what changed and decide what to keep. Datahike lets your application query past states, develop a proposal on a separate branch, and compare it with the accepted data.
Start with relational queries and durable storage in your application or on a server. History and branching use the same database model.
What would this change affect?
Consider a shared project plan. Someone proposes a later delivery date, with changes to several dependent tasks. The team needs to review the revised plan while work continues against the accepted one.
-
1Develop the proposal
Create a database branch and make the related edits there. Unchanged data is shared, so starting a branch does not copy the full database.
-
2Compare the consequences
Your application recalculates the schedule from the proposed data. Query both database states to show the changed dates and tasks.
-
3Review and accept
Show the proposal to the team and apply the agreed changes. The original database value stays available to explain what the review was based on.
Datahike provides the database states and queries; your application supplies the scheduling rules and review process. The same pattern supports an editor's drafts, a data correction, or a proposal generated by an agent. Simmis uses it for shared knowledge and proposed changes.
See how branches are compared and merged →
Coming from InstantDB? Compare the application and migration options →
Try it in your application
Run a query, retain a database value, and compare it with a later state. Choose your starting point:
Each path uses the same database model. Clojure exposes it most directly: immutable values in the language, a live REPL, and the APIs Datahike itself uses.
// npm install datahike. The same code runs in Node.
// const d = require('datahike'); ← in Node; already loaded on this page
const config = { store: { backend: ':memory', id: d.randomUuid() }, // fixed in a real database
'schema-flexibility': ':read' };
await d.createDatabase(config);
const conn = await d.connect(config);
await d.transact(conn, [{ name: 'Alice', role: 'engineer' },
{ name: 'Bob', role: 'design' }]);
const before = await d.db(conn); // a database value, kept
await d.transact(conn, [{ name: 'Carol', role: 'engineer' }]);
const now = await d.db(conn);
console.log('engineers:', await d.q(
'[:find ?n :where [?e :role "engineer"] [?e :name ?n]]', now));
// Two database values in one query. Another branch, or a colleague's
// database on another bucket, would go in the same way.
console.log('new since:', await d.q(
'[:find ?n :in $now $before :where [$now ?e :name ?n] (not [$before ?e :name ?n])]',
now, before));
// Fork it. The branch shares storage with the trunk; nothing is copied.
await d.branch(conn, ':db', ':proposal'); Edit it and run again. The engine is 453 KB gzipped and is fetched only when you press Run. The same package runs in Node: typed API, Node and browser builds, optimistic updates, the same Datalog.
Choose an npm entry point and configuration to suit the application. Thin (datahike/remote, about 5 KB gzipped in Datahike 0.8.1881) calls a server and holds no database, with cacheable reads and a change stream to listen on. Replica (datahike/kabel) keeps a copy in IndexedDB, queries it locally and writes through the server. The peer reconnects and refreshes its token; the application restores the database subscription. Datahike Server provides per-database permissions when configured with a system database. Embedded is what runs above, with no server at all. See the JavaScript API docs or the playground.
// pom.xml: org.replikativ : datahike : LATEST (repository https://repo.clojars.org/)
import datahike.java.*; import java.util.*;
var cfg = Database.file("./db").keepHistory(true).build();
Datahike.createDatabase(cfg);
var conn = Datahike.connect(cfg);
Datahike.transact(conn, List.of(Map.of(":name", "Alice", ":role", "engineer"),
Map.of(":name", "Bob", ":role", "design")));
var before = Datahike.deref(conn); // a database value, kept
Datahike.transact(conn, List.of(Map.of(":name", "Carol", ":role", "engineer")));
Datahike.q("[:find ?n :where [?e :role \"engineer\"] [?e :name ?n]]", Datahike.deref(conn));
// Two database values in one query, from any JVM language.
Datahike.q("[:find ?n :in $now $before :where [$now ?e :name ?n] (not [$before ?e :name ?n])]",
Datahike.deref(conn), before); Full Datalog, including joins, aggregates, pull expressions and rules, from any JVM language. Also a Babashka pod and a C library via libdatahike.
# One binary, no server. Linux, macOS and Windows: github.com/replikativ/datahike/releases
unzip datahike-<version>-linux-amd64.zip && chmod +x dthk
# A database on the filesystem, or point :store at an S3 bucket
cat > db.edn <<'EOF'
{:store {:backend :file :path "./mydb" :id #uuid "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}
:keep-history? true
:schema-flexibility :read}
EOF
./dthk create-database edn:db.edn
# Two shells writing at once are fenced: each commit lands on the head it read, or retries
./dthk transact conn:db.edn '[{:name "Alice" :role "engineer"}]'
./dthk transact conn:db.edn '[{:name "Bob" :role "design"}]'
./dthk query '[:find ?n :where [?e :role "engineer"] [?e :name ?n]]' db:db.edn
# A second db: argument is a second source. Here the same store on a branch;
# it could as easily be a colleague's config pointing at another bucket.
./dthk branch conn:db.edn :db :proposal
sed 's/:schema-flexibility/:branch :proposal :schema-flexibility/' db.edn > proposal.edn
./dthk transact conn:proposal.edn '[{:name "Carol" :role "engineer"}]'
./dthk query '[:find ?n :in $trunk $proposal
:where [$proposal ?e :name ?n] (not [$trunk ?e :name ?n])]' \
db:db.edn db:proposal.edn dthk is the whole database as a native binary: a filesystem or an S3 bucket is the only
infrastructure, and concurrent writers are serialised by the store itself (file locks on a host,
conditional writes on S3), so cron jobs, containers and Lambdas can share one database without a server.
Every release
ships it for Linux (amd64, arm64), macOS (Intel, Apple Silicon) and Windows (amd64), together with
libdatahike, the C library behind the Python and other native bindings, and a Babashka pod.
See the CLI docs.
# Datahike Server: HTTP, durable storage, and an optional PostgreSQL listener
docker volume create datahike-data
docker run --name datahike --detach \
--publish 4444:4444 \
--stop-timeout 40 \
--mount type=volume,source=datahike-data,target=/var/lib/datahike \
--env DATAHIKE_TOKEN='replace-with-a-long-random-token' \
ghcr.io/replikativ/datahike-server:latest
curl --fail --retry 30 --retry-connrefused --retry-delay 1 \
http://127.0.0.1:4444/health/live Datahike Server is the batteries-included network deployment: authenticated HTTP, a durable system catalogue, portable storage backends, and the beta PostgreSQL-compatible listener. Before exposing port 5432, configure the listener with separate users and TLS. The server deployment guide covers version-pinned images, secrets, storage, and PostgreSQL configuration.
Compatible readers can query shared storage directly, while Datahike Server gives network clients conventional APIs and centralises writes. Compare the deployment models →
;; deps.edn
{:deps {org.replikativ/datahike {:mvn/version "RELEASE"}}}
require('[datahike.api :as d])
;; The store id is the database's identity, the same wherever it is replicated.
def cfg: {:store {:backend :file, :path "./db",
:id #uuid "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}, :keep-history? true, :schema-flexibility :read}
d/create-database(cfg)
def conn: d/connect(cfg)
d/transact(conn,
[{:name "Alice", :role "engineer"} {:name "Bob", :role "design"}])
def before: @conn
d/transact(conn, [{:name "Carol", :role "engineer"}])
d/q('[:find ?n :where [?e :role "engineer"] [?e :name ?n]], @conn)
;; Two database values in one query: a past state, a branch, or a colleague's
;; database on another bucket all arrive the same way.
d/q('[:find ?n :in $now $before
:where [$now ?e :name ?n] not([$before ?e :name ?n])],
@conn, before);; deps.edn
{:deps {org.replikativ/datahike {:mvn/version "RELEASE"}}}
(require '[datahike.api :as d])
;; The store id is the database's identity, the same wherever it is replicated.
(def cfg {:store {:backend :file :path "./db"
:id #uuid "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}
:keep-history? true :schema-flexibility :read})
(d/create-database cfg)
(def conn (d/connect cfg))
(d/transact conn [{:name "Alice" :role "engineer"}
{:name "Bob" :role "design"}])
(def before @conn) ; a database value, kept
(d/transact conn [{:name "Carol" :role "engineer"}])
(d/q '[:find ?n :where [?e :role "engineer"] [?e :name ?n]] @conn)
;; Two database values in one query: a past state, a branch, or a colleague's
;; database on another bucket all arrive the same way.
(d/q '[:find ?n :in $now $before
:where [$now ?e :name ?n] (not [$before ?e :name ?n])]
@conn before)The reference implementation and the language in which the stack is written. A database value, a query, and a past state are all ordinary immutable values. JVM, browser, and native.
In production
Selected production uses of Datahike.
"Datahike is a foundational part of the stub story, going from a rough prototype all the way to finding product-market fit, generating revenue, and raising capital. It's been a critical part of our journey, and if I had to do this all again, you best believe I'd use Datahike again."
The Swedish Public Employment Service has used Datahike in production since 2024 for the JobTech Taxonomy. It contains more than 40,000 labour-market concepts used by thousands of caseworkers each day. Their evaluation also compares Datahike with Datomic.
Heidelberg University built emotrack on Datahike, a longitudinal emotion tracking application for psychological research, capturing and querying time-series self-report data across study participants.
Extend beyond the database
Start with Datahike for relational data and history. The wider ecosystem brings snapshots and branching to analytical queries, vector search and full-text search.
Datahike
Datalog with history
Keep transaction history, query past database values and create branches without copying the full database.
- Clojure, Java, JS, Python, C/C++, CLI, HTTP
- Readers connect directly to storage, no server required
- Pluggable storage: filesystem, S3, JDBC
- History-aware excision and garbage collection
Stratum
SQL that branches
Columnar SQL for the JVM, with copy-on-write tables and a published comparison against DuckDB.
- PostgreSQL wire protocol
- Full DML, window functions, CTEs
- CoW snapshots and time-travel
Proximum
Versioned vector search
Pure-JVM HNSW search with immutable snapshots, branches and content-addressed commits.
- Spring AI & LangChain4j integrations
- Merkle-verified index snapshots
- No native dependencies
Scriptum
Branchable Lucene indexes
Git-like branching for Apache Lucene. Fork a 100 GB index in milliseconds via segment sharing.
- Full Lucene 10.x: text, facets, KNN
- Forks copy metadata, not segment data
- Query any historical commit point
From database states to running applications
These projects apply snapshots and branching to reactive programs, agent workflows and collaborative applications.
Spindel
Compute with alternative states
Spindel runs reactive computations and can fork their execution context to explore alternatives. Its inference algorithms use forks to evaluate separate possibilities.
GitHub →Dvergr
Review an agent's proposed changes
Dvergr composes people, language models and scripts into agent workflows. Proposals can use separate code and database branches, with review before changes reach shared state.
GitHub →Simmis
Organizational memory and controlled change
Simmis uses Datahike as the versioned knowledge layer for work shared by people and AI agents, keeping proposed changes separate until they are adopted.
See Datahike in Simmis →Yggdrasil defines the shared snapshot and branching protocols used across the stack.
Notes
Worked examples, deployment choices and benchmarks from the Datahike ecosystem.
What comes after InstantDB?
Compare self-hosted Instant with Datahike's thin, replicated and embedded clients, including permissions, optimistic writes, history and operating costs.
Serialization considered boring
Why Datahike's serializer is plain CBOR: a standard format at the speed of the fastest private ones, readers in other languages, and reads into stored values without decoding them.
Data governance in versioned systems
How purge, garbage collection and access control work across Datahike, including the storage layers they do not cover.
Branches as values, merges as queries
How Datahike creates branches with a small set of konserve writes and uses multi-source Datalog to compare and merge them.
Datahike speaks PostgreSQL
How the pg-datahike beta exposes Datahike through the PostgreSQL wire protocol for psql, migrations and supported ORM workflows.
Anomaly detection inside the database
Why Stratum runs isolation forests inside its SQL engine, what that removes from a pipeline, and where the current implementation fits.
Work with us
If you need help getting Datahike into production, we can help with integration, custom development, and support contracts.