jaydb Docs v1.0
GitHub Console
JayDB / Documentation

jaydb Documentation

Ship apps with no backend at all. Your app is a folder of static files; jaydb is both the login and the database, reached straight from the browser.

Introduction

jaydb removes the backend from app development. An app ships as static files, and jaydb provides the two things those files cannot provide themselves: real per-user sign-in and a shared database. There is no server of your own to run — not a serverless function, not a BaaS SDK, no API tier, no auth service, no session store.

What normally sits between a static frontend and its data — an auth service, an API tier, a session store, an ORM, a connection pool, and a deploy pipeline for all of it — is not made cheaper here. It is deleted. Each end user signs in with their own identity through a per-tenant OIDC issuer, so the browser talks to the data plane directly with a token scoped to that user, and nothing confidential ships to the front end.

This is not "serverless functions"

Serverless functions still boot a server per request — one that you write, deploy, and version. jaydb removes that layer entirely. The only always-on system is jaydb, which you do not operate.

Two ways to build

Both talk to the same data plane. The difference is whether a server of yours is involved at all.

Shape Credential When
Frontend-only (the default) Authorization: Bearer <jwt> Static files on any host. Users sign in with OIDC + PKCE; the browser gets its own token, scoped to that user.
Server-side integration X-JayDB-API-Key A backend, script, or job holds a long-lived secret. Optional — never required to start.

See Authentication for both flows in full.

Underneath: the database itself

jaydb is a high-performance document database written in Go. It uses AWS S3 (or any S3-compatible object storage such as Cloudflare R2, MinIO, or Wasabi) as its durability layer, with sub-millisecond in-memory sharded caching, atomic Compare-and-Swap (CAS) locking, and singleflight read coalescing. One key holds one JSON document; you read it, write it, or compare-and-swap it. That engine is open source and MIT licensed, and can be self-hosted.

Readable by agents, too

Every API action is plain HTTP with standard headers — no client SDK, no connection string. That also makes jaydb a natural target when an AI agent scaffolds an app that needs shared state: a namespace is created on first write, so there is no provisioning turn to burn. Agents can read the whole surface from one fetch at /api.md or /llms.txt.

5-Minute Quickstart

You can interact with JayDB Cloud using standard HTTP requests (with curl or native fetch), or use the official zero-dependency Frontend SDK (@jaydb/cloud) for automated OIDC token management and typed CAS primitives. For a full, runnable application demonstrating user sign-in and real-time synchronization, see the Kanban Demo App below.

1. Write a document

Write any JSON document to a hierarchical path. If the namespace does not exist, it is created implicitly on first write.

curl -X PUT https://acme.jaydb.com/v1/n/app/docs/users/101 \
  -H "X-JayDB-API-Key: $JAYDB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "role": "admin", "plan": "pro"}'
const res = await fetch('https://acme.jaydb.com/v1/n/app/docs/users/101', {
  method: 'PUT',
  headers: {
    'X-JayDB-API-Key': process.env.JAYDB_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: 'Alice', role: 'admin', plan: 'pro' }),
});
const meta = await res.json();
console.log('Saved with ETag:', meta.etag);
import os, requests

res = requests.put(
    "https://acme.jaydb.com/v1/n/app/docs/users/101",
    headers={"X-JayDB-API-Key": os.environ["JAYDB_KEY"]},
    json={"name": "Alice", "role": "admin", "plan": "pro"}
)
meta = res.json()
print(f"Saved with ETag: {meta['etag']}")
package main

import (
	"bytes"
	"net/http"
	"os"
)

func main() {
	body := []byte(`{"name":"Alice","role":"admin","plan":"pro"}`)
	req, _ := http.NewRequest("PUT", "https://acme.jaydb.com/v1/n/app/docs/users/101", bytes.NewReader(body))
	req.Header.Set("X-JayDB-API-Key", os.Getenv("JAYDB_KEY"))
	req.Header.Set("Content-Type", "application/json")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
}

2. Read a document

Retrieve the document and its current version ETag header:

curl -i https://acme.jaydb.com/v1/n/app/docs/users/101 \
  -H "X-JayDB-API-Key: $JAYDB_KEY"
