Releases: aiassistsecure/nedb
Release list
NEDB v2.4.468 — Tri-Distribution Release
Target: master / main · © Interchained LLC × Claude
One engine, three products, one version — live across npm, PyPI, and crates.io with full macOS + Linux + Windows native addons.
TL;DR
NEDB now ships as three version-aligned distributions built from a single repo and published on a single tag:
| Product | What it is | npm |
pip |
cargo |
|---|---|---|---|---|
| nedb-engine | The flagship — the complete engine | nedb-engine |
nedb-engine |
nedb-engine |
| crypto-database | Verifiable v2/v3 — content-addressed Merkle DAG, AS OF / TRACE time-travel, BLAKE2b tamper-evidence |
crypto-database |
cryptodb |
crypto-database |
| aof-db | Fast / lightweight — append-only op-log, minimal footprint | aof-db |
aof-db |
aof-db |
All three are at 2.4.468 on every registry. crypto-database publishes its Python wheel as cryptodb (the crypto-database name is held by a third party on PyPI).
Install
# npm (native addon: macOS arm64/x64 · Linux x64 · Windows x64)
npm install nedb-engine # npm install crypto-database # npm install aof-db
# PyPI
pip install nedb-engine # pip install cryptodb # pip install aof-db
# crates.io
cargo add nedb-engine # cargo add crypto-database # cargo add aof-dbWhat's new in this line
- Three real, independently-built distributions. Not relabels — each ships its own native
.node, wheel, and crate under its own name, built from forks submoduled underdistributions/. The centralcodemagic.yamlbuilds all six macOS wheels andrelease-distros.ymlpublishes the distros; the forks carry no CI of their own. - Distro npm packages now bundle macOS native addons. The publish step waits for Codemagic's
<distro>.darwin-arm64/-x64.nodeand assembles them alongside the Linux/Windows builds beforenpm publish— socrypto-databaseandaof-dbinstall natively on all four platform targets, not just Linux/Windows. - One-command releases.
scripts/release.py "vFROM" "vTO"bumps the flagship and both forks, repoints submodules, opens + merges the release PRs, and tags — firing CI/CD across all three products. Idempotent and safe to re-run. - Version realignment. Everything resolves to 2.4.468 by default on every registry.
Verified live
npm nedb-engine 2.4.468 (latest) crypto-database 2.4.468 (latest) aof-db 2.4.468 (latest)
PyPI nedb-engine 2.4.468 cryptodb 2.4.468 aof-db 2.4.468
crates.io nedb-engine 2.4.468 crypto-database 2.4.468 aof-db 2.4.468
Distro npm tarballs confirmed to contain all four native addons, e.g. crypto-database@2.4.468:
crypto-database.darwin-arm64.node · crypto-database.darwin-x64.node · crypto-database.linux-x64-gnu.node · crypto-database.win32-x64-msvc.node (identical four-platform set for aof-db).
Under the hood (fixes that brought it green)
- Engine-version alignment. The engine crate (
rust/nedb-v2, packagenedb-engine) had been stranded at2.4.3while each distribution's wrapper crate required^2.4.x, so cargo couldn't resolve and the napi/Codemagic builds failed. Every version-bearing line — engine, wrappers, clients, and the maturin project — is now aligned. release-distrosstartup_failure. Threeif:conditions used double-quoted"refs/tags/"; GitHub Actions expressions require single quotes, so the workflow was valid YAML but an invalid workflow (no jobs, no logs). Restored to'refs/tags/'.- Codemagic distro paths. After the distribution rename, the macOS build steps were updated to
distributions/crypto-database/distributions/aof-db.
Releasing (for maintainers)
python3 scripts/release.py "vFROM" "vTO"
# e.g.
python3 scripts/release.py "v2.4.68" "v2.4.468"Both args require the leading v. The tool is idempotent — repos already at TO are skipped (no empty PRs), an existing tag is left in place, and it always proceeds through the remaining steps. It never force-pushes master and never commits to it directly. Requires GITHUB_TOKEN (repo + workflow).
Known follow-ups
- Per-distribution defaults. The three engines are currently identical; the next step is divergent defaults at the wrapper seam (
crypto-database→ verifiable v2/v3 on;aof-db→ fast append-only) so each behaves as its name promises with no flags. nedbd-v2server-binary naming. The bundled daemon binary is uploaded un-suffixed to the shared release; per-distro naming would prevent a distro shipping another product'snedbd-v2.- Release-tool pre-flight. Add an npm/PyPI/crates name-availability (and "too-similar") check to
scripts/release.pybefore a tag is spent.
NEDB v2.2.27 — Full Release Notes
Released: 2026-06-21
Packages:nedb-engine(PyPI) ·nedb-engine(npm) ·nedb-core-v2(crates.io)
What's in this release
v2.2.27 closes out a major shipping sprint. The headline: pip install nedb-engine now just works on every platform — Windows MSVC, Windows MSYS2/Git Bash, Linux, macOS — with the server binary and native extension bundled. If anything is missing, nedbd --doctor tells you exactly what to run to fix it.
Breaking changes
None. All existing databases, APIs, and client code are forward-compatible.
Highlights
Fat universal wheel — all platform binaries bundled
The py3-none-any wheel (the one pip installs when no platform wheel matches) now contains:
| File | Purpose |
|---|---|
nedb/nedbd-v2 |
Linux x86_64 DAG server binary |
nedb/nedbd-v2.exe |
Windows DAG server binary |
nedb/nedbd-v2-darwin-arm64 |
macOS Apple Silicon DAG server binary |
nedb/nedbd-v2-darwin-x64 |
macOS Intel DAG server binary |
nedb/_native.abi3.so |
Linux Rust extension (embedded NedbCore) |
nedb/_native.pyd |
Windows Rust extension (embedded NedbCore) |
nedb/_native.*darwin*arm64*.so |
macOS ARM Rust extension |
nedb/_native.*darwin*x86_64*.so |
macOS Intel Rust extension |
Previously only platform wheels contained these. Now even the fallback universal wheel is fully functional on all supported platforms.
nedbd --doctor — interactive environment diagnostics
New command that examines your exact environment and prints targeted, copy-paste-ready fix commands:
nedbd --doctor
Shows:
- Your Python executable path, version, pip path, site-packages location
- Whether
nedb._native(embedded Rust core) is available and why if not - Whether
nedbd-v2binary is found, and where it searched - Whether
cargois on PATH - A numbered fix plan with exact commands for your specific paths
On MSYS2/MinGW64, doctor explains the MSVC extension limitation and shows the HTTP mode workaround. On all other platforms it gives you the exact pip or cargo command to run.
nedbd-v2 on crates.io
cargo install nedb-core-v2Installs the DAG HTTP server binary (nedbd) from source on any platform with a Rust toolchain. Binary lands in ~/.cargo/bin/nedbd which nedbd --dag searches automatically.
test_the_will.py — HTTP mode via NEDB_URL
The durable DAG story test now runs on any platform — including MSYS2/MinGW where _native can't load:
# Terminal 1
nedbd --dag ./will-data
# Terminal 2
NEDB_URL=http://localhost:7070 python3 tests/test_the_will.pyThe HttpDb wrapper transparently maps all NedbCore methods to HTTP: put, link, neighbors, get (with time-travel AS OF), query (TRACE), verify, head, seq. 18/18 checks pass in both native and HTTP modes.
POST /v1/databases/{db}/neighbors endpoint
New endpoint on the Python HTTP server (AOF and DAG mode) for graph traversal:
POST /v1/databases/mydb/neighbors
{ "node": "person:robert", "rel": "parent_of" }
→ { "nodes": ["person:mark", "person:lisa"], "count": 2 }Returns the same format as NedbCore.neighbors() — no NQL workaround needed.
Bug fixes
caused_by inside doc was silently discarded (server PUT)
When clients sent caused_by as a field inside the doc object (natural placement), the server ignored it and never recorded the causal link. TRACE would return 0 ancestors.
Fixed: the server now checks for caused_by inside doc as a fallback if it's not at the body's top level.
nedbd --dag NameError on Python 3.10 (sys → _sys)
The --dag handler imported sys as _sys for aliasing but a leftover reference to sys.platform caused a NameError on Python 3.10. Fixed.
Windows: nedbd --dag subprocess/exec on Win32
Fixed the Windows path through the --dag handler: .exe binary search, subprocess.call instead of os.execv (which doesn't work on Windows), correct positional data dir argument.
Crypto dependency: pycryptodome replaces cryptography
cryptography >= 41 pulled in cffi >= 2.0 which fails to build on Windows MinGW. Switched primary backend to pycryptodome >= 3.19 which ships pre-built binary wheels for all platforms including Windows MinGW — no C compiler required. cryptography still accepted as optional fallback.
Rust banner version was frozen at 2.2.14
The N E D B · DAG ENGINE {version} banner printed by nedbd --dag read CARGO_PKG_VERSION at compile time. The Rust workspace Cargo.toml version field was not being bumped in release commits. Fixed — all Rust crates now version-sync with the Python package.
NedbCore cold-restart: seq and head returned 0/empty
When NedbCore.open(path) opened an existing database and the background scan hadn't completed, seq() returned 0 and head() returned empty. Fixed by calling flush() before closing (persists MANIFEST to disk) and waiting 1 second after reopen for the scan to complete.
YAML syntax errors in release.yml and codemagic.yaml
Inline python3 -c "..." blocks inside YAML run: | block scalars caused scanner errors at column 1. Fixed by replacing with a dedicated scripts/extract_native.py helper called as python3 scripts/extract_native.py.
MinGW wheel rejected by PyPI
A CI matrix entry for x86_64-pc-windows-gnu (MinGW target) produced wheels tagged mingw_x86_64_msvcrt_gnu which PyPI rejects as a non-standard platform tag. This caused the Publish native wheels to PyPI job to abort before uploading even the valid MSVC wheel. Fixed by removing the MinGW matrix entry — MSYS2 users get the binary from the fat universal wheel.
nedb._native import raises cryptic ImportError
On platforms where the compiled extension is absent, from nedb._native import NedbCore produced a bare ModuleNotFoundError with no guidance. Fixed with a stub module that intercepts the import and raises a detailed ImportError listing all three fix options (HTTP mode, pip reinstall, nedbd --doctor).
npm package — nedb-engine
The npm package now ships nedbd-v2 server binaries alongside the napi .node addon:
| File | Platform |
|---|---|
nedbd-v2-linux-x64 |
Linux x86_64 |
nedbd-v2-win-x64.exe |
Windows x86_64 |
nedbd-v2-darwin-arm64 |
macOS Apple Silicon |
nedbd-v2-darwin-x64 |
macOS Intel |
nedbd-v2.js |
Platform-detection shim |
The nedbd-v2 bin entry in package.json means npx nedbd-v2 ./data works after npm install nedb-engine.
CI changes
- Fat wheel assembly:
pypijob now depends on[node-binaries, create-release, wheels], downloads all platform binaries and_nativeextensions from GitHub release artifacts, and assembles a fat universal wheel before publishing. - Codemagic: uploads
nedbd-v2-darwin-arm64andnedbd-v2-darwin-x64binaries to the GitHub release (in addition to.nodefiles) so thepypijob can include them. - crates.io publish: new
publish-crateCI job runscargo publish --package nedb-core-v2on everyv*tag. RequiresCARGO_REGISTRY_TOKENActions secret. - Secret sync: the
nedb-releaseskill now syncsCARGO_REGISTRY_TOKENalongsidePYPI_API_TOKENandNPM_TOKEN.
Install
# Python
pip install nedb-engine
# Node
npm install nedb-engine
# Rust (DAG server binary only)
cargo install nedb-core-v2Windows MSYS2 / Git Bash
pip install nedb-engine # installs universal wheel with nedbd-v2.exe
nedbd --data ./data --dag # DAG server — works on MSYS2
NEDB_URL=http://localhost:7070 python3 your_script.py # embedded API via HTTP
nedbd --doctor # diagnose anything still missingTest
# Native (requires platform wheel)
python3 tests/test_the_will.py
# HTTP mode (any platform)
nedbd --data ./will-data --dag &
NEDB_URL=http://localhost:7070 python3 tests/test_the_will.pyINTERCHAINED, LLC × Vex (Claude Sonnet 4.6)
NEDB v2.2.0 — Performance Sprint
"In-memory DAG. WAL write buffer. Bindings on v2. Batch at 4,798/s."
Install
pip install --upgrade nedb-engine # 2.2.0 — PyO3 bindings now use v2 DAG
npm install nedb-engine # 2.2.0 — napi-rs bindings now use v2 DAG
pip install --upgrade nedb-engine-client # 1.2.0
npm install nedb-engine-client # 1.2.0What's new
🦀 Db::in_memory() — zero-disk DAG
Pure in-memory database: HashMap-backed ObjectStore, IdIndex, and GraphStore. Zero file I/O, sub-microsecond puts, instant startup. Perfect for tests, hot cache layers, and ephemeral sessions.
# Python (via PyO3)
from nedb._native import NedbCore
db = NedbCore() # in-memory — no files
db.put("items", "1", '{"x":1}')
db.query('FROM items LIMIT 5')
# TypeScript (via napi-rs)
import { NedbCore } from "nedb-engine"
const db = new NedbCore() // in-memory
db.put("items", "1", JSON.stringify({x:1}))🏁 NEDBD_MEMORY=1 — in-memory server mode
Start the entire nedbd daemon in memory — no files created anywhere:
NEDBD_MEMORY=1 nedbd --dag --data /ignored
curl http://127.0.0.1:7070/health
# {"memory": true, "engine": "dag", "ok": true}Banner shows: memory = yes — all data lost on exit. Health endpoint includes "memory": true.
⚡ WAL write buffer — id-index off the hot path
id_index.set() previously called fs::rename() on every PUT — one file rename per document, serialized across concurrent workers. Now:
set()writes to aDashMapbuffer only (zero I/O, lock-free, ~nanoseconds)- Background ticker calls
flush_write_buf()every 1s — Rayon parallel flush to disk get()checks WAL buffer first (latest value), then disklist_ids()merges disk + WAL, applies tombstones
🔀 spawn_blocking — parallel object I/O
db.put() wraps the object file write in tokio::task::spawn_blocking so concurrent PUT requests run on the blocking thread pool (up to 512 threads) instead of blocking the tokio async executor. Correct architecture for Linux where concurrent file writes perform well.
🔄 PyO3 + napi-rs bindings → v2 Db API
Both native binding crates (nedb-py, nedb-node) now use nedb_core_v2::Db:
pip install nedb-engine+ native wheel → Python gets v2 DAG engine natively (no HTTP)npm install nedb-engine+ native addon → Node.js gets v2 DAG engine natively- Same API surface as v1 — existing Python/Node code works unchanged
put()extractscaused_by/valid_from/valid_tofrom doc for DAG provenancelink()/unlink()stored as__links__docs for NQL TRAVERSE compatibilityflush()now callsflush_all()(WAL + MANIFEST)
🚀 503 fix — empty DB instant ready
New or just-created databases (0 objects) previously had a tiny window where startup_ready = false while a background thread confirmed "nothing to scan". Any write landing in that window got a 503. Fixed: empty DB sets startup_ready = true immediately without spawning a thread.
Benchmark (Intel iMac, 10k writes / 10k reads)
| Operation | v2.1.0 | v2.2.0 | Change |
|---|---|---|---|
| Batch writes (500/req) | 4,019/s @ 0.35ms | 4,798/s @ 0.14ms | +19% 🔥 |
| Sequential writes | 492/s @ 2.7ms | 504/s @ 1.8ms | +2% |
| Point-lookup reads | 542/s @ 3.2ms | 475/s @ 2.1ms | ≈ flat |
| ORDER BY queries | 583/s @ 2.1ms | 573/s @ 2.4ms | ≈ flat |
| Verify 30k objects | 997ms | 939ms | +6% |
| 503 on warmup | ✅ gone | fixed |
Note: concurrent write gains require Linux (ext4/io_uring). macOS APFS serializes concurrent file writes at the kernel level regardless of threading.
Full changelog
feat(db):Db::in_memory()— zero-disk DAG (ObjectStore, IdIndex, GraphStore all HashMap-backed)feat(server):NEDBD_MEMORY=1/memory_mode— in-memory server flag, health field, banner lineperf(index): WAL write buffer —id_index.set()writes to DashMap, Rayon-parallel flush every 1sperf(server):spawn_blockingfor PUT — object file I/O runs on blocking thread poolfeat(bindings): PyO3 bindings →nedb_core_v2::Db— native Python DAG enginefeat(bindings): napi-rs bindings →nedb_core_v2::Db— native Node.js DAG enginefix(startup): empty DB setsstartup_ready = trueimmediately — eliminates 503 on first writefix(compile):use std::sync::Arcin store/index/graph (missing import)
Links
- Source: github.com/aiassistsecure/nedb
- PyPI: pypi.org/project/nedb-engine
- npm: npmjs.com/package/nedb-engine
- Client PyPI: pypi.org/project/nedb-engine-client
- Studio: studio.interchained.org
© INTERCHAINED, LLC × Vex (Claude Sonnet 4.6)
NEDB v2.1.0 — Sprint Release
"Batch writes tripled. TRACE and AS OF fixed. Live query subscriptions. 28/28 tests green."
Install
pip install --upgrade nedb-engine # 2.1.0
npm install nedb-engine # 2.1.0
pip install --upgrade nedb-engine-client # 1.1.0
npm install nedb-engine-client # 1.1.0Start the v2 DAG engine:
NEDBD_DAG=1 NEDB_TMK=<32-byte-hex> nedbd --data ./data
curl http://127.0.0.1:7070/health
# {"ok":true,"version":"2.1.0","engine":"dag","service":"nedbd","encrypted":true}What's new
🔥 Batch writes +185% — 1,106/s → 3,153/s
POST /v1/databases/:name/batch now processes all put ops in parallel via Rayon. Object writes and id-index updates run concurrently across all cores. Sequential ordering preserved via pre-allocated seq numbers.
Batch writes (500/req): 1,106/s @ 1.25ms → 3,153/s @ 0.42ms
📈 Point reads +33% — 392/s → 523/s
Fixed a silent bug: node.hash was serialized as empty (skipped by skip_serializing_if) so every read-back returned a node with hash = "". This broke TRACE, AS OF, and caused unnecessary read failures. Now restored from the lookup path on every objects.read().
🔧 TRACE and AS OF were broken — now fixed
TRACE caused_by and AS OF seq both rely on following hash chains through the graph and object store. With node.hash = "" they silently returned empty results. Both are now fully functional:
# Causal trace — follow the chain backward
curl -X POST .../query -d '{"nql":"FROM ops WHERE _id = \"c\" TRACE caused_by LIMIT 10"}'
# Time-travel — state at a specific sequence number
curl -X POST .../query -d '{"nql":"FROM docs AS OF 42"}'📡 Live query subscriptions — POST /subscribe
Subscribe to any NQL query and receive real-time diffs as SSE:
curl -N -X POST http://127.0.0.1:7070/v1/databases/vision/subscribe \
-H 'content-type: application/json' \
-d '{"nql":"FROM blocks ORDER BY height DESC LIMIT 5"}'
# Initial result sent immediately
# data: {"sub_id":1,"rows":[...],"event":"initial"}
# After every PUT/batch that changes the result:
# data: {"sub_id":1,"rows":[...],"count":5}Unsubscribe: DELETE /v1/databases/:name/subscribe/:sub_id
⏰ Hourly checkpoint + graceful shutdown
- Hourly: MANIFEST flushed at top of every clock hour (system-clock aligned)
- SIGTERM (
systemctl stop, Docker): caught, flushes all MANIFESTs, clean exit - SIGINT (Ctrl+C): same
- Background ticker: MANIFEST dirty-flag flushed every 1s — removed per-write I/O from the hot path
🔬 DAG test suite — 28/28
python3 tests/test_dag.py — auto-spawns nedbd if nothing on port 7070, cleans up after. Covers:
- CRUD, tombstone deletes, query (NQL WHERE/ORDER BY/GROUP BY/SEARCH)
- Batch (parallel puts, mixed put/del, monotonic seq)
- Integrity (BLAKE2b head, verify, tamper evidence)
- Timeline (VALID AS OF, AS OF seq time-travel)
- Causal (TRACE caused_by)
- SSE (/events endpoint)
🖥️ Studio DAG engine badge
NEDB Studio now shows a DAG ◆ badge (green) in the nav bar when connected to the v2 Rust engine, AOF (amber) when on v1. Read-only — Studio observes the engine, never controls it. Tooltip: "start nedbd with --dag to use v2 DAG engine".
📊 engine: "dag" in /health
The health endpoint now reports the engine type:
{
"ok": true,
"service": "nedbd",
"version": "2.1.0",
"engine": "dag",
"databases": ["vision"],
"encrypted": true
}Benchmark (Intel iMac, 10k writes / 100k reads)
| Operation | v2.0.36 | v2.1.0 | Change |
|---|---|---|---|
| Sequential writes | 352/s @ 6.4ms | 399/s @ 5.6ms | +13% |
| Batch writes (500/req) | 1,106/s @ 1.25ms | 3,153/s @ 0.42ms | +185% 🔥 |
| Point-lookup reads | 392/s @ 5.9ms | 523/s @ 4.7ms | +33% |
| ORDER BY queries | 457/s @ 3.8ms | 572/s @ 2.4ms | +25% |
| Verify 30k objects | — | 1.15s (~26k/s) | ✅ |
| Concurrent writes (16) | 248/s @ 230ms | 245/s @ 215ms | ≈ flat |
Concurrent single-PUT bottleneck remains — targeted in v2.2.
Full changelog
perf(db): parallel batch writes viaput_batch()— Rayon object + index writesperf(db): decoupleflush_manifestfrom write path — background ticker every 1sfeat(server): hourly clock-aligned checkpoint + SIGTERM/SIGINT graceful shutdownfeat(server): live query subscriptions —POST /v1/databases/:name/subscribefeat(server):engine: "dag"field in/healthresponsefix(store): restorenode.hashafter deserialization — fixes TRACE and AS OFfix(store): guard empty hash inObjectStore::read()— no more panic on empty stringtest: DAG correctness suite — 28 tests, auto-spawn,tests/test_dag.pyfeat(studio): engine status badge (DAG ◆ / AOF) in Studio Nav bar
Links
- Source: github.com/aiassistsecure/nedb
- PyPI: pypi.org/project/nedb-engine
- npm: npmjs.com/package/nedb-engine
- Client PyPI: pypi.org/project/nedb-engine-client
- Studio: studio.interchained.org
© INTERCHAINED, LLC × Vex (Claude Sonnet 4.6)
NEDB v2.0.36 — Production Stable
Cross-platform native wheels shipping the
nedbd-v2Rust binary insidepip install nedb-engine. Linux, Windows, macOS arm64, and macOS x86_64 — all four platforms publish from a singlev*tag.
NEDB is a content-addressed Merkle DAG, hash-chained, time-traveling, bi-temporal, causally-provable embedded database. Replay-protected, idempotent, relational, filterable, sortable, searchable, concurrent. One Rust core, shipped to PyPI and npm from a single source.
Install
pip install nedb-engine # Python ≥ 3.8 — pure-Python + native wheel with nedbd-v2 binary
npm install nedb-engine # Node ≥ 16 — napi-rs prebuilt addons (Linux, Windows, macOS arm64+x86_64)
pip install nedb-engine-client # async Python HTTP client — connect to any nedbd instance
npm install nedb-engine-client # TypeScript / Node.js 18+ HTTP clientThe native wheel includes the nedbd-v2 Rust DAG-engine binary bundled inside the Python package. After pip install, run nedbd --dag --data ./data and the v2 engine boots immediately — no separate Rust build required.
What's new in the v2 DAG (since v1 AOF)
NEDB v1 was an append-only log with a BLAKE2b hash chain. v2 keeps every guarantee — tamper-evident, replay-protected, hash-chained, time-traveling, bi-temporal, causally-provable — and adds a content-addressed Merkle DAG underneath. Documents are immutable, deduplicated by hash, and verifiable in parallel.
| Property | v2 DAG | v1 AOF |
|---|---|---|
| Uncorruptable (atomic writes, hash-verified reads) | yes | partial |
| O(1) warm start via MANIFEST (no scan, no replay) | yes | no |
| Deferred cold scan (socket open immediately) | yes | no |
| O(1) incremental Merkle head (never recomputed) | yes | no |
| Parallel writes (no global lock) | yes | no |
| BLAKE2b Merkle head on every response | yes | no |
| IdIndex sharded across 256 subdirectories | yes | no |
| TCP_NODELAY (no 40–200 ms loopback Nagle delay) | yes | no |
GET /events SSE log stream |
yes | no |
| Tombstone deletes (history preserved) | yes | yes |
| Auto-migrates v1 AOF → v2 DAG on startup | yes | — |
| Same HTTP API — Vision, Studio, all clients unchanged | yes | yes |
v1 AOF engine is still shipped and unchanged. Running nedbd (no flag) launches v1. Running nedbd --dag (or NEDBD_DAG=1 nedbd) launches the v2 DAG engine binary.
Highlights
- Content-addressed Merkle DAG — every document version is an immutable BLAKE2b-verified object. Identical content is deduplicated automatically. Nothing is ever overwritten.
- O(1) warm start — every restart after the first open reads a tiny
MANIFESTfile and restoresseq+ Merkleheadin milliseconds. No scan, no replay, independent of dataset size. - Deferred cold start — first open of an existing dataset spawns the integrity scan in a background thread and accepts connections immediately. Reads serve instantly from the content-addressed DAG; writes return
HTTP 503 startup in progressuntil thestartup_readygate flips. - Live event stream —
GET /eventsis a Server-Sent Events endpoint that streams scan progress (event: scan), ready transitions (event: ready), and per-write head updates (event: write) to any connected client. The Studio uses this for live indicators. - IdIndex sharding — 256 subdirectories under
dag/keep the filesystem fast even at millions of objects. - Sharded production sequencer — group-commit batches writes; a single committer thread per database chains every op and issues one fsync per batch. Parallel readers, no write-write races, no global lock.
- TCP_NODELAY — the axum listener disables Nagle. On macOS loopback this eliminates the 40–200 ms artificial delay that would otherwise hit small request/response payloads.
- AES-256-GCM at-rest encryption — TMK/DEK double-envelope, opt-in via
NEDB_TMK=<32-byte-hex>. Per-database DEK derived from the TMK and the database name. - Auto-migration — first
--dagstartup on a v1 AOF data directory replays the log into the DAG store with zero data loss. v1 stays mountable; v2 is additive.
Performance (v2.0.36)
Measured on an Intel iMac with AES-256-GCM encryption on, 10k writes / 100k reads / 30k objects, against the running nedbd-v2 over HTTP/JSON:
| Operation | Throughput | p50 | p99 |
|---|---|---|---|
| Sequential writes | 418 ops/s | 2.3 ms | 3.3 ms |
| Point-lookup reads | 478 ops/s | 2.0 ms | 3.0 ms |
| ORDER BY queries | 489 ops/s | 1.8 ms | 4.3 ms |
| Batch writes (500 ops/req) | 1,104 ops/s | 0.9 ms | 1.2 ms |
| Tamper-verify (30k objects) | ~21,000 BLAKE2b/sec | — | 1.38 s total |
p99 latencies hold under 4 ms because of TCP_NODELAY on the axum listener. Without it, macOS loopback adds 40–200 ms from Nagle on every small write.
Reproduce locally:
NEDBD_DAG=1 nedbd --data /tmp/perf &
python3 tests/test_dag_perf.py --n 10000 --reads 100000CI architecture
NEDB v2.0.36 ships from two CI providers, coordinated by a single git tag.
GitHub Actions — Linux + Windows
pypi— builds the universal pure-Python wheel (py3-none-any) + sdist onubuntu-latest. Runs the 266-test gate first, then publishes to PyPI viatwine upload --skip-existing.wheels— matrix-builds the maturin native wheel forx86_64-unknown-linux-gnuandx86_64-pc-windows-msvc. Each runner compiles thenedbd-v2binary, stages it into the Python package layout, and builds the wheel.publish-native— downloads every wheel artifact (Linux, Windows, and the macOS wheels uploaded by Codemagic) and publishes them all to PyPI.create-release— opens the GitHub Release for the tag so Codemagic has somewhere to upload Mac.nodebinaries.node-binaries— napi-rs matrix on Linux + Windows; uploads each.nodeto the GitHub Release.publish-npm— polls the GitHub Release until all four platform.nodebinaries are present (Linux + Windows from GitHub, macOS arm64 + x86_64 from Codemagic), then runs onenpm publishwith the complete bundle.client-pypi/client-npm— publishesnedb-engine-client(HTTP client) to PyPI and npm on the same tag.
Codemagic — macOS arm64 + x86_64
Two parallel workflows on Apple Silicon M2 Mac Minis:
macos-arm64-wheel— buildsnedbd-v2foraarch64-apple-darwin, packages it inside the maturin wheel, publishes the wheel directly to PyPI, then builds the napi addon and uploads the.nodeto the GitHub Release.macos-intel-wheel— cross-compilesnedbd-v2forx86_64-apple-darwinfrom the M2 host (configuringCARGO_TARGET_X86_64_APPLE_DARWIN_LINKER,CC_x86_64_apple_darwin, andSDKROOTto Xcode's clang + macOS SDK). Same flow as arm64 — wheel to PyPI,.nodeto the GitHub Release.
Why split
GitHub's macOS runners are slow and queue-bound; cross-compiling x86_64 from a hosted arm64 macOS runner is unreliable. M2 Mac Minis on Codemagic build both Mac targets in parallel in ~20 minutes wall-clock, in lockstep with the Linux + Windows GitHub jobs. PyPI wheels upload independently (--skip-existing makes this safe). npm publishing waits for all four .node binaries to land on the GitHub Release, then publishes once with the complete bundle.
push tag v2.0.36
│
┌──────────┴──────────┐
│ │
GitHub Actions Codemagic
│ │
┌────┼────┐ ┌────┴────┐
│ │ │ │ │
PyPI Linux Win Mac arm64 Mac x86_64
│ │ │ │ │
│ └────┴───┬───────┴─────────┘
│ │
│ GitHub Release (.node × 4)
│ │
└──────► publish-npm ◄──── nedb-engine
nedbd --dag startup
When you launch the v2 DAG engine, the binary prints the Merkle DAG triangle banner before binding the listener:
◆
╱ ╲ N E D B · DAG ENGINE 2.0.36
◆ ◆ ─────────────────────────────────────────────
╱ ╲ ╱ ╲ content-addressed · tamper-evident · causal
◆ ◆ ◆ bi-temporal · replay-protected · encrypted
╱ ╲ ╱ ╲ ╱ ╲
◆ ◆ ◆ ◆ © INTERCHAINED, LLC × Vex (Claude Sonnet 4.6)
╱ ╲ ╱ ╲ ╱ ╲ ╱ ╲ interchained.org · hyperagent.com/refer/J2G6TCD7
─────────────────────────────────────────────────────────────
listen http://127.0.0.1:7070
data ./nedb-data
enc AES-256-GCM
token off (set NEDBD_TOKEN to require auth)
─────────────────────────────────────────────────────────────
The Merkle triangle is intentional — every layer doubles the previous, just like the BLAKE2b parent/child structure of the content-addressed DAG itself.
Environment variables
| Variable | Default | Description |
|---|---|---|
NEDBD_DAG |
0 |
Set 1 to launch the v2 DAG engine (nedbd-v2). Same as --dag. |
NEDBD_HOST |
127.0.0.1 |
Bind address. v2.0.36 defaults to loopback (was 0.0.0.0) — security hardening. Set explicitly to 0.0.0.0 to expose. |
NEDBD_PORT |
7070 |
HTTP bind port. |
NEDBD_TOKEN |
unset | Optional bearer token; required on every /v1/* request when set. |
NEDB_TMK |
unset | 32-byte hex AES-256-GCM at-rest master key. |
NEDBD_DATA |
./nedb-data |
Root directory. v2 creates dag/, IdIndex sharded across 256 subdirectories, and a small MANIFEST file. |
HTTP API
The v2 DAG engine exposes the same /v1/databases/* surface as v1 — Vision, Studio, and every existing client work unchanged.
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness + version + databases + encryption status. |
GET |
/events |
Server-Sent Events strea... |
NEDB v2.0.4 — The DAG Engine
"Content-addressed, tamper-evident, parallel, instant cold start."
This is the first production release of NEDB v2 — a ground-up rewrite of the storage engine from an append-only log (AOF) to a content-addressed Merkle DAG. The v1 Python AOF engine ships alongside it untouched; v2 is opt-in via --dag.
What's New
nedbd --dag — The v2 DAG Engine
Start the Rust v2 DAG engine with a single flag:
nedbd --dag --data /path/to/data
# or
NEDBD_DAG=1 nedbd --data /path/to/dataThe Python process execs directly into the compiled Rust binary — no wrapper overhead, clean signal handling, same port/token/data env vars as v1. The v1 AOF engine is completely untouched.
Content-Addressed DAG Storage
Every document version is an immutable, encrypted, BLAKE2b-verified object stored at objects/{hash[0:2]}/{hash[2:]}. Nothing is ever overwritten.
- Uncorruptable — atomic writes (tmp → rename), hash verification on every read
- Parallel — no global lock; each document has its own index file
- Instant cold start — no AOF replay; rebuild sorted indexes in parallel from the object store
- Tamper-evident — BLAKE2b chain: MANIFEST → collection heads → every node
- Self-healing — v1 AOF auto-migrates to v2 DAG on first startup (encrypted AOFs supported)
Real BLAKE2b Merkle Head
Every response from the v2 server includes a head field — a BLAKE2b hash of all current id-index hashes, sorted and chained with the global sequence number. Changes on every write. Anchorable. Verifiable.
{ "ok": true, "seq": 42, "head": "a3f9c2e1..." }Real Tombstone Deletes
DELETE /v1/databases/{name}/rows/{coll}/{id} now:
- Writes an immutable tombstone node (history preserved in the DAG forever)
- Removes the live id pointer from the index
- Doc is permanently invisible to all queries and
list()— but fully recoverable viaAS OF
AES-256-GCM Encryption Through Migration
The DEK (derived from NEDB_TMK) now flows correctly through the v1→v2 migration path. Encrypted v1 AOF databases convert to v2 DAG without data loss.
Native Wheels with nedbd-v2 Binary
Platform wheels (manylinux, macos arm64, win amd64) now ship the compiled nedbd-v2 Rust binary alongside the Python package. pip install nedb-engine gives you both engines on every supported platform.
Installation
pip install --upgrade nedb-engine # → 2.0.4Run v2 DAG (recommended for new deployments)
NEDBD_DAG=1 NEDB_TMK=<32-byte-hex> nedbd --data /path/to/data
curl http://127.0.0.1:7070/health
# {"ok":true,"version":"2.0.4","service":"nedbd","encrypted":true}Run v1 AOF (unchanged, all existing deployments)
NEDB_TMK=<32-byte-hex> nedbd --data /path/to/dataAutomatic v1 → v2 Migration
Zero user action required. On first --dag startup, if log.aof is detected in a database directory, v2 reads all valid ops, converts them to content-addressed DAG nodes in parallel, rebuilds indexes, and renames log.aof → log.aof.v1.bak. The backup is always kept as a rollback path.
[nedb] Detected v1 log.aof — running automatic migration to v2 DAG...
[nedb] 469041 op(s) to migrate
[nedb] Migration complete: 469041 op(s) → v2 DAG. Backup: log.aof.v1.bak
The Full HTTP API
100% wire-compatible with v1 — Vision, NEDB Studio, and all existing clients work unchanged.
| Method | Route | Description |
|---|---|---|
| GET | /health |
Version, databases, encryption status |
| GET | /v1/databases |
List all databases with seq + Merkle head |
| POST | /v1/databases |
Create a database |
| GET | /v1/databases/:name |
Database info |
| DELETE | /v1/databases/:name |
Drop database |
| POST | /v1/databases/:name/put |
Write document (causal provenance + bi-temporal) |
| POST | /v1/databases/:name/query |
NQL query |
| POST | /v1/databases/:name/batch |
Batch put/delete |
| DELETE | /v1/databases/:name/rows/:coll/:id |
Tombstone delete |
| POST | /v1/databases/:name/index |
Create sorted index |
| GET | /v1/databases/:name/verify |
BLAKE2b tamper-evidence check (parallel) |
| POST | /v1/databases/:name/checkpoint |
Checkpoint (no-op in v2 — always snapshotted) |
| GET | /v1/databases/:name/log |
Recent write log |
NQL — NEDB Query Language
FROM blocks
[AS OF <seq>]
[VALID AS OF "<iso-date>"]
[WHERE field = value [AND ...]]
[ORDER BY field [DESC]]
[LIMIT n]
[GROUP BY field COUNT|SUM|AVG|MIN|MAX]
[TRACE caused_by [REVERSE]]
[SEARCH "text"]Full Changelog
feat:--dag/NEDBD_DAG=1—os.execvinto Rust v2 binary, no Python overheadfeat: Real tombstone deletes — tombstone node written, id removed fromIdIndexfeat: Real BLAKE2b Merkle head on every response — tamper-evident, changes on every writefeat: DEK passthrough tomigrate_if_needed()— encrypted v1 AOFs convert correctlyfeat:nedbd-v2binary bundled in native Python wheels (Linux / macOS / Windows)fix:DekderivesClone— fixes borrow-after-move inDb::open()fix:Db.seqmadepub— server can compute Merkle headfix:base64_decodeerror type —String→anyhow::Errorvia.map_err()fix:parking_lotdep corrected to0.12chore:rust/nedb-v2added to Rust workspace withCargo.lockcommittedchore: All 6 version strings bumped to2.0.4in syncdocs:index.html+reference.htmlupdated for v2 DAG engine
What's Next
- PyO3 + napi-rs bindings updated to v2
DbAPI (Python SDK + Node SDK using DAG natively) - MANIFEST file persisted to disk for external anchoring
- NEDB Studio: DAG mode toggle in the Connect panel
- Vision production upgrade:
NEDBD_DAG=1with full ITC block history backfill
Built by Interchained LLC × Claude — maintained by the NEDB Hyperagent.
GitHub: Eth-Interchained/nedb | Mirror: aiassistsecure/nedb
NEDB v1.2.0 — Redis Layer-2: Backfill + Write Shadowing
RELEASE NOTES
NEDB can now wrap any existing Redis connection and retroactively absorb all historical data plus auto-chain every future write into its hash-verified, time-traveling log — in three lines.
What's new
CollectionMapping — teach NEDB your key structure
Map Redis key glob patterns to NEDB collections with a custom id extractor and value parser.
register() — chainable collection registration
(r.nedb
.register("driver:*", collection="driver", value_parser=json.loads)
.register("trip:*", collection="trip", value_type="hash")
)backfill() — one-time import of all existing Redis data
Scans Alice's existing Redis keys via SCAN and imports them into NEDB's hash chain in one pass. Returns the number of keys imported. Evidence is tagged "backfill" on every imported record.
imported = r.nedb.backfill() # → 6 (scanned and imported)shadow_writes = True — all future surface-1 writes auto-chain
Once enabled, every r.set(), r.hset(), r.setex(), etc. is silently mirrored into NEDB alongside the normal Redis write. Alice's app code changes zero lines.
r.nedb.shadow_writes = True
r.set("driver:d1", json.dumps({"name": "Bob", "status": "active"}))
# goes to Redis AND into NEDB's hash chain automaticallyFull three-step migration
import redis, json
from nedb import wrap_redis
# ONE LINE — Alice's app doesn't change
r = wrap_redis(redis.Redis("localhost", 6379), db_name="rideshare")
# Step 1 — register key patterns as NEDB collections
(r.nedb
.register("driver:*", collection="driver", value_parser=json.loads)
.register("trip:*", collection="trip", value_type="hash")
)
# Step 2 — backfill all existing Redis data into NEDB (one-time)
imported = r.nedb.backfill()
print(f"Imported {imported} existing keys")
# Step 3 — shadow all future surface-1 writes
r.nedb.shadow_writes = True
# Alice's existing app — zero changes
r.set("driver:d1", json.dumps({"name": "Bob", "status": "active", "lat": 37.77}))
r.hset("trip:t1", mapping={"status": "en_route", "driver_id": "d1"})
# New NEDB features on the same connection
active = r.nedb.query('FROM driver WHERE status = "active" ORDER BY lat ASC')
# Time-travel
snap = r.nedb.seq
r.set("driver:d1", json.dumps({"name": "Bob", "status": "offline"}))
past = r.nedb.get_as_of("driver", "d1", snap) # {"status": "active", ...}
# Causal provenance
r.nedb.put("dispatch", "e1",
{"trip_id": "t1", "driver_id": "d1", "algo": "nearest"},
caused_by=[r.nedb.seq - 1],
evidence="inference",
confidence=0.97)
r.nedb.query('FROM dispatch WHERE _id = "e1" TRACE caused_by')
# Tamper evidence
r.nedb.verify() # True
r.nedb.head() # 64-char BLAKE2b commitment hashIsolation guarantee
NEDB never writes to Alice's namespace. It owns only:
| Key | Type | Purpose |
|---|---|---|
nedb:{db_name}:oplog |
Redis Stream | append-only op log |
nedb:{db_name}:snapshot |
Redis Hash | checkpoint |
nedb:{db_name}:meta |
Redis Hash | index config |
Local demo (no Redis server needed)
pip install nedb-engine fakeredis
git clone https://github.com/aiassistsecure/nedb
cd nedb
python3 examples/fakeredis_demo.py
# 29/29 checks passedTest coverage
- 74/74 tests passing (
python3 tests/test_wrap_redis.py) - 30 new tests covering: backfill, write shadowing, hset merge, time-travel through shadows, full pipeline + restart
Install
pip install nedb-engine==1.2.0
npm install nedb-engine@1.2.0Changelog
| Version | Highlight |
|---|---|
| 1.2.0 | wrap_redis() backfill + write shadowing — register(), backfill(), shadow_writes |
| 1.1.0 | wrap_redis() — NEDB as a Redis layer-2, dual surface |
| 1.0.5 | License → GPL-3.0-or-later, npm homepage fix |
| 1.0.3 | Rust NQL integer/float comparison fix |
| 1.0.2 | Rust AS OF index bypass fix |
| 1.0.1 | napi-rs binding rewrite |
Built by INTERCHAINED LLC × Claude Sonnet 4.6
Docs: nedb.aiassist.net
nedb-engine v0.9.0 — Causal Write Provenance
The first embedded database with cryptographically-sealed causal chains.
Every write can now declare why it happened, what caused it, and how confident
the writer was — sealed inside the hash chain at write time, tamper-evident,
time-travelable, and queryable in both directions.
Install / Upgrade
pip install --upgrade nedb-engineSupports Python ≥ 3.8. Native Rust-core wheels ship for Linux (x86_64 manylinux),
macOS (arm64), and Windows (x86_64). All platforms fall back to the pure-Python
reference engine automatically.
What's New in v0.9.0 — Causal Write Provenance
The feature
AI agents write data constantly. Until now, no embedded database tracked why a
write happened — which inputs triggered it, what it was inferred from, or how
certain the writer was. That meant auditing an agent's reasoning required
reconstructing causality from application logs, which are untrustworthy and
inevitably stale.
NEDB v0.9.0 makes causality a first-class storage primitive. Three optional
fields on every put() call:
| Field | Type | Meaning |
|---|---|---|
caused_by |
List[int] |
Seqs of the ops that caused this write |
evidence |
str |
Source type: user_message · inference · tool_result · correction · external |
confidence |
float |
Agent certainty, 0.0–1.0 |
These are sealed inside the BLAKE2b hash chain at write time — if you change
them after the fact, verify() fails. They are also mirrored into the document
as queryable _caused_by, _evidence, _confidence fields so they work with
any WHERE clause.
API
from nedb import NEDB
db = NEDB("./agent-memory")
# Raw inputs — uncaused roots
db.put("inputs", "turn_1", {"role": "user", "text": "I hate bright screens"})
db.put("inputs", "turn_2", {"role": "user", "text": "I have migraines"})
seq_1, seq_2 = db.seq - 1, db.seq
# Derived belief, sealed in the chain
db.put("beliefs", "dark_mode_pref",
{"value": True, "summary": "User prefers dark mode"},
caused_by=[seq_1, seq_2],
evidence="user_message",
confidence=0.95)
# Second-order inference
db.put("beliefs", "low_blue_light",
{"value": True},
caused_by=[db.seq], # caused by dark_mode_pref
evidence="inference",
confidence=0.82)
# Provenance is queryable via normal WHERE
db.query('FROM beliefs WHERE _evidence = "user_message"')
db.query('FROM beliefs WHERE _confidence > 0.9')NQL: TRACE operator
FROM beliefs WHERE _id = "dark_mode_pref" TRACE caused_by
Backward traversal — recursively follows caused_by seqs to their originating
documents. Answers: "Why does the agent believe this?"
FROM inputs WHERE _id = "turn_1" TRACE caused_by REVERSE
Forward traversal — uses the in-memory reverse index to find all documents that
declared this op as a cause. Answers: "What did this input cause downstream?"
Why this matters
- EU AI Act (full applicability: August 2026): Article 13 requires operators of
high-risk AI systems to produce records of "the logic involved" in decisions.
Causal provenance at the storage layer makes this verifiable, not self-reported. - OWASP Agentic Top 10 (ASI06 — Memory & Context Poisoning): A tamper-evident
causal chain means you can detect injected beliefs — they either lack provenance
or breakverify(). - Operational auditing: "Why does the agent recommend X?" is now a database
query, not a forensic reconstruction exercise.
Engram and Operad build causal provenance at the application layer over PostgreSQL
and Neo4j. NEDB builds it at the storage layer — sealed in the same hash chain
that already proves tamper-evidence and time-travel.
Backward compatibility
Fully backward-compatible. Existing databases and AOF files verify without
modification. Old ops without provenance omit the fields from their hash body —
mixed chains (some ops with provenance, some without) verify correctly. No
migration required.
What Changed Since v0.7.4
v0.8.3 — Deploy fix (encrypted new databases)
Bug: Creating a new database while NEDB_TMK (encryption) was set failed with
FileNotFoundError: ./nedb-data/<name>/key.enc.tmp because load_or_create_dek()
ran before _open() created the directory.
Fix: os.makedirs(path, exist_ok=True) immediately before the DEK call.
This was the root cause of every "Deploy failed (502)" in the studio when the
daemon was running with at-rest encryption enabled.
v0.8.2 — Structured logging + deploy integration test
NEDBD_DEBUG=1/nedbd --log-level N(0=errors only, 1=requests, 2=deploy phases, 3=verbose)- Full traceback always printed on unhandled exceptions (never swallowed silently)
tests/test_deploy.py— integration test for the full scaffold deploy path
(the test that would have caught the v0.8.3 bug before production)
v0.8.0 — Concurrent daemon (group-commit sequencer)
ThreadingHTTPServer was thread-unsafe: concurrent writes to one database raced
the hash chain, causing 500s surfaced as the studio's 502. The fix:
- Single-writer per database: writers enqueue intents, one committer thread
owns all mutation — correct chain by construction, zero write locks. - Group commit: the committer drains the whole queue, applies every op,
then issues onefsyncper batch. More concurrent writers → bigger batches →
higher throughput. ~15,000 writes/s under load. - Lock-free MVCC reads: reads run at the last committed seq (snapshot
isolation); they never touch the write queue or take a lock.
v0.7.6 — Self-healing chains
The encryption backfill (plaintext DB opened with NEDB_TMK) appended a
checkpoint op that was never persisted — creating a permanent gap in the chain.
Every subsequent open returned verify() = False, showing the "tampered" pill
in the studio even though nothing was tampered.
- Backfill no longer checkpoints — it drops the stale snapshot and rewrites
the AOF cleanly. _self_heal_if_needed()on open: ifverify()fails but every op is
internally consistent (only the linkage broke, content is intact), the chain
is re-linked in place and the AOF rewritten. Genuine content tampering is left
Falsewith a warning — real attacks are never masked.
v0.7.5 — MongoDB compatibility adapter
nedb.mongo.MongoCompat (MongoClient alias) — the third compatibility layer
alongside SQL and Redis adapters. Full document/collection API:
from nedb import NEDB, MongoClient
db = NEDB()
users = MongoClient(db)["users"]
users.insert_many([{"name": "Alice", "age": 31}, {"name": "Bob", "age": 24}])
list(users.find({"age": {"$gt": 25}}).sort("age", -1))
users.update_one({"name": "Alice"}, {"$inc": {"logins": 1}})
users.aggregate([{"$group": {"_id": None, "avg": {"$avg": "$age"}}}])Supports: find findOne count distinct aggregate insertOne insertMany
updateOne updateMany deleteOne deleteMany replaceOne · Query operators:
$eq $ne $gt $gte $lt $lte $in $nin $exists $regex $and $or $nor $not $size $all $mod $elemMatch · Update operators: $set $unset $inc $mul $min $max $rename $push $addToSet $pull $pop $setOnInsert · Aggregation: $match $group $sort $skip $limit $count $project
Also exposed over nedbd: POST /v1/databases/:name/mongo
Full Changelog (v0.7.4 → v0.9.0)
| Version | Date | Summary |
|---|---|---|
| v0.7.4 | 2026-06-14 | Fix maturin native wheel — stage Python source into crate so the built wheel contains the full package, not just _native.so |
| v0.7.5 | 2026-06-14 | MongoDB compatibility adapter (MongoCompat/MongoClient) |
| v0.7.6 | 2026-06-14 | Self-healing chains; encrypt-backfill gap fix |
| v0.8.0 | 2026-06-14 | Concurrent daemon — single-writer group-commit sequencer |
| v0.8.1 | 2026-06-14 | MongoDB nedbd endpoint (POST /v1/databases/:name/mongo) |
| v0.8.2 | 2026-06-14 | Structured logging (--log-level); deploy integration test |
| v0.8.3 | 2026-06-14 | Fix encrypted new-DB deploy (makedirs before DEK creation) |
| v0.9.0 | 2026-06-15 | Causal Write Provenance — caused_by, evidence, confidence, TRACE NQL |
Architecture Summary
nedb-engine
├── Hash-chained append-only OpLog (tamper-evident, replay-protected)
│ └── Causal provenance v0.9.0 (caused_by / evidence / confidence)
├── MVCC store (time-travel AS OF seq)
├── Relations + adjacency index (TRAVERSE, link/unlink)
├── Eq / Ordered / Search indexes
├── Concurrent Sequencer (group-commit, lock-free reads)
├── AES-256-GCM at-rest encryption (TMK / DEK double-envelope)
├── AOF durable persistence + snapshots
└── Compatibility adapters
├── SQL (SELECT/INSERT/UPDATE/DELETE)
├── Redis (GET/HSET/SADD/LPUSH/…)
└── MongoDB (find/aggregate/update/…)
Links
- PyPI: https://pypi.org/project/nedb-engine/0.9.0/
- GitHub: https://github.com/Eth-Interchained/nedb
- Studio: https://studio.interchained.org
- INTERCHAINED: https://interchained.org · https://aiassist.net
Built by INTERCHAINED LLC × Claude Sonnet 4.6
Apache-2.0 (engine) · GPLv3 (studio)
NEDB v0.6.0 Release Notes
nedb-engine + nedb-studio · INTERCHAINED, LLC · June 2026
nedb-engine v0.6.0 — RESP2 + Encryption + Snapshots
The headline release. nedbd now speaks the Redis wire protocol natively — redis-cli, redis-benchmark, and every Redis client library in every language connects without modification. Combined with AES-256-GCM at-rest encryption, Redis-style AOF persistence with snapshot checkpoints, and SQL/Redis/Python adapters, NEDB is a time-traveling, verifiable, encrypted Redis replacement.
pip install nedb-engine
# Generate a TMK (save it somewhere safe)
TMK=$(python3 -c "import os; print(os.urandom(32).hex())")
# Start the server — encrypted + redis-cli compatible
NEDB_TMK=$TMK NEDBD_RESP2_PORT=6379 nedbd
# Boot log:
# encryption: AES-256-GCM enabled (NEDB_TMK configured)
# [mydb] backfill-encrypting existing log… (first run only)
# nedbd 0.6.0 — http://127.0.0.1:7070 data=./nedb-data auth=off
# resp2 — redis:// 127.0.0.1:6379 (RESP2 wire protocol)
# Connect with redis-cli
redis-cli -p 6379 PING
redis-cli -p 6379 SELECT mydb
redis-cli -p 6379 HSET user:1 name Ada age 31
redis-cli -p 6379 EVAL "FROM users WHERE status = \"active\" LIMIT 5" 0What's new in v0.6.0
RESP2 Wire Protocol (NEDBD_RESP2_PORT)
nedbd runs a second TCP server on the port you specify, speaking RESP2 (Redis Serialization Protocol v2). Every Redis client connects natively — no proxy, no adapter, no Redis installation.
SELECT <name>— opens a named NEDB database (replaces Redis's integer 0-15 DBs)EVAL "<NQL>" 0— NQL pass-through: run any NQL query, rows returned as JSON strings- All major command groups: SET/GET/DEL/EXISTS/INCR/INCRBY/DECR, HSET/HGET/HGETALL/HDEL, SADD/SMEMBERS/SISMEMBER/SREM/SCARD, LPUSH/RPUSH/LRANGE/LLEN/LPOP/RPOP, KEYS/TYPE/DBSIZE/FLUSHDB
- Unsupported commands (EXPIRE/TTL, SUBSCRIBE/PUBLISH, MULTI/EXEC) return clear
-ERRwith a roadmap note — never silently wrong
AES-256-GCM Encryption at Rest (NEDB_TMK)
Double-envelope key architecture: a random per-database DEK is wrapped by an external TMK (Table Master Key).
- Encrypts the AOF, snapshot.json, and BlobStore chunks
- Auto backfill-encrypt: enabling
NEDB_TMKon an existing unencrypted database rewrites the entire AOF encrypted in place (atomic) on first open — visible in boot logs - Key rotation:
db.rewrap_key(old_tmk, new_tmk)re-wraps the DEK without touching any data cryptographyis now a bundled required dependency — encryption just works, no separate install
TMK sources (priority order)
NEDB(path, tmk=bytes)— programmaticNEDB_TMK=<64-char hex>— environment variableNEDB_TMK_FILE=/path/to/keyfile— raw bytes from a file
Full changelog since v0.4.2
| Version | Feature |
|---|---|
| v0.5.0 | Snapshot checkpoints (db.checkpoint()), TTL/expiry (ttl_s=, expire(), sweep()), GROUP BY aggregations (COUNT/SUM/AVG/MIN/MAX in NQL) |
| v0.5.1 | nedbd auto-checkpoints all databases on SIGTERM/SIGINT — synchronized restart; O(delta) startup |
| v0.5.2 | BlobStore (Cascade files) persisted in snapshots — get_file(), Merkle proofs survive restart |
| v0.5.3 | AES-256-GCM encryption at rest — AOF, snapshots, blob chunks; NEDB_TMK env; key rotation |
| v0.5.4 | nedbd Manager wires NEDB_TMK — all databases encrypted when env is set |
| v0.5.5 | cryptography bundled as required dep — no separate install needed |
| v0.5.6 | Auto backfill-encrypt existing plaintext AOF on first encrypted open (atomic, visible in boot log) |
| v0.5.7 | Eager database open at startup — backfill + snapshot load shows in boot log |
| v0.6.0 | RESP2 wire protocol — drop-in Redis replacement; EVAL "<NQL>" 0 pass-through |
Feature matrix vs Redis / MariaDB
| Feature | NEDB | Redis | MariaDB |
|---|---|---|---|
| Key/value + hash/set/list | ✓ RESP2 | ✓ | — |
| SQL SELECT/INSERT/UPDATE/DELETE | ✓ adapter | — | ✓ |
| Full-text search | ✓ inverted index | — (RediSearch) | ✓ FULLTEXT |
| Graph relations + TRAVERSE | ✓ time-travel aware | — (RedisGraph) | — |
| Time-travel reads (AS OF seq) | ✓ | — | — |
| Hash-chain integrity (verify) | ✓ BLAKE2b | — | — |
| Replay protection + idempotency | ✓ nonce + idem key | — | — |
| Encryption at rest | ✓ AES-256-GCM | ✓ enterprise | ✓ |
| Snapshot checkpoints | ✓ chain-anchored | ✓ RDB | ✓ |
| GROUP BY aggregations | ✓ NQL native | — Lua | ✓ |
| Versioned file store + Merkle | ✓ Cascade | — | — |
| RESP2 wire (redis-cli) | ✓ NEDBD_RESP2_PORT |
✓ | — |
| Natural-language queries + writes | ✓ Studio + NQL | — | — |
Full feature set (cumulative)
- Hash-chained append-only log — nonce-enforced replay protection, idempotency, MVCC time-travel (
AS OF seq), tamper-evidentverify() - NQL —
FROM t [AS OF n] [WHERE …] [SEARCH "text"] [ORDER BY f] [TRAVERSE rel] [LIMIT n] [GROUP BY f COUNT|SUM|AVG|MIN|MAX] - Relations — first-class graph edges with O(1) traversal, time-travel aware
- Indexes — eq (hash), ordered (bisect), full-text inverted — maintained incrementally
- Cascade compression — content-defined chunking + dedup + zlib/LZMA tiers, Merkle proofs per file version (anchorable on-chain)
- SQL adapter —
sql_exec(db, sql)translates SELECT/INSERT/UPDATE/DELETE to NQL/NEDB - Redis adapter —
RedisCompat(db).execute(cmd, *args)— 30+ commands - Auto-indexing —
AutoIndexDB(db, threshold=5)creates indexes from query patterns - nedbd — HTTP/JSON API + RESP2 wire protocol; multi-database, bearer auth, CORS, auto-checkpoint on shutdown
- Benchmark suite —
bench/benchmarks.pywith reproducible baselines inbench/RESULTS.md
Install
pip install nedb-engine # v0.6.0 — everything included
pip install --upgrade nedb-engine # upgrade from any earlier versionRequires: Python 3.8+ · cryptography>=41 (bundled — no separate install)
nedb-studio v0.3.2 — phpMyAdmin for NEDB
A Portal-powered workspace over the NEDB engine — prompt-to-database, then deploy, query, and edit in natural language. Live at studio.interchained.org
git clone https://github.com/aiassistsecure/nedb-studio
cd nedb-studio && npm install
PORT=3129 npm run build && PORT=3129 npm run startWhat's in v0.3.2
The two-way console
Ask a question → NQL → results. Ask to add/change a record → the model shows the exact row it would write (editable, with Regenerate and Cancel) → Commit → nedbd writes it. Merge semantics on update.
Language tabs: NQL / SQL / Redis
- NQL — native NEDB query language, full grammar
- SQL — SELECT translates to NQL and runs; INSERT/UPDATE/DELETE surface an editable preview
- Redis — HGETALL/KEYS/SMEMBERS translate to NQL and run
/databases — deploy and operate live databases
- Deploy a generated scaffold or sample database directly to a running nedbd
- Schema graph (persisted in
_studio/schemaon deploy, shown by default as the first tab) - Browse collections, verify hash-chain integrity, view the append-only log
- Connection snippets (curl, Python)
/settings — connection config
NEDB_URLenv-first with runtime override- Encryption status badge —
● AES-256-GCM(green) or● plaintext(amber) from nedbd health check - AiAssist gateway status and UI preferences
NL→write intent classification
The model classifies read vs write intent first. Writes preview the exact proposed row — all fields editable, Regenerate, Cancel — before anything is committed to the database.
Configuration
# .env
AIASSIST_API_KEY=your-key # AI generation + NL→NQL + NL→write
NEDB_URL=http://127.0.0.1:7070 # nedbd HTTP server
NEDB_TMK=<64-char hex> # optional: encryption status shown in Settings
PORT=3129 # studio app port (separate from VITE_PORT in dev)Architecture
The studio is a thin HTTP client of a running nedbd — it holds zero database state. All persistence, integrity, time-travel, and encryption live in the engine.
License
- nedb-engine — Apache-2.0
- nedb-studio — GPL-3.0-or-later
- sentinel-blocks — GPL-3.0-or-later
© 2026 INTERCHAINED, LLC — interchained.org · powered by AiAssist