Local and remote access
ZeeDB supports an embedded Go API and a standalone HTTP server. The server owns the directory and shares its handle across requests. Applications use the same API locally and remotely; change the URL and token when deploying. Calling HTTP or running a compiled executable requires no Go toolchain. Building from source does.
This experimental single-node server adds no replication, automatic local/cloud synchronization, SQL, tenant isolation, or compare-and-swap. One token grants full database access. It is not a production-certified service.
Start locally
Build once from the repository root, generate a token, and start the binary:
go build -o zeedb.exe ./cmd/zeedb
$env:ZEEDB_TOKEN = [Guid]::NewGuid().ToString('N') + [Guid]::NewGuid().ToString('N')
.\zeedb.exe serve --data ./data --listen 127.0.0.1:8080
Copy the token securely into client environments before starting the server.
Keep it stable across restarts; generating another token changes credentials.
It must contain at least 32 bytes with no surrounding whitespace. Use a randomly
generated secret, kept out of source control. The token is read only from
ZEEDB_TOKEN, never a command-line argument. Stop with Ctrl+C (or Unix SIGTERM);
the server drains requests and closes the engine.
On Linux/macOS, build with go build -o zeedb ./cmd/zeedb, generate a token with
export ZEEDB_TOKEN="$(openssl rand -hex 32)", and run ./zeedb serve --data ./data.
The binaries GitHub Actions workflow builds downloadable development artifacts
for Windows, Linux, and macOS when manually dispatched or triggered by a version
tag. These are not signed or published package releases.
Use HTTP while the server is running; the local CLI cannot open its directory. Separate local and hosted directories are separate databases.
First request
In another PowerShell terminal, set the same token and run:
$headers = @{ Authorization = "Bearer $env:ZEEDB_TOKEN" }
$body = @{
collection = 'users'
key = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('1024'))
value = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('{"name":"Ada"}'))
} | ConvertTo-Json
Invoke-RestMethod http://127.0.0.1:8080/v1/put -Method Post -Headers $headers -ContentType application/json -Body $body
Invoke-RestMethod http://127.0.0.1:8080/v1/get -Method Post -Headers $headers -ContentType application/json -Body $body
API contract
Every route requires Authorization: Bearer YOUR_TOKEN. POST requires
Content-Type: application/json, one JSON object, and no unknown fields.
Byte fields use standard padded Base64. Collections are JSON strings.
Versions and the stats sequence are decimal strings to preserve uint64
precision in JavaScript. Empty values are allowed and returned as "".
| Method | Path | Request / response |
|---|---|---|
| GET | /v1/health | Handle readiness; {"status":"ok"} |
| GET | /v1/stats | Engine counters; sequence is a decimal string |
| POST | /v1/put | {collection,key,value}; returns {"ok":true} |
| POST | /v1/get | {collection,key}; returns {key,value,version} |
| POST | /v1/delete | {collection,key}; idempotent; returns {"ok":true} |
| POST | /v1/scan | {collection,start?,end?,limit?}; returns {"records":[...]} |
| POST | /v1/batch | {"mutations":[{collection,key,value?,delete?},...]}; atomic; returns {"ok":true} |
| POST | /v1/flush | {}; synchronous checkpoint; returns {"ok":true} |
For example, {"collection":"users","key":"MTAyNA=="} reads key 1024.
A response could be {"key":"MTAyNA==","value":"eyJuYW1lIjoiQWRhIn0=","version":"1"}.
Scan start is inclusive, end exclusive. Omit end or send null for unbounded;
"end":"" is an explicit empty upper bound. Limit 0/omitted uses the engine
maximum of 1000. No collection-listing endpoint, SQL, or opaque pagination token
is supplied. See pagination for ordered key boundaries.
Each call sees one consistent state; successive calls can observe other writes.
Put does not return a version; a separate get could observe another writer.
Bodies are capped at 4 MiB. Default durable engine budgets also apply: 1 MiB encoded batches, 4096 operations, 4 KiB keys, roughly 1 MiB values, 256 MiB live data accounting, and 1000 records / 8 MiB accounting per scan. No custom budget or relaxed-durability server flags are supplied yet.
Errors and retries
API errors have the shape {"error":{"code":"not_found","message":"record not found"}}.
Unknown routes and method mismatches use Go’s plain-text HTTP 404/405 responses.
Transport failures may have no JSON body; clients must inspect status/content type.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_json, invalid_argument | Malformed body/Base64, unknown fields, invalid arguments |
| 401 | unauthorized | Missing or wrong token |
| 403 | origin_not_allowed | Direct browser request with Origin |
| 404 | not_found | Record absent or deleted |
| 408 | timeout | Context deadline or cancellation |
| 413 | body_too_large | Body exceeds 4 MiB |
| 415 | content_type | Missing or unsupported content type |
| 422 | resource_limit | Engine budget exceeded |
| 503 | busy, unavailable | Request capacity exhausted or engine failure |
At most 64 authenticated requests are admitted concurrently. The operation
context lasts 30 seconds; header/read/write timeouts are 5/35/40 seconds.
Deadlines cannot interrupt every lock/filesystem call or roll back a WAL commit.
A timeout, disconnect, or failure can leave a write outcome unknown. Reconcile
through reads before retrying; no idempotency keys or exactly-once delivery are
implemented. busy is returned before operation execution.
JavaScript and Python
Set ZEEDB_URL (default http://127.0.0.1:8080) and the same ZEEDB_TOKEN, then:
node examples/http/client.mjs
python examples/http/client.py
These complete, dependency-free examples in the repository write sample records
to http-examples, read them, decode Base64, and scan. They are integration
examples, not published npm/PyPI SDKs. Other languages call the same HTTP API.
Keep the full-access token in trusted backends, never browser bundles or public mobile apps. Browser code calls your backend, which authorizes users before accessing ZeeDB. Requests carrying Origin are rejected and no CORS is enabled.
Docker and online hosting
The root Dockerfile builds a static executable and runs it as UID/GID 65532,
with no Go toolchain in the final image. Set ZEEDB_TOKEN and run from the root:
docker compose up --build -d
docker compose logs zeedb
docker compose down
Compose publishes only 127.0.0.1:8080, with a named volume at /data.
down retains data; down -v deletes the volume. Bind-mounted directories
must be writable by UID 65532. Stop the server before copying its whole directory
for a backup; flush alone does not make a live copy coherent. See
offline backups.
Deploy on a VPS or container host with persistent local storage, one replica per directory. Keep the API private behind an HTTPS reverse proxy and firewall, with request/rate limits. Separate proxy/database containers connect over a private container network; localhost is local to each container. Never log tokens. Cloudflare Pages hosts the documentation, not this stateful server. Ephemeral serverless disk and shared/network volumes are unsuitable storage targets.
Alternatively, the binary supports direct TLS:
./zeedb serve --data /srv/zeedb --listen 0.0.0.0:8443 --tls-cert /secrets/fullchain.pem --tls-key /secrets/privkey.pem
Provide both PEM files and arrange certificate renewal/restart externally.
Plain public HTTP exposes tokens and data. For hosted clients, set ZEEDB_URL
to your HTTPS origin and obtain credentials through the host’s secret manager.
Token rotation requires a restart; no overlapping tokens or live rotation exist.
Test storage/deployment limits, backup/restore, and resource headroom for your workload. Authentication/TLS do not establish audited security, production readiness, tenant isolation, replication, or managed backups.