p2p-db.git
ird-p2p-db — an encrypted, append-only, peer-to-peer database for ClojureScript on Node.js (22+) and in the browser, built on Helia + OrbitDB and designed to pair with p2p-cljs: rooms are ephemeral messaging, this is the shared history that survives churn. Every holder of the secret appends sealed records and replicates them peer to peer on a private libp2p swarm — no server, no account. core.async channels end to end.
Use as a dependency
No registry — a source library served straight from this domain, consumed
as a git dependency (with shadow-cljs
reading deps.edn); ird-p2p-cljs comes along
transitively:
;; deps.edn
{:deps {ro.ird/ird-p2p-db {:git/url "https://ird.cod-sursa.ro/p2p-db.git"
:git/tag "v0.2.1" :git/sha "0000000"}
thheller/shadow-cljs {:mvn/version "3.5.0"}}}
The latest release is v0.2.1
(0000000). To pin that exact commit:
ro.ird/ird-p2p-db {:git/url "https://ird.cod-sursa.ro/p2p-db.git"
:git/sha "0000000000000000000000000000000000000000"}
Add the Helia/OrbitDB stack to your package.json dependencies
at the exact versions the suite proves — helia 7.1.12,
@orbitdb/core 4.0.0, @libp2p/gossipsub 17.1.1 and the
libp2p 3.3.x pieces listed in the repo's README.md; ClojureScript
and core.async come from deps.edn. These coordinates are filled
from releases.json, which scripts and agents can
read directly (head, tag, tag_sha per repo).
Quickstart
(require '[cljs.core.async :refer [go
Every async call returns a core.async channel delivering one value — the
result or an ex-info error. Records are sealed with AES-256-GCM
under a key derived from the secret before OrbitDB signs, hashes and syncs
them; write access is the secret itself (a shared OrbitDB identity derived
from it).
The swarm reaches nothing public — no bootstrap list, no DHT, no gateways —
and reachability is yours to arrange. Node peers meet over tcp or websockets
by explicit dial (:dial at open, db/dial later). A
browser can dial but never listen, so browser peers, and anything behind a
NAT, come in through a circuit relay v2 hop you run: a Node peer opened with
:relay-hop? true on a websockets address browsers can reach
(wss for https pages), named in :relay
by every peer that reserves a slot on it and so becomes reachable at
<relay>/p2p-circuit/p2p/<its id>. The relay is
configuration you supply, exactly like :dial — never a built-in
address, no relay discovery, no NAT hole punching, and the ird rooms relay is
not one; it forwards the noise-encrypted stream and terminates nothing.
:mdns? true (opt-in, Node only) is LAN discovery that actually
connects: the peer announces itself and queries under the service tag
_ird-p2p-db._udp.local — not libp2p's default, so only peers of
this library answer or are dialed — and dials every peer that answers, again
whenever one answers while not connected (a restarted peer comes back by
itself). mDNS announces the listen addresses, so peers on different machines
must :listen on a LAN address; the loopback default only lets
peers of one machine meet. Every such peer on the LAN is dialed, whatever
database it holds — a connection, not access.
:gater is opt-in inbound connection gating: a function of the
connecting peer's multiaddr string (it always ends in
/p2p/<its id>; <relay>/p2p-circuit/p2p/<id>
when relayed) returning true to let it in — false,
nil or a throw refuses, and a refused peer gets no connection at
all. Without it every inbound connection is accepted. Outbound dials are
never gated, and gating is about connections, not identity: whoever you let
in (or dial) that holds the secret reads and writes everything.
Node 22+, and engines stays node >=22: Node
18–21 was investigated and is blocked upstream (@orbitdb/core
4.0.0 declares it, and the libp2p 3 line under it needs Node 22 whatever it
declares) — the README's Versions section records the evidence. The exact key
schedule and AAD, what peers and the network can see, the limits (not a blob
store, ordering, persistence), browser peers and the proven version set are
in the repo's README.md.
Clone
git clone https://ird.cod-sursa.ro/p2p-db.git
browse the source in your browser →
What's inside
src/ird/p2p/db/core.cljs— the public API: open, append, entries, entries-stream, peers-stream, dial, closesrc/ird/p2p/db/crypto.cljs— the key schedule (HKDF labels), sealed records, the opaque manifest namesrc/ird/p2p/db/identity.cljs— the shared OrbitDB identity: two Ed25519 keys derived from the secretsrc/ird/p2p/db/errors.cljs— the error valuessrc/ird/p2p/db/impl/— the only namespaces with JavaScript interop (Helia on a private swarm — the Node or the browser variant — and OrbitDB)browser/vendor-entry.mjs— the esbuild entry that bundles the ESM-only stack for a browser buildtest/— key schedule and records, identity, the zero-interop gate, two real nodes replicating over loopback,:mdns?peers meeting by discovery alone,:gaterletting one peer in and refusing another, a bounded 600-record soak with a restart, and a headless-Chrome browser peer replicating with a Node peer through a relay
License
MIT.