const res = await fetch('https://acme.jaydb.com/v1/n/app/docs/users/101', {
  headers: { 'X-JayDB-API-Key': process.env.JAYDB_KEY },
});
const etag = res.headers.get('ETag');
const user = await res.json();
console.log(user, 'ETag:', etag);
res = requests.get(
    "https://acme.jaydb.com/v1/n/app/docs/users/101",
    headers={"X-JayDB-API-Key": os.environ["JAYDB_KEY"]}
)
etag = res.headers.get("ETag")
doc = res.json()
print(doc, "ETag:", etag)
req, _ := http.NewRequest("GET", "https://acme.jaydb.com/v1/n/app/docs/users/101", nil)
req.Header.Set("X-JayDB-API-Key", os.Getenv("JAYDB_KEY"))
resp, _ := http.DefaultClient.Do(req)
etag := resp.Header.Get("ETag")
defer resp.Body.Close()

Frontend SDK (@jaydb/cloud)

While JayDB Cloud can be queried directly with native fetch() or curl, we provide an official, zero-dependency client SDK—@jaydb/cloud. It gives frontend applications typed document access, OIDC + PKCE browser authentication, automatic token refresh, optimistic concurrency control (CAS), and structured error handling in a lightweight bundle (~12 KB minified).

SDK Documentation & Live Playground

Visit the standalone Interactive SDK Documentation & Playground ↗ or view the source on GitHub: jaydb-cloud/jaydb-cloud-sdk ↗.

Installation

Install from npm or use a native browser <script type="importmap"> without any bundler or build step:

Option A: npm package
npm install @jaydb/cloud
Option B: Zero-build Browser Import Map (GitHub Pages CDN)
<script type="importmap">
{
  "imports": {
    "@jaydb/cloud": "https://jaydb-cloud.github.io/jaydb-cloud-sdk/jaydb-cloud.esm.min.js"
  }
}
</script>

1. Authentication with OIDC + PKCE

The Auth class encapsulates the entire OAuth 2.0 / OIDC Authorization Code Flow with PKCE. Tokens and cryptographic verifiers are securely kept in client storage (defaults to sessionStorage) with zero client secrets in browser code:

auth.js — Browser OIDC sign-in with PKCE
import { Auth } from '@jaydb/cloud';

const auth = new Auth({
  tenant: 'acme',                                // acme.jaydb.com
  clientId: 'app_kanban',                         // Registered public PKCE client
  redirectUri: window.location.origin + '/callback',
  scopes: ['openid', 'profile', 'email'],
});

// Initiate login redirect if not signed in
if (!auth.isSignedIn()) {
  auth.signIn();
}

// On callback page: exchange authorization code for user token
await auth.handleCallback();
const user = auth.getUser();
console.log('Signed in as:', user.email);

2. Initializing JayDB Client

Pass the auth instance directly into JayDB. Every document operation automatically resolves a valid Bearer token (coalescing requests and refreshing silently in the background):

db.js — Client initialization
import { JayDB, Auth } from '@jaydb/cloud';

const auth = new Auth({ tenant: 'acme', clientId: 'app_kanban' });
const db = new JayDB({
  tenant: 'acme',
  namespace: 'app',
  auth, // or custom getToken: async () => accessToken
});

3. CRUD Operations & CAS Optimistic Concurrency

All write operations natively support compare-and-swap (CAS) via { ifMatch: etag } and create-only locks via { createOnly: true }. When a conflict occurs, the SDK throws a structured ConflictError:

crud.js — Document writes with CAS
import { ConflictError, NotFoundError } from '@jaydb/cloud';

// 1. Create a document safely (fails if key already exists)
const created = await db.put('boards/main', { title: 'Kanban' }, { createOnly: true });
console.log('Created with ETag:', created.etag);

// 2. Read document
const doc = await db.get('boards/main');
if (!doc) throw new NotFoundError('board missing');

// 3. Update with optimistic concurrency control
try {
  const updated = await db.put(
    'boards/main',
    { ...doc.data, title: 'Sprint Board' },
    { ifMatch: doc.etag } // Guarantees no overwrite if another user modified it
  );
} catch (err) {
  if (err instanceof ConflictError) {
    console.warn('Conflict detected! Re-fetch latest and retry.');
  }
}

