OrbitDB vs GenosDB: Built on IPFS, or Built on Nothing at All
What OrbitDB's IPFS foundation costs in practice, what its open issues say about the gaps it left, and how a database designed without that dependency handles the same problems.

Full Stack Developer - dWEB R&D
OrbitDB did something important: it showed that a serverless, peer-to-peer database in the browser was not a thought experiment. It has been around since 2015, it is built on IPFS and libp2p, it uses Merkle-CRDTs for conflict-free merges, and a lot of what the decentralized web takes for granted today was proven there first.
It is also the clearest example of a design decision that defines everything downstream: OrbitDB is a database built on top of IPFS. Not one that can use IPFS — one that cannot exist without it. Storage, replication, addressing and identity all run through that layer.
This article looks at what that choice costs in practice, what OrbitDB's own open issues say about the gaps it left, and how a database designed without that dependency handles the same problems.
What Is OrbitDB?
From their README:
"OrbitDB is a serverless, distributed, peer-to-peer database. OrbitDB uses IPFS as its data storage and Libp2p Pubsub to automatically sync databases with peers."
It offers several database types (events, documents, keyvalue), an append-only log as the underlying model, and pluggable access controllers and encryption modules. If your project already lives inside the IPFS ecosystem — content addressing, CIDs, pinning services — OrbitDB fits naturally into it, and nothing below changes that.
The Cost of Standing on IPFS
Here is the official example from OrbitDB's own documentation for opening a database:
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB } from '@orbitdb/core'
import { LevelBlockstore } from 'blockstore-level'
import { Libp2pOptions } from './config/libp2p.js'
const blockstore = new LevelBlockstore('./ipfs')
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p, blockstore })
const orbitdb = await createOrbitDB({ ipfs })
const db = await orbitdb.open('my-db')
Five imports, a blockstore, a libp2p configuration file and a Helia node — before a single record is written. That is not boilerplate that a tutorial skipped; it is the architecture. And it has a weight:
| Package | Unpacked | Direct dependencies |
|---|---|---|
@orbitdb/core |
2,577 KB | 10 |
helia (required) |
5,998 KB | 31 |
libp2p (required) |
2,768 KB | 27 |
| Total | ≈ 11.3 MB | 68 |
genosdb |
2,007 KB | 0 |
Resolved transitively, @orbitdb/core pulls 53 packages into a project. GenosDB pulls none — not a small tree, zero — so there is no transitive surface to audit and nothing upstream that can go unmaintained on your behalf.
In GenosDB the equivalent of everything above is one line:
const db = await gdb('my-db', { rtc: true })
Storage is OPFS in the browser. Peer discovery runs over the Nostr relay network, which already exists and never sees your data. Transport is WebRTC through GenosRTC. There is no node to boot, no blockstore to configure and no pubsub layer to tune.
What OrbitDB's Open Issues Tell You
The most honest way to assess a project is to read what its users asked for and never got. These are all open issues in the OrbitDB repository, with the dates they were filed:
"Query standard" — open since August 2018
OrbitDB has no query language. You iterate the log and filter in JavaScript. The request for a standard has sat open, with zero replies, for eight years.
GenosDB ships a query engine built into map(): \(eq \)ne \(gt \)gte \(lt \)lte \(in \)between \(exists \)startsWith \(endsWith \)contains \(text \)like \(regex \)not \(and \)or \(edge \)near, with sorting, cursor-based pagination and $edge for recursive graph traversal. Every query can be made reactive by passing a callback — it re-fires as peers write.
"Lamport clock can be set far into future making db progress halt" — open since November 2018
A peer with a wrong clock can push the Lamport counter so far ahead that the database stops making progress. Eight years open, seven comments, unresolved.
This is precisely the failure mode Hybrid Logical Clocks exist to prevent. GenosDB blends physical time with a logical counter, so causality is preserved without trusting any device's wall clock, and a misconfigured peer cannot stall everyone else.
"Ordering tie breaker can cause undefined ordering" — open since November 2018
Two entries with the same identity and timestamp can order unpredictably. Filed in 2018, zero comments since.
GenosDB defines a deterministic tie order, and for lists that users reorder by hand it uses fractional order keys, so inserting, moving and pasting in a shared list converges to the same result on every peer.
"Database Encryption & Read Protection" — open since October 2021, 15 comments
OrbitDB does support encryption, through a modular interface where you supply the module. The one the project maintains carries this warning in OrbitDB's own documentation:
"WARNING: SimpleEncryption is an unaudited encryption module. Use at your own risk."
In GenosDB, encryption is not a module you source and vet. The Security Manager is part of the database: WebAuthn-based identity, signing on every operation, and end-to-end encryption where each record carries its own wrapped key. Reading an encrypted record costs 1.2 ms; the conventional approach of deriving a PBKDF2 key per record costs around 10.9 ms, which over a 40-record list is the difference between ~50 ms and nearly two seconds.
"Typescript implementation" — open since May 2025, 19 comments
The most-discussed open issue in the repository.
"Database record limits for acceptable lookup performance" — open since February 2024
Users asking where the practical ceiling is. Still open.
Access Control: Write Lists, Not Roles
OrbitDB's access controllers, in their own words, "define the write access a user has to a database". You get two: an immutable one stored on IPFS and a mutable one backed by an OrbitDB keyvalue store. Both answer a single question — is this identity allowed to write to this database?
That leaves three things to you: read protection, granularity below the database level, and any notion of roles.
GenosDB answers all three inside the client. Role-based access control with a real hierarchy — guest, user, manager, admin, superadmin — ACLs at the level of individual nodes, and rule-based governance that promotes and demotes users automatically according to signed rules. Confidentiality is cryptographic rather than topological: a peer can relay data it is not allowed to read.
Availability Without a Pinning Service
Because OrbitDB stores data as IPFS blocks, a record survives only while some peer still holds that block. Pinning — a paid service or an always-on node — is therefore a durability requirement, not a convenience: stop pinning and the data can genuinely go away.
GenosDB inverts the relationship. Every peer keeps a full local replica in OPFS, so the data already exists, complete, on every device that has ever opened the app. Reading it needs no network at all, and nothing is lost when peers disconnect — there is no block to keep alive somewhere else. A device that comes back pulls only what it missed: rejoining a 210-item space costs 7.9 KB, where the same reconciliation without deltas costs 83 KB, thanks to the Hybrid Delta Protocol.
The optional always-on peer solves a different problem — not keeping data alive, but greeting a brand-new device that arrives when nobody else happens to be online. It is a peer you may choose to run, not infrastructure the design leans on.
The Same App, Both Ways
A shared list that syncs live between browsers. This is OrbitDB's own getting-started example, unedited:
import { createLibp2p } from 'libp2p'
import { createHelia } from 'helia'
import { createOrbitDB } from '@orbitdb/core'
import { LevelBlockstore } from 'blockstore-level'
import { Libp2pOptions } from './config/libp2p.js'
const blockstore = new LevelBlockstore('./ipfs/blocks')
const libp2p = await createLibp2p(Libp2pOptions)
const ipfs = await createHelia({ libp2p, blockstore })
const orbitdb = await createOrbitDB({ ipfs })
const db = await orbitdb.open('my-db')
await db.add('hello world 1')
console.log(await db.all())
await db.close()
await orbitdb.stop()
await ipfs.stop()
Note what all() is: a point-in-time read. To know when a peer writes something you subscribe to database events separately, and to find records matching a condition you load everything and filter in JavaScript — there is no query layer to push that down to.
The same thing in GenosDB:
import { gdb } from 'genosdb'
const db = await gdb('my-db', { rtc: true })
await db.put({ text: 'hello world 1', at: Date.now() })
// live: the callback re-fires as peers write
db.map({ order: 'desc' }, ({ id, value, action }) => render(id, value, action))
And when the list is not "everything", the query goes to the engine rather than to a .filter():
db.map({
query: { status: 'open', priority: { \(gte: 3 }, title: { \)text: 'sync' } },
order: 'desc',
$limit: 20
}, onRow)
There is no libp2p configuration file because there is no libp2p, no blockstore because storage is OPFS, and no ipfs.stop() because there is no node to stop.
Side by Side
| OrbitDB | GenosDB | |
|---|---|---|
| Foundation | IPFS + libp2p, required | None — runs standalone |
| Install weight | ≈ 11.3 MB, 53 transitive packages | 2 MB, zero dependencies |
| Setup | blockstore + libp2p + Helia + OrbitDB | await gdb(name, { rtc: true }) |
| Data model | Append-only log (events, docs, keyvalue) | Graph with relations and traversal |
| Queries | None — iterate and filter in JS | Full operator set, reactive |
| Conflict resolution | Merkle-CRDTs + Lamport clocks | Hybrid Logical Clocks |
| Encryption | Modular; official module is unaudited | Built in, per-record wrapped keys |
| Access control | Write lists per database | RBAC + node-level ACLs + governance |
| Identity | Keypair | WebAuthn or mnemonic, signed operations |
| Availability | Survives only while a peer pins the block | Full replica on every device, always |
| TypeScript | Open issue since 2025 | Typed |
What This Looks Like Running
Everything below runs in the browser with nothing deployed behind it — no IPFS node, no pinning service, no backend.
Collaborative editing, no CRDT library:

dCode — a code editor with branches, commits and pull requests, peer-to-peer:

A Hacker News clone where moderation is a signed constitution instead of an admin panel:

A collaborative whiteboard in a single file:

When OrbitDB Is the Right Choice
- You are already on IPFS. If your data is content-addressed, if CIDs matter to your model, or if you are pinning through an existing service, OrbitDB is native to that world and GenosDB is not.
- You need a tamper-evident archive. OrbitDB's append-only Merkle log is content-addressed, so history cannot be quietly rewritten — valuable if auditability is the product. GenosDB signs every operation and keeps an oplog for synchronisation, but it is built to represent current state rather than to serve as a permanent ledger.
- You want a fully open-source stack. OrbitDB is MIT. GenosDB's production bundle is free for personal and commercial use, but the source is proprietary by deliberate governance choice — it is not open source, and describing it as such would be inaccurate.
- Content addressing is the feature. If deduplication and permanent addressing of blobs is what you are buying, that is IPFS's job and it does it well.
GenosDB is the better fit when the IPFS layer is a cost rather than a feature: when you want relational queries over a graph, roles and per-node permissions without a server, a two-megabyte dependency-free bundle, and an app that works with nothing deployed behind it.
A Note on Where This Comes From
I have spent years building browser P2P databases — I wrote the official GUN documentation platform and contributed to that codebase before starting GenosDB. Every design decision described here came from hitting these specific walls in real projects: queries I could not express, clocks I could not trust, permissions I could not enforce without a server, and bundles too heavy to justify.
None of this is a knock on OrbitDB's ambition. It asked the right question in 2015 and answered it with the tools available then. GenosDB is what happens when you ask the same question after OPFS, WebRTC, WebAuthn and Nostr exist, and conclude that the storage layer does not have to be someone else's network.
If anything here is out of date or wrong, tell me and I will correct it — GitHub Discussions is open and I answer.
⭐ Found this useful? Star GenosDB on GitHub — or spin it up in seconds: npm i genosdb.
This article is part of the official documentation of GenosDB (GDB). GenosDB is a distributed, modular, peer-to-peer graph database built with a Zero-Trust Security Model, created by Esteban Fuster Pozzi (estebanrfp).
📄 Whitepaper | overview of GenosDB design and architecture
🛠 Roadmap | planned features and future updates
💡 Examples | code snippets and usage demos
📖 Documentation | full reference guide
🔍 API Reference | detailed API methods
💬 GitHub Discussions | community questions and feedback
🗂 Repository | Minified production-ready files
📦 Install via npm | quick setup instructions



