NEDB speaks three protocols natively: its own HTTP/JSON, PostgreSQL's wire protocol, and RESP2. The point of the last two is that your existing tools already speak them — no bespoke client, no driver to write.
HTTP/JSON — the native surface
The daemon (nedbd) serves /v1/databases/*: put/get/query/tx (atomic
CAS transactions), TTL, indexes, relations, /verify, Merkle proofs, the
Mongo-compatible route, /cast, and /events (SSE). Optional bearer
token via NEDBD_TOKEN. Binds loopback by default since v2.2.31 — a hardening fix;
expose deliberately or tunnel.
PostgreSQL wire protocol
nedbd --pg-port 5433 opens a real pgwire endpoint — reads and writes.
psql, DBeaver, Metabase, Grafana, psycopg, asyncpg and JDBC all work against a
tamper-evident store with ordinary SQL:
UPDATE orders SET total = 999 WHERE _id = 'o1';
SELECT total FROM orders WHERE _id = 'o1'; -- 999
SELECT total FROM orders AS OF SYSTEM TIME 0 WHERE _id = 'o1'; -- 120, forever
Both wire protocols are implemented — simple (psql, psycopg2) and extended
Parse/Bind/Describe/Execute (psycopg3,
asyncpg, JDBC). Until the extended protocol landed, those frameworks could not run a single
query — psycopg3 hung, asyncpg refused. Parameters arrive in text and binary format; a
row-capped Execute suspends its portal so a JDBC setFetchSize pages
instead of stalling.
Typing without a schema: the type is sampled from the documents already stored — the
data is the catalogue. Placeholders in clause positions (AS OF SYSTEM TIME $1)
are typed by the grammar; aggregates by meaning (COUNT integer, AVG
fractional). A driver that declares types is believed; only unspecified slots are inferred.
Introspection is real SQL: pg_catalog and information_schema
are queryable relations synthesised from the live database — \dt is two
LEFT JOINs and a nine-branch CASE; \d orders is a regex
plus three correlated subqueries. Every psql 17 backslash-describe command exits 0
(tests/test_psql_introspection.py drives the real binary, psql 16 and 17); three
exit 1 exactly as on fresh Postgres. Derived values are honest: sizes are blank cells, never
invented numbers.
Limits, named: SQL-level cursors (DECLARE/FETCH) are
refused by name, not hung. A column with disagreeing stored types advertises
text. The connection is cleartext — the endpoint is off without
--pg-port, loopback by default; NEDBD_PG_READ_ONLY=1 for
never-mutate deployments. Verified by 64 checks in tests/test_pgwire.py driven
through psycopg2 — which is libpq.
RESP2
NEDBD_RESP2_PORT=6380 nedbd also speaks RESP2 — redis-cli and
redis-benchmark work unchanged, with per-database selection
(SELECT shop) and neSQL queries through EVAL:
redis-cli -p 6380 SELECT shop
redis-cli -p 6380 EVAL 'FROM users AS OF 10 WHERE status = "active"' 0
redis-cli -p 6380 EVAL 'FROM beliefs TRACE caused_by' 0
tools/build_docs_site.py.