Skip to content

Releases: aiassistsecure/nedb

NEDB v2.4.468 — Tri-Distribution Release

Choose a tag to compare

@ethgr0wth ethgr0wth released this 27 Jun 20:45
7042d20

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-db

What'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 under distributions/. The central codemagic.yaml builds all six macOS wheels and release-distros.yml publishes 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.node and assembles them alongside the Linux/Windows builds before npm publish — so crypto-database and aof-db install 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, package nedb-engine) had been stranded at 2.4.3 while 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-distros startup_failure. Three if: 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-v2 server-binary naming. The bundled daemon binary is uploaded un-suffixed to the shared release; per-distro naming would prevent a distro shipping another product's nedbd-v2.
  • Release-tool pre-flight. Add an npm/PyPI/crates name-availability (and "too-similar") check to scripts/release.py before a tag is spent.

NEDB v2.2.27 — Full Release Notes

Choose a tag to compare

@ethgr0wth ethgr0wth released this 22 Jun 14:37
5e9d232

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-v2 binary is found, and where it searched
  • Whether cargo is 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-v2

Installs 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.py

The 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: pypi job now depends on [node-binaries, create-release, wheels], downloads all platform binaries and _native extensions from GitHub release artifacts, and assembles a fat universal wheel before publishing.
  • Codemagic: uploads nedbd-v2-darwin-arm64 and nedbd-v2-darwin-x64 binaries to the GitHub release (in addition to .node files) so the pypi job can include them.
  • crates.io publish: new publish-crate CI job runs cargo publish --package nedb-core-v2 on every v* tag. Requires CARGO_REGISTRY_TOKEN Actions secret.
  • Secret sync: the nedb-release skill now syncs CARGO_REGISTRY_TOKEN alongside PYPI_API_TOKEN and NPM_TOKEN.

Install

# Python
pip install nedb-engine

# Node
npm install nedb-engine

# Rust (DAG server binary only)
cargo install nedb-core-v2

Windows 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 missing

Test

# 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.py

INTERCHAINED, LLC × Vex (Claude Sonnet 4.6)

NEDB v2.2.0 — Performance Sprint

Choose a tag to compare

@ethgr0wth ethgr0wth released this 17 Jun 20:59
4628df9

"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.0

What'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 a DashMap buffer 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 disk
  • list_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() extracts caused_by/valid_from/valid_to from doc for DAG provenance
  • link()/unlink() stored as __links__ docs for NQL TRAVERSE compatibility
  • flush() now calls flush_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 ⚠️ yes ✅ 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 line
  • perf(index): WAL write buffer — id_index.set() writes to DashMap, Rayon-parallel flush every 1s
  • perf(server): spawn_blocking for PUT — object file I/O runs on blocking thread pool
  • feat(bindings): PyO3 bindings → nedb_core_v2::Db — native Python DAG engine
  • feat(bindings): napi-rs bindings → nedb_core_v2::Db — native Node.js DAG engine
  • fix(startup): empty DB sets startup_ready = true immediately — eliminates 503 on first write
  • fix(compile): use std::sync::Arc in store/index/graph (missing import)

Links


© INTERCHAINED, LLC × Vex (Claude Sonnet 4.6)

NEDB v2.1.0 — Sprint Release

Choose a tag to compare

@ethgr0wth ethgr0wth released this 17 Jun 19:43
69ae9a1

"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.0

Start 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 via put_batch() — Rayon object + index writes
  • perf(db): decouple flush_manifest from write path — background ticker every 1s
  • feat(server): hourly clock-aligned checkpoint + SIGTERM/SIGINT graceful shutdown
  • feat(server): live query subscriptions — POST /v1/databases/:name/subscribe
  • feat(server): engine: "dag" field in /health response
  • fix(store): restore node.hash after deserialization — fixes TRACE and AS OF
  • fix(store): guard empty hash in ObjectStore::read() — no more panic on empty string
  • test: DAG correctness suite — 28 tests, auto-spawn, tests/test_dag.py
  • feat(studio): engine status badge (DAG ◆ / AOF) in Studio Nav bar

Links


© INTERCHAINED, LLC × Vex (Claude Sonnet 4.6)

NEDB v2.0.36 — Production Stable

Choose a tag to compare

@ethgr0wth ethgr0wth released this 17 Jun 15:54
85e05d1

Cross-platform native wheels shipping the nedbd-v2 Rust binary inside pip install nedb-engine. Linux, Windows, macOS arm64, and macOS x86_64 — all four platforms publish from a single v* 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 client

The 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 MANIFEST file and restores seq + Merkle head in 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 progress until the startup_ready gate flips.
  • Live event streamGET /events is 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 --dag startup 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 100000

CI 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 on ubuntu-latest. Runs the 266-test gate first, then publishes to PyPI via twine upload --skip-existing.
  • wheels — matrix-builds the maturin native wheel for x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. Each runner compiles the nedbd-v2 binary, 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 .node binaries.
  • node-binaries — napi-rs matrix on Linux + Windows; uploads each .node to the GitHub Release.
  • publish-npm — polls the GitHub Release until all four platform .node binaries are present (Linux + Windows from GitHub, macOS arm64 + x86_64 from Codemagic), then runs one npm publish with the complete bundle.
  • client-pypi / client-npm — publishes nedb-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 — builds nedbd-v2 for aarch64-apple-darwin, packages it inside the maturin wheel, publishes the wheel directly to PyPI, then builds the napi addon and uploads the .node to the GitHub Release.
  • macos-intel-wheel — cross-compiles nedbd-v2 for x86_64-apple-darwin from the M2 host (configuring CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER, CC_x86_64_apple_darwin, and SDKROOT to Xcode's clang + macOS SDK). Same flow as arm64 — wheel to PyPI, .node to 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...
Read more

NEDB v2.0.4 — The DAG Engine

Choose a tag to compare

@ethgr0wth ethgr0wth released this 16 Jun 19:15
95eb787

"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/data

The 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:

  1. Writes an immutable tombstone node (history preserved in the DAG forever)
  2. Removes the live id pointer from the index
  3. Doc is permanently invisible to all queries and list() — but fully recoverable via AS 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.4

Run 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/data

Automatic 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.aoflog.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=1os.execv into Rust v2 binary, no Python overhead
  • feat: Real tombstone deletes — tombstone node written, id removed from IdIndex
  • feat: Real BLAKE2b Merkle head on every response — tamper-evident, changes on every write
  • feat: DEK passthrough to migrate_if_needed() — encrypted v1 AOFs convert correctly
  • feat: nedbd-v2 binary bundled in native Python wheels (Linux / macOS / Windows)
  • fix: Dek derives Clone — fixes borrow-after-move in Db::open()
  • fix: Db.seq made pub — server can compute Merkle head
  • fix: base64_decode error type — Stringanyhow::Error via .map_err()
  • fix: parking_lot dep corrected to 0.12
  • chore: rust/nedb-v2 added to Rust workspace with Cargo.lock committed
  • chore: All 6 version strings bumped to 2.0.4 in sync
  • docs: index.html + reference.html updated for v2 DAG engine

What's Next

  • PyO3 + napi-rs bindings updated to v2 Db API (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=1 with 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

Choose a tag to compare

@ethgr0wth ethgr0wth released this 15 Jun 18:18
0a91395

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 automatically

Full 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 hash

Isolation 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 passed

Test 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.0

Changelog

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

Choose a tag to compare

@ethgr0wth ethgr0wth released this 15 Jun 00:09

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-engine

Supports 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 break verify().
  • 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 one fsync per 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: if verify() 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
    False with 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 Provenancecaused_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


Built by INTERCHAINED LLC × Claude Sonnet 4.6
Apache-2.0 (engine) · GPLv3 (studio)

NEDB v0.6.0 Release Notes

Pre-release

Choose a tag to compare

@ethgr0wth ethgr0wth released this 14 Jun 20:08

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" 0

What'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 -ERR with 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_TMK on 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
  • cryptography is now a bundled required dependency — encryption just works, no separate install

TMK sources (priority order)

  1. NEDB(path, tmk=bytes) — programmatic
  2. NEDB_TMK=<64-char hex> — environment variable
  3. NEDB_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-evident verify()
  • NQLFROM 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 adaptersql_exec(db, sql) translates SELECT/INSERT/UPDATE/DELETE to NQL/NEDB
  • Redis adapterRedisCompat(db).execute(cmd, *args) — 30+ commands
  • Auto-indexingAutoIndexDB(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 suitebench/benchmarks.py with reproducible baselines in bench/RESULTS.md

Install

pip install nedb-engine            # v0.6.0 — everything included
pip install --upgrade nedb-engine  # upgrade from any earlier version

Requires: 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 start

What'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/schema on 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_URL env-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