put!
Writes unconditionally at the caller's key. Callers mint fresh, content-derived segment keys with segment-key , so identical content lands at the same address and republishing unchanged data is idempotent.
DaoJing is the storage boundary: a decentralized key-value store of opaque bytes, designed to hold immutable, content-addressed segments plus mutable stream-root references.
Jing (井) is the Chinese character for a well, the place where water is stored. The name is the design: a well holds whatever is poured into it and gives it back when drawn, without knowing or caring what the water is for. DaoJing is that well for bytes.
DaoJing is pure syntax. It holds form , never meaning . What any byte denotes, whether "datom," "view," or "query," is semantics an interpreter projects onto it, never something storage knows. Concretely it is a dumb key-value store, the decentralized analog to Datomic's storage layer.
A store is defined by what it holds, not the pipe data arrived through. Datoms enter through DaoStream member logs, exactly as a transaction log feeds Datomic's storage, but the streams are upstream of the store, not its identity.
Syntax : storage holds bytes; it never knows what a datom is.
Segments : immutable, content-addressed chunks written once under fresh keys.
Roots : mutable references advanced under optimistic concurrency.
The physical store is deliberately dumb. It does not pattern-match and it does not run Datalog. Those belong to DaoSpace , the embeddable reader above it. This is the Datomic discipline taken literally: storage is a dumb KV blob store; all intelligence lives in the reader on top.
The IKVStore protocol is the single interface a writer publishes bytes through and a reader reads them back from. It does not interpret what passes through it. This is a storage API both sides share, not a query API. The interface is inspired by Datomic Storage: the same handful of dumb key-value operations Datomic asks of its pluggable storage services, with get deliberately mirroring both clojure.core/get and Datomic's KVStore get.
;; The dumb storage API (src/cljc/dao/jing.cljc)
(defprotocol IKVStore
(put! [this k v-map]
"Write an entry unconditionally. Used for writing new
immutable segments (which never overwrite).")
(cas! [this k old-rev v-map]
"Compare-And-Swap an entry. Only writes v-map if the current
revision matches old-rev. Used for updating mutable
references like the stream root pointer.")
(get [this k not-found]
"Read an entry by key. Returns the v-map (which includes its
:rev), or not-found if the key is absent.")
(delete! [this k]
"Remove an entry by key.")
(close! [this]
"Release the storage backend resources."))put!Writes unconditionally at the caller's key. Callers mint fresh, content-derived segment keys with segment-key , so identical content lands at the same address and republishing unchanged data is idempotent.
cas!Advances a mutable root reference under optimistic concurrency: the write lands only if the current :rev matches, and a false return means another writer won. Roots are how readers find the current state of a stream's published segments.
getResolves a key to its stored value, including its :rev , or the caller's not-found value when the key is absent. Distributed backends may throw when a root's authority is unreachable rather than pass off a freshness failure as absence.
Immutability and content addressing are a discipline callers observe over the IKVStore contract, not a guarantee the protocol enforces: put! will overwrite any key with anything. The keyspace itself is classed by the key alone, :segment/sha256- or :root/ , and the layers above keep the two disjoint.
DaoJing itself owns the key-minting mechanism: canonicalize a value so equal values print identical bytes, hash the canonical print, and mint a segment key from the hash.
;; A segment's key is derived from its content
(jing/segment-key v-map)
;; => :segment/sha256-
(jing/key-class :segment/sha256-abc) ;; => :segment
(jing/key-class :root/my-stream) ;; => :root Content addressing is a property of what an immutable segment is , independent of whether that segment is ever exposed to a peer. A single-process in-memory store still benefits: identical content deduplicates for free, with no coordination, and a segment's identity is stable before it is ever replicated.
That is why a store can be swapped from memory to file to DHT without re-keying anything. The keys were never a networking artifact to begin with. The network backend merely goes on to rely on them, using hash-verified fetch against untrusted peers.
PostgreSQL couples its storage engine to its data structures because that coupling makes it fast. The price is vacuum, WAL record types per structure, a lock manager, and a storage format that admits new structures only through years of C.
DaoJing is built on the opposite bet: in-place readability is a property of the byte layout, not of the storage engine. A flat, self-describing layout can be traversed in place by the reader while the store remains a dumb map of opaque blobs. Structural awareness is one way to obtain format stability; it is the expensive way, because it welds the layout to the engine.
In-place updates, fine-grained retrieval, and storage-level MVCC. Updates are naturally out-of-place: new segments are written, old ones are orphaned.
Invent a new data structure without touching storage code. Swap a local disk for a peer-to-peer network without touching query code.
Strip structural awareness and the storage layer becomes a hash table of blobs, simple enough to own rather than delegate.
A store that never knew your structures never needs to learn new ones.
Because the boundary is thin, backends are interchangeable realizations of the same contract. DaoJing is implemented as cross-platform .cljc : the in-memory and file backends operate identically across Clojure, ClojureScript, and ClojureDart, while the remote client is Clojure-only today.
An in-process map. The reference backend for tests and single-process use.
A Bitcask-style append-only log with compaction: sweep the live keyset, rewrite live values to a new log, and atomically swap the file beneath the interface.
A UDP Kademlia peer grid. Segments are hash-verified against their keys and cacheable anywhere; roots are never cached, and their reads and writes are forwarded to the root's owner peer.
A local store exposed over the network: every IKVStore method becomes an RPC call over a DaoStream WebSocket transport.
The protocol is so primitive that it behaves almost exactly like a hardware block device, and proven hardware patterns map directly onto it: out-of-place writes with garbage collection mirror SSD flash translation, per-writer logs give NVMe-style zero-contention queues, and caching, mirroring, and erasure-coding stores compose as decorators with zero changes to the query logic above.
DaoJing is the meeting point of two traditions, one for what it holds and one for what it is built from.
The storage discipline: a dumb KV store of immutable segments under a strict Transactor / Storage / Query separation, with content-addressed identity and the Peer-as-library read model. DaoJing is the decentralized Storage; the reader library above it is the Peer.
The substrate, one level down in DaoStream: the member logs the store is fed through are independent, location-transparent, append-only streams. The store inherits this only because its intake is streams.
The associative-matching, generative-communication behavior built on top of these bytes is not here. That lineage, Linda, belongs to DaoSpace , the reader above this boundary.
See how datoms arrive through DaoStream member logs, and how DaoSpace projects indexes and queries onto the bytes DaoJing holds.