Open-source project
replikativ/datahike avatar
replikativ/datahike

Datahike: a Datalog database where the database version is a value

Versioned, fast, distributed Datalog engine for everyone.

1,881 stars117 forksClojureEPL-1.0

At a glance

What is it?
Datahike is an EPL-1.0 Clojure database that keeps Datomic-compatible APIs, appends every transaction to history, and lets a database snapshot be held and queried like any other value. The judgement is that the immutable snapshot model is the real contribution, and that the distributed index space is the part that promises scale the implementation may not yet fully deliver.
Who is it for?
Datahike fits a team that wants query language over relationships without writing joins, that needs history and audit by default rather than as a feature, and that is prepared to work in Clojure or through one of the beta language bindings. It does not fit a team that wants a conventional relational database with a familiar driver, and it does not fit a project that needs guaranteed write latency on a single node.
Can I use it commercially?
Yes, with conditions. EPL-1.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Clojure, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • replikativ/datahike1,881 ★ · +1
Star history
Repository2026-10-162026-10-24
replikativ/datahike1.9k1.9k

1 Stars in the last 8 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A database snapshot is a value, not a connection

Datahike is described as a durable Datalog database with Datomic-compatible APIs and git-like semantics, built on persistent data structures and structural sharing.

The sentence that carries the design is the one about snapshots. Database snapshots are immutable values that can be held, shared, and queried anywhere, without locks or copying. That is a different mental model from the one most application developers have, where a database is a service you connect to and a query is a request you send.

The rest of the feature list follows from that choice. There is a distributed index space for read scaling without database connections, because readers access persistent indices directly. There is real-time sync with WebSocket streaming using Kabel for browser to server. There is time travel, since you can query any historical state with a full transaction audit trail. And there is GDPR-ready data purging, complete data excision for regulatory compliance.

The argument for Datalog itself is made in the README in terms of how applications change. Modern applications model increasingly complex relationships, social networks, organisational hierarchies, supply chains and knowledge graphs, and traditional SQL forces you to express graph queries through explicit joins, accumulating complexity as relationships grow. Datalog uses pattern matching over relationships, so you describe what you are looking for rather than how to join tables.

There is a second argument about time. Most value derives from how facts evolve over time, and the immutable design treats the database as an append-only log of facts. That is what makes the audit trail, the time-travel debugging and the GDPR excision the same mechanism rather than three separate features.

The project describes itself as production-proven, tested with billions of datoms and deployed in government services, and it is part of the replikativ ecosystem for decentralised data architectures.

The API is Datomic, and the compatibility is a subset

The usage example in the README is the whole mental model, and it is short enough to follow end to end.

clojure
(require '[datahike.api :as d])


;; use the filesystem as storage medium
(def cfg {:store {:backend :file 
                  :id #uuid "550e8400-e29b-41d4-a716-446655440000"
                  :path "/tmp/example"}})

;; create a database at this place, per default configuration we enforce a strict
;; schema and keep all historical data
(d/create-database cfg)

(def conn (d/connect cfg))

The configuration is a map with a `:store` describing the backend, an `:id` and a `:path`. The comment about the default configuration is worth noting: by default the system enforces a strict schema and keeps all historical data. Both of those are the kind of default you either want or need to change.

The first transaction is the schema, and the README notes you can also add this within database creation by adding `:initial-tx` to the configuration. The schema uses Datomic's attribute vocabulary, `:db/ident`, `:db/valueType` and `:db/cardinality`, and the example declares a `:name` string with `:db.cardinality/one` and an `:age` long with the same cardinality.

Data goes in with `d/transact` and a vector of maps, and the query is `d/q` with a query vector and a database value:

clojure
(d/q '[:find ?e ?n ?a
       :where
       [?e :name ?n]
       [?e :age ?a]]
  @conn)
;; => #{[3 "Alice" 20] [4 "Bob" 30] [5 "Charlie" 40]}

Note the `@conn`. The connection is dereferenced to get the current database value, and that value is what you query. This is the immutability model showing through in ordinary code. You can also pass a query as a hash map with `:query` and `:args`, which the README points at as the way to work with queries in the style used in a specific blog post about Datomic query syntax.

The compatibility claim has a limit, and the README states it: the API namespace provides compatibility to a subset of Datomic functionality and should work as a drop-in replacement on the JVM. A subset, and on the JVM. The rest of Datahike will be ported to core.async to coordinate IO in a platform-neutral manner, which is a statement about work in progress rather than a description of current behaviour.

History is a query argument, not a separate tool

The time-travel feature is the one that best justifies the immutable design, and the example is a one-liner.

clojure
(d/q '[:find ?a
       :where
       [?e :name "Alice"]
       [?e :age ?a]]
  (d/history @conn))
;; => #{[20] [25]}

The only difference from the ordinary query is the database argument. `(d/history @conn)` returns the history of the current database, and querying it finds both values Alice's age has ever had, 20 and 25.

This is where the "git-like semantics" description earns its place. A branch database, a commit, a diff, a history traversal and a checkout of an old state are all operations on an append-only log of facts, and the README's headline phrase is "Branch databases, not just code".

The GDPR story comes from the same place. The documentation links a data purging section under time variance, and the claim is complete data excision for regulatory compliance. With an append-only log, deleting data is not a `DELETE` statement, it is a transaction that marks the fact as retracted and a mechanism that physically removes it from the storage.

The limits are real, though, and they follow from the architecture. Every transaction is retained by default, so a database that accumulates history is also a database that grows, and the storage cost of a long-lived system is not the same as that of a mutable one. Time travel also means the whole history is in scope for the query planner's cost model, and a query that does not need history still has to be evaluated against a structure that contains it.

The other thing worth noting is the transaction semantics visible in the example. A transaction with a missing attribute is accepted, and the query afterwards returns the entities that have the attribute, so partial data is a normal state rather than an error. The README's own example includes `{:age 15 }` with no name, and the result set simply omits it.

Storage backends and language bindings, both wide

Two breadth claims in the feature list need unpacking, because they are the ones most likely to change your adoption decision.

The storage story is genuinely wide. The flexible storage option lists file, LMDB, S3, JDBC, Redis and IndexedDB via konserve. The JVM starting point is the file backend, as the usage example shows, and the rest are alternatives behind the same `:store` configuration key.

The cross-platform claim is equally wide and equally qualified. The documentation lists JVM, Node.js and browser, with Clojure, ClojureScript, JavaScript and Java APIs, and the language bindings section is marked beta. The Quick links in the README point at a bindings page for Java, JavaScript, Python, CLI and more, with beta in the anchor text.

The repository layout backs up how many of these are real rather than aspirational. There is `libdatahike/`, the core library. `src-datomic/` is a separate source tree for the Datomic compatibility layer, with `test-datomic/` and `tests-datomic.edn` alongside it, so the compatibility work is tested separately from the rest. `src-hitchhiker-tree/` is a tree structure library, `src-kabel/` is the Kabel WebSocket library, `src-secondary/` handles secondary indexes, and `src/` is the main tree.

Then there are the client and server implementations: `http-server/` and `http-server-java/`, `ts-client/` for a TypeScript client, `npm-package/` for the npm distribution, and `pydatahike/` for Python. There is a `java/` directory for the Java API, `bb/` with a `bb.edn` for Babashka, `externs/` for the ClojureScript externs, and `shadow-cljs.edn` plus `karma.conf.js` for the browser test setup. The `benchmark/` directory, `docker/`, `examples/` with `basic/` and `java/` subdirectories, and `flake.nix` complete the picture.

The `package.json` at the root is for the browser tests, named `datahike-browser-tests`, with Karma and shadow-cljs as the runners, and a script that regenerates the TypeScript client from the Clojure code generator and type-checks it.

Where the distributed promise is thinnest

The distributed index space is the most interesting capability and the one the documentation points at least concretely, so it is worth separating the claim from the mechanism.

The distributed index space is described as read scaling without database connections, where readers access persistent indices directly. The logic is consistent with the immutability model. If a snapshot is a value with structural sharing, then a reader can hold the index itself and query it without talking to a writer, which removes a connection from the read path and lets reads scale by giving each reader its own copy of the shared structure.

What the README does not document is the coordination side. There is no description in the visible material of how a distributed index is kept current, what happens when a reader's snapshot is stale relative to a new transaction, or how a reader obtains a newer index than the one it holds. The `doc/distributed.md` page is linked, so the detail is presumably there, but the README's own summary is a capability claim rather than a mechanism.

That gap matters when you compare the options. The alternatives to a distributed index space are familiar: keep a single writer and scale reads with replicas, which is the standard relational answer and adds replication lag, or query a local snapshot and accept staleness, which the immutable model makes cheap. Both are well understood. The distributed index space is a third option with a potentially better read profile and an unfamiliar failure model.

The real-time sync feature sits alongside it, with WebSocket streaming using Kabel for browser to server communication. That is the mechanism by which a browser client would learn about new transactions, and it is the piece that would have to carry the coherence guarantees. A browser client that receives a stream of transactions and applies them locally can build its own current database value, and then the distributed index space becomes a matter of shipping indices rather than a query service.

For an adopter, the honest position is that the immutability and history parts of this project are concrete and well demonstrated by the API examples, while the distributed parts need to be evaluated against the linked documentation rather than the README summary.

Project state and how the versions are numbered

The release numbering is unusual and worth understanding, because it tells you how the project is versioning a moving target.

The three listed releases are 0.8.1903 on 2026-09-30, 0.8.1902 on 2026-09-30 and 0.8.1901 on 2026-09-29. So the pattern is a fixed 0.8 prefix with a four-digit build counter, and two of the three were published on the same day, an hour or so apart. That is a continuous delivery scheme, where the build number is the identifier rather than a semver version, which suits a project whose API is explicitly still moving.

The README says so directly. It provides a stable API for the JVM, and extends it by first providing experimental and beta features that then get merged into the API over time. So the API is versioned by declaration rather than by the build number, and a consumer on the JVM has a promise the consumers of the other language bindings do not have.

The last push was on 2026-10-16 and the repository is not archived, so the code is being worked on continuously. The homepage is datahike.io, the API documentation is on cljdoc, and there are articles on the company's blog at datahike.io/notes.

The community infrastructure is a Slack channel on the Clojurians workspace, a CircleCI badge, and Clojars for the artifact `org.replikativ/datahike`. There is also a CHANGELOG.md, a `deps.edn` and a `build.clj` for the two build paths, a `config.edn`, and a `bb.edn` for the Babashka tasks.

The licence is EPL-1.0, recorded in the `LICENSE` file at the root and in the `package.json` metadata. That is a file-level copyleft licence, which is a reasonable fit for a library intended to be embedded in applications without the copyleft reaching into the host code, though it is a different choice from the AGPL and the MIT that most of the ecosystem uses, and it is worth reading the EPL text if you are shipping it in a closed-source product.

Editorial conclusion

Datahike fits a team that wants query language over relationships without writing joins, that needs history and audit by default rather than as a feature, and that is prepared to work in Clojure or through one of the beta language bindings. It does not fit a team that wants a conventional relational database with a familiar driver, and it does not fit a project that needs guaranteed write latency on a single node. Verify first that the storage backend you want is one of the documented ones, since the list spans file, LMDB, S3, JDBC, Redis and IndexedDB through konserve, that the language binding you plan to use is still labelled beta, and that the Datomic subset the API namespace covers is enough for your query shapes, because the compatibility claim is explicitly a subset.

Frequently asked questions

What is Datahike and what does it replace?

Datahike is a durable Datalog database with Datomic-compatible APIs and git-like semantics, built on persistent data structures and structural sharing. It replaces a conventional SQL database when the application models complex relationships, since Datalog uses pattern matching over relationships rather than explicit joins.

How do I set up a Datahike database in Clojure?

Require `org.replikativ/datahike` from Clojars, then create a database with a configuration map naming the storage backend, for example `:file` with a `:path`, call `d/create-database` and then `d/connect`, and use `d/transact` for both the schema transaction and the data, and `d/q` to query the dereferenced connection.

How do I query historical data with Datahike?

You pass a different database value to the same query. `(d/history @conn)` returns the history of the current database, and querying it returns every value a fact has ever had, for example both 20 and 25 for a person's age. Time travel, the audit trail and GDPR data purging all come from the same append-only design.

Which storage backends does Datahike support?

The documented list is file, LMDB, S3, JDBC, Redis and IndexedDB via konserve. The configuration uses a `:store` key with a `:backend`, and the README's own example uses the file backend with a `:path`.

Is the Datomic compatibility in Datahike complete?

No. The API namespace provides compatibility to a subset of Datomic functionality and should work as a drop-in replacement on the JVM. The rest of Datahike is being ported to core.async to coordinate IO in a platform-neutral manner, and the non-JVM language bindings are marked beta.

Official sources

  1. License: EPL-1.0
  2. Project website
  3. README
  4. Releases
  5. replikativ/datahike on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/replikativ-datahike.svg)](https://hysenlabs.com/projects/replikativ-datahike)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/replikativ-datahike.svg)](https://hysenlabs.com/projects/replikativ-datahike)

Was this useful?