01
HTTP interface
The service creates named databases and accepts document CRUD
requests. It generates UUIDv7 document IDs and exposes revisions
as ETags. Replacement, patch, and deletion can use
If-Match to reject a stale revision.
Patches support JSON Merge Patch and JSON Patch. The HTTP layer validates names and documents, limits body size, returns one JSON error shape, and shuts down gracefully.
02
Storage
Pebble holds every logical database in one ordered local store. Documents, database metadata, index definitions, and index entries occupy separate key ranges. Synchronous batches update a document and its indexes atomically and durably.
Striped locks coordinate concurrent document writes. Database deletion takes the corresponding database lock so that it cannot race with those writes.
03
Encryption
The server requires a base64-encoded 256-bit master key in a file outside the data directory. It derives a store wrapping key and uses it to protect a random key for each database.
Document JSON is compressed with Zstandard, then encrypted and authenticated with AES-256-GCM. Per-document keys are derived with HKDF-SHA256. Cursor keys are derived separately. The key and record layout authenticate stored data but do not hide database names, IDs, indexed values, or record sizes.
04
Indexes
Indexes are declared when a database is created. Each one maps a JSON Pointer path to ordered Pebble keys containing the encoded field value and document ID.
Equality accepts any JSON scalar. Ordered comparisons accept numbers and strings without coercion. Number encoding preserves exact JSON numeric order; strings use binary UTF-8 order. Query pages return document IDs and opaque authenticated cursors.
05
JSONPath queries
QUERY /{database} accepts RFC 9535 JSONPath in a JSON
request body. A document matches when any scalar selected by the
path satisfies the comparison.
A simple path uses a matching declared index when it is safe to do so. Other paths scan documents in ID order. Each page may scan at most 10,000 documents, each request has a five-second deadline, and path size, nesting, selectors, and evaluated document nodes are bounded.