// 4. List documents under a prefix
const { keys, prefixes } = await db.list({ prefix: 'boards/' });

// 5. Delete with version guard
await db.delete('boards/main', { ifMatch: doc.etag });

Error Hierarchy

The SDK provides explicit error types so application logic can cleanly distinguish between network, precondition, and auth issues:

  • JayDBError: Base error class with status, statusText, and response details.
  • ConflictError: Thrown on HTTP 412 (Precondition Failed) when an ifMatch or createOnly check fails.
  • NotFoundError: Thrown or returned on HTTP 404.
  • AuthError: Thrown on HTTP 401 or 403 authorization failures.

Example: Kanban Demo App

Looking for a complete, production-ready usage example? The JayDB Kanban Demo is a real-time, multi-user Kanban board built with zero backend servers and powered by the @jaydb/cloud SDK. It showcases how a static frontend uses JayDB Cloud for both browser login and direct document storage.

Try the live demo

Open jaydb-cloud.github.io/jaydb-kanban-demo in two browser windows side-by-side to watch cards, moves, edits, and active presence sync in real time.

What the demo illustrates

  • Zero backend servers: Pure static frontend (HTML, CSS, vanilla JS) deployed on GitHub Pages. No Node.js, serverless lambdas, or backend proxies are involved.
  • Browser sign-in with OIDC + PKCE: Managed via Auth from @jaydb/cloud. Users authenticate via Google or GitHub against the tenant's OIDC issuer. Scoped access tokens are stored in sessionStorage and refreshed silently.
  • Direct browser-to-database requests: Every card creation, update, and deletion is performed directly through JayDB client targeting https://{tenant}.jaydb.com/v1/n/{namespace}/docs/{key} with Bearer tokens.
  • Optimistic concurrency (CAS): Moving cards or editing text uses db.put(key, data, { ifMatch: etag }). When two users modify a card simultaneously, the write throws a typed ConflictError (HTTP 412), prompting the store to re-read the winning version and replay the change conflict-free.
  • Presence and live synchronization: Instead of heavyweight WebSocket servers, the client queries key prefixes with db.list({ prefix: 'cards/' }) and polls at a light cadence, diffing ETags to re-render only modified items.
  • Zero-build distribution: Native ES module import via <script type="importmap"> resolving @jaydb/cloud directly from the CDN with no build tools or package managers required.
store.js — CAS optimistic concurrency with @jaydb/cloud
import { ConflictError } from '@jaydb/cloud';

// Read-modify-write with bounded retry using CAS primitives
async function mutateCard(id, mutate, { retries = 4 } = {}) {
  let record = cards.get(id) ?? (await fetchCard(id));

  for (let attempt = 0; ; attempt++) {
    const next = {
      ...mutate(structuredClone(record.data)),
      updatedAt: new Date().toISOString(),
    };

    try {
      // Atomic conditional PUT guarded by ETag
      const written = await db.put(`boards/main/cards/${id}`, next, {
        ifMatch: record.etag, // Throws ConflictError (412) if another user saved first
      });
      cards.set(id, { id, data: next, etag: written.etag });
      return { ok: true };
    } catch (error) {
      if (!(error instanceof ConflictError) || attempt >= retries) throw error;

      // Race lost: re-fetch winning version and replay the mutation
      record = await fetchCard(id);
    }
  }
}

Explore the full repository on GitHub: github.com/jaydb-cloud/jaydb-kanban-demo.

Architecture Overview

JayDB uses a tiered, decoupled architecture where storage durability is handled by cold object storage, and low latency + concurrency are guaranteed by in-memory ownership rings and QUIC streams.

System Architecture
+-------------------------------------------------------------------------+
|                              SERVER MODE                                |
|  - fasthttp RESTful HTTP API (GET / PUT / DELETE / LIST)                |
|  - Memberlist Gossip Discovery (SWIM Protocol)                          |
|  - Lexicographical Partition Ring (Deterministic Owner Node)            |
|  - Multiplexed QUIC Connection Mesh (Sub-ms Inter-Query Execution)      |
+-------------------------------------------------------------------------+
                                    |
                                    v (Internal Core)
+---------------------------------------------------------------------------+
|                             EMBEDDED MODE                                 |
|                         (Core Engine Library)                             |
|                                                                           |
|  +---------------------------------------------------------------------+  |
|  | High-Level Go API (Get, Put, Delete, List)                          |  |
|  +---------------------------------------------------------------------+  |
|  | Key-Level Mutex & Singleflight Cache Manager                        |  |
|  |   - Flawless Multi-Node Consistency via Owner Node Routing          |  |
|  |   - Read Coalescing (1 S3 GET for concurrent readers)               |  |
|  +---------------------------------------------------------------------+  |
|  | Pluggable Codec (JSON default, MsgPack, Raw)                        |  |
|  +---------------------------------------------------------------------+  |
|  | Cold Storage Driver Interface (S3 Driver + FS Driver + Mem Driver)  |  |
+---------------------------------------------------------------------------+

Core Architectural Components

  • Authoritative Key Ownership: Keys are partitioned by path prefix across cluster nodes using a lexicographical ring. Requests for a key always route directly to the single owner node.
  • Singleflight Read Coalescing: When 100 concurrent requests hit a cold key, JayDB holds all 100 requests in-flight, executes a single S3 GetObject, and distributes the result to all callers.
  • Atomic Optimistic Locking (CAS): Updates pass S3 If-Match / If-None-Match headers down to the storage layer, ensuring no update is lost even across distributed instances.

JayDB vs Traditional Databases

Why choose JayDB over Postgres, MongoDB, or DynamoDB for modern services and agent applications?

Capability Traditional Managed DB (Postgres / Mongo) DynamoDB / Cloud KV JayDB (S3-Backed Document DB)
Monthly Base Cost $15 - $50+/month floor $0 base + high per-request fees < $0.32/month on S3 (or $0.00 dev)
Infrastructure Ops Provisioning, scaling, replicas, upgrades IAM policies, partition keys, WCU/RCU Zero Ops (100% serverless on S3)
Local Dev Setup Docker, background daemons, migrations Local emulator container Zero Dependency (`memory` or `fs` driver)
Concurrency Control Row locks, table locks, connection pools Conditional expressions Built-in ETag CAS & Singleflight
Data Model Strict DDL tables / BSON collections Key-attribute items Hierarchical Document Paths (`a/b/c`)

Hierarchical Document Model

JayDB organizes data as a tree of document paths inside isolated namespaces.

A document key is a /-delimited path (e.g. users/101/profile, teams/engineering/projects/jaydb).

Path Hierarchy Rules

- Paths cannot contain leading or trailing slashes (e.g. use users/101, not /users/101/).
- Keys cannot contain consecutive slashes (//) or .. traversals.
- Namespaces partition your data entirely. Two documents with the same key in different namespaces are completely independent.

Compare-and-Swap (CAS)

JayDB uses optimistic concurrency control (OCC) powered by standard HTTP ETag headers. Every document write calculates and returns a new ETag hash.

Guarded Operations

  • Create Only (If-None-Match: *): The write succeeds only if the key does NOT currently exist. If it exists, returns 412 Precondition Failed.
  • Conditional Update (If-Match: "<etag>"): The write succeeds only if the document's current ETag matches what you provide. If another writer updated it first, returns 412 Precondition Failed.
  • Unconditional Write (No headers): Overwrites the key regardless of current version.
Conditional Write with cURL
curl -X PUT https://acme.jaydb.com/v1/n/app/docs/users/101 \
  -H "X-JayDB-API-Key: $JAYDB_KEY" \
  -H 'If-Match: "3f9a1c72"' \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "role": "admin"}'

Read-Modify-Write Pattern

To safely mutate documents under concurrent updates without lost writes, use the standard read-modify-write retry loop:

const BASE = 'https://acme.jaydb.com/v1/n/app/docs';
const headers = { 'X-JayDB-API-Key': process.env.JAYDB_KEY };

async function update(key, mutateFn, maxRetries = 5) {
  for (let i = 0; i < maxRetries; i++) {
    const getRes = await fetch(`${BASE}/${key}`, { headers });
    const doc = getRes.ok ? await getRes.json() : {};

    const guard = getRes.ok
      ? { 'If-Match': getRes.headers.get('ETag') }
      : { 'If-None-Match': '*' };

    const putRes = await fetch(`${BASE}/${key}`, {
      method: 'PUT',
      headers: { ...headers, ...guard, 'Content-Type': 'application/json' },
      body: JSON.stringify(mutateFn(doc)),
    });

    if (putRes.ok) return putRes.json();
    if (putRes.status !== 412) throw new Error(await putRes.text());
  }
  throw new Error(`Too much contention updating ${key}`);
}
import os, requests

BASE = "https://acme.jaydb.com/v1/n/app/docs"
headers = {"X-JayDB-API-Key": os.environ["JAYDB_KEY"]}

def update(key, mutate_fn, max_retries=5):
    for _ in range(max_retries):
        r = requests.get(f"{BASE}/{key}", headers=headers)
        doc = r.json() if r.status_code == 200 else {}
        
        guard = {"If-Match": r.headers.get("ETag")} if r.status_code == 200 else {"If-None-Match": "*"}
        
        put_r = requests.put(
            f"{BASE}/{key}",
            headers={**headers, **guard},
            json=mutate_fn(doc)
        )
        if put_r.status_code == 200:
            return put_r.json()
        if put_r.status_code != 412:
            put_r.raise_for_status()
    raise RuntimeError(f"High contention on {key}")
// In embedded Go mode, use db.WithExpectedETag or db.CreateOnly
meta, err := database.Put(ctx, "users/101", user, db.WithExpectedETag(readMeta.ETag))
if errors.Is(err, db.ErrPreconditionFailed) {
    // Re-read and retry
}
Real-world CAS in action

The Kanban Demo App relies on this exact pattern to handle concurrent card movements and collaborative updates conflict-free straight from the browser.

Singleflight Coalescing

In traditional databases and naive S3 integrations, a traffic spike on a single document can result in hundreds of duplicate reads hitting storage.

JayDB incorporates a key-level singleflight coalescing engine. If 1,000 requests for pricing/plans arrive simultaneously while the cache is cold, exactly one S3 GetObject request is dispatched. The other 999 callers await the same in-flight result, eliminating cache stampedes and keeping your S3 bill negligible.

HTTP REST API Reference

The jaydb REST API is lightweight, fast, and completely stateless.

  • Base URL: https://{tenant}.jaydb.com
  • Prefix: /v1/n/{namespace}/docs
  • Authentication: one of the two credentials below on every request

Authentication

The data plane accepts two credentials. Which one you use depends on whether a server of yours is involved at all.

Header Identity Use it for
Authorization: Bearer <jwt> The signed-in end user Frontend-only browser apps. Minted by the tenant's own OIDC issuer.
X-JayDB-API-Key The organization / namespace Server-side and REST integrations, where the secret stays server-side.
Do not ship an API key in a static frontend

An API key is scoped to an organization and namespace, not to a single user, and carries no read/write distinction. Anyone who views source gets full read-write over that namespace. Use the bearer path for anything a browser loads.

Sign-in from the browser (OIDC + PKCE)

Every organization gets its own OIDC issuer at https://{tenant}.jaydb.com, publishing standard discovery metadata and a JWKS under that issuer — so tokens are verifiable with any standard OIDC library. Browser clients use Authorization Code + PKCE: no client secret ships to the browser, because the PKCE proof key replaces it.

Register the app first in the console (App Registrations) as a public PKCE client with its client ID, redirect URIs, and allowed origins. Registering the app is what grants its origin cross-origin access to the data plane — there is no operator-managed allowlist to edit. Org admins separately choose which upstream identity providers are offered (Identity Providers): Google, GitHub, Microsoft, any generic OIDC provider, or a custom OAuth2 provider.

For turnkey browser authentication with automated PKCE flow and silent token refresh, use the official Frontend SDK (@jaydb/cloud), or inspect the Kanban Demo App (source code, live demo).

1. Redirect the user to the authorize endpoint

Authorize request
GET https://acme.jaydb.com/oauth/v2/authorize
  ?response_type=code
  &client_id=my-app
  &redirect_uri=https://myapp.example/callback
  &scope=openid%20profile%20email
  &code_challenge=<S256 challenge>
  &code_challenge_method=S256

2. Exchange the returned code for a token

Token request
POST /oauth/v2/token
Host: acme.jaydb.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<code from the redirect>
&client_id=my-app
&redirect_uri=https://myapp.example/callback
&code_verifier=<the original verifier>

3. Call the data plane with the token

Authenticated read
const res = await fetch(
  'https://acme.jaydb.com/v1/n/app/docs/users/101',
  { headers: { 'Authorization': `Bearer ${accessToken}` } }
);
const etag = res.headers.get('ETag'); // exposed cross-origin
const doc = await res.json();
Browser notes

The ETag response header is exposed cross-origin, so browser JavaScript can read it and use it for compare-and-swap. There is no SSE or websocket — clients poll, and because list returns a per-key etag, a poll loop can diff and re-read only what changed.

The endpoint examples below show X-JayDB-API-Key for brevity. Substitute Authorization: Bearer <jwt> unchanged wherever a browser app is the caller.

Read Document

GET /v1/n/{namespace}/docs/{key}

Fetches the JSON document stored at {key}.

Header Type Description
Authorization
X-JayDB-API-Key
string Required — one of the two. Bearer <jwt> for a signed-in browser user, or your secret API key server-side. See Authentication.
Response: 200 OK
HTTP/1.1 200 OK
ETag: "3f9a1c72"
Content-Type: application/json

{
  "name": "Alice",
  "role": "admin"
}

Write & CAS Update

PUT /v1/n/{namespace}/docs/{key}

Creates or updates the document at {key} with the supplied JSON body.

Header Type Description
Content-Type string Must be application/json.
If-Match string Optional. ETag from previous read for compare-and-swap update.
If-None-Match string Optional. Set to * to ensure the document is created only if it does not already exist.
Response: 200 OK
HTTP/1.1 200 OK
ETag: "8c1e04b5"
Content-Type: application/json

{
  "status": "ok",
  "key": "users/101",
  "etag": "8c1e04b5",
  "mod_time": "2026-08-28T01:00:00Z"
}

Delete Document

DELETE /v1/n/{namespace}/docs/{key}

Removes the document at {key}. Deletes are never billed.

List Keys & Prefix Scan

GET /v1/n/{namespace}/docs?list&prefix={prefix}&limit={n}&cursor={cursor}

Lists document metadata matching an optional key path prefix.

Query Parameter Type Description
list flag Required. Flag indicating listing query.
prefix string Optional. Key prefix filter (e.g. users/).
limit number Optional. Max items to return (default 100, max 1000).
cursor string Optional. Opaque pagination token returned in previous response next_cursor.
Response: 200 OK
{
  "items": [
    { "key": "users/101", "etag": "8c1e04b5", "mod_time": "2026-08-28T01:00:00Z", "size": 1204 },
    { "key": "users/102", "etag": "1a77de90", "mod_time": "2026-08-28T01:00:00Z", "size": 980 }
  ],
  "next_cursor": "dXNlcnMvMTAy"
}

Status Codes & Errors

HTTP Code Meaning Action
200 OK Operation succeeded Normal response.
400 Bad Request Invalid path, malformed JSON body, or invalid query parameter Check payload structure.
401 Unauthorized Missing or invalid X-JayDB-API-Key Verify your API key.
403 Forbidden Request did not target a valid tenant subdomain Ensure using {tenant}.jaydb.com.
404 Not Found Key does not exist Key is missing or deleted.
412 Precondition Failed ETag mismatch on If-Match or existing key on If-None-Match Re-read document and retry CAS update.
413 Payload Too Large Request body exceeded maximum document size limit Keep documents under 5MB.

Go Library & Embedded Engine

JayDB can be imported as a pure Go library for direct in-process database execution without any network hops.

go get github.com/avivklas/jaydb
main.go (Embedded Example)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/avivklas/jaydb/pkg/db"
	"github.com/avivklas/jaydb/pkg/storage/s3"
)

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func main() {
	ctx := context.Background()

	// 1. Initialize S3 Driver
	driver, err := s3.NewDriver(s3.Config{
		Bucket: "my-app-documents",
		Region: "us-east-1",
	})
	if err != nil {
		log.Fatal(err)
	}

	// 2. Open Embedded JayDB Instance
	database, err := db.Open(db.Options{
		Storage:       driver,
		ShardingDepth: 2, // Key prefix shard depth (e.g. "users/101")
	})
	if err != nil {
		log.Fatal(err)
	}
	defer database.Close()

	// 3. Create Document
	user := User{Name: "Alice", Email: "alice@example.com"}
	meta, err := database.Put(ctx, "users/101", user, db.CreateOnly())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Created user ETag:", meta.ETag)

	// 4. Read Document
	var fetched User
	readMeta, err := database.Get(ctx, "users/101", &fetched)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Fetched user: %+v (ETag: %s)\n", fetched, readMeta.ETag)
}

Storage Drivers

JayDB includes pluggable storage backends implementing the storage.Driver interface:

  • S3 Driver (pkg/storage/s3): Primary cold storage driver. Works with AWS S3, Cloudflare R2, MinIO, and Wasabi.
  • Filesystem Driver (pkg/storage/fs): Stores documents on local disk. Ideal for single-node appliances and local testing.
  • In-Memory Driver (pkg/storage/memory): Ephemeral in-memory storage for unit tests. Zero external dependencies.

Cache & Sharding Depth

ShardingDepth controls how key paths are hashed into partition rings. For example, with ShardingDepth: 2, a path like orgs/acme/projects/1 is partitioned by the prefix orgs/acme.

Clustering & High Availability

JayDB instances coordinate as a peer-to-peer cluster using SWIM Memberlist Gossip and long-lived multiplexed QUIC streams.

Lexicographical Ring & QUIC Mesh

  • Node Discovery: Nodes join via gossip (JoinAddrs). Node failure detection happens automatically via SWIM protocol.
  • Deterministic Key Ownership: A lexicographical consistent hash ring routes document requests to the responsible node.
  • Sub-Millisecond QUIC Mesh: When a node receives a query for a key owned by another peer, it forwards the query across an established QUIC connection, eliminating TLS handshake overhead.
Cluster Node Setup in Go
// Initialize Node 1 (Seed Node)
node1, _ := cluster.NewNode(cluster.NodeConfig{
    NodeName:  "node-1",
    BindAddr:  "127.0.0.1",
    BindPort:  19001,
    QuicPort:  19002,
    Ring:      ring,
    DBHandler: dbInstance1,
})

// Initialize Node 2 (Joins Node 1)
node2, _ := cluster.NewNode(cluster.NodeConfig{
    NodeName:  "node-2",
    BindAddr:  "127.0.0.1",
    BindPort:  19003,
    QuicPort:  19004,
    JoinAddrs: []string{"127.0.0.1:19001"},
    Ring:      ring,
    DBHandler: dbInstance2,
})

Observability & Metrics

JayDB exposes native Prometheus metrics out-of-the-box on GET /metrics.

Metric Name Type Description
jaydb_cache_hits_total Counter Total in-memory cache hits.
jaydb_cache_misses_total Counter Cache misses requiring cold storage read.
jaydb_singleflight_coalesced_total Counter Concurrent reads coalesced by singleflight engine.
jaydb_cas_conflicts_total Counter Precondition failed 412 conflicts on CAS updates.
jaydb_storage_operation_duration_seconds Histogram Latency of cold S3 driver operations.
jaydb_cluster_nodes Gauge Number of active cluster peers in memberlist.

Continuous Profiling (Pyroscope)

JayDB Cloud includes continuous CPU, memory allocation, and mutex contention profiling integration with Grafana Pyroscope.

# Access local pprof flamegraphs
./jaydb-cloud server --port 8080
# Profile endpoint: http://localhost:8080/debug/pprof/

S3 Cost Breakdown

How does JayDB run 1,000,000 requests/month for under 32 cents?

Workload Item Volume / Month AWS Standard Rate Effective Monthly Cost
S3 Document Storage 2 GB total $0.023 / GB-month $0.046
S3 GET Reads 50,000 cold reads (95% absorbed by cache) $0.0004 / 1,000 $0.020
S3 PUT Writes 50,000 updates $0.0050 / 1,000 $0.250
Data In / Out 100 GB egress Free tier $0.000
TOTAL MONTHLY AWS S3 COST ~$0.316 / month