Understand

Architecture

ThimbleDB is a client-read, server-write database for small web applications. Cloudflare Workers and R2 are the reference deployment. Azure Blob Storage, Amazon S3, and a local filesystem adapter implement the same storage abstraction.

See System diagrams for trust boundaries, request sequences, scope separation, and provider layouts. See Storage providers for the provider contract and conformance requirements.

Reference path

Browser
  authority-and-scope-namespaced memory LRU
  authority-and-scope-namespaced encrypted IndexedDB cache
  typed point, index, and bounded-scan queries
          |
          v
Private read broker
  session and scope validation
  encrypted binary envelopes from R2

Browser mutations
          |
          v
Cloudflare Worker
  external OIDC authentication and identity mapping
  scope authorisation
  validation
  declared secondary-index maintenance
  gzip then AES-256-GCM
  conditional R2 writes
  cache-update bundle response

The browser never receives an R2 API token, AWS secret, Azure account key, or write-capable storage credential.

Data scopes

Every collection tree belongs to one access scope:

<application-prefix>/
  scopes/
    public/
      content-trie/ or content-snapshot/
    tenant-123/
      content-trie/ or content-snapshot/
    user-456/
      content-trie/ or content-snapshot/

A scope can represent public data, one tenant, one user, or a role. Pages from different scopes are never mixed. This is required because possession of a scope key permits decryption of every page encrypted by that key.

Public scopes use the same binary envelope and adaptive gzip but omit encryption. Private scopes use a versioned AES-256-GCM data key.

Read path

  1. The browser reads HEAD.json from memory or IndexedDB.
  2. If its TTL expired, the browser revalidates HEAD with If-None-Match.
  3. A 304 response keeps the current layout generation.
  4. ID equality resolves directly to one document path.
  5. A matching declared index resolves a bounded set of candidate IDs.
  6. Queries without a usable index use an explicitly bounded scan.
  7. Trie HEAD points to an immutable root, branch, and leaf path.
  8. Snapshot HEAD points to one immutable collection snapshot.
  9. The browser coalesces concurrent reads of the same immutable object.
  10. It re-evaluates the complete predicate, orders, limits, and returns the query plan with the documents.

Immutable pages do not need revalidation. Their object key identifies their content within the scope and key version.

Write path

  1. The browser sends a mutation to the authority.
  2. The authority authenticates the session and resolves allowed scopes.
  3. Application validation runs before storage work.
  4. Changed trie pages or the next immutable snapshot are serialised, gzip-compressed when useful, and encrypted.
  5. Every configured secondary index is updated or rebuilt.
  6. New immutable document and index objects are created.
  7. HEAD publishes the document root and all active index references with one ETag compare-and-swap.
  8. The response includes the new HEAD, changed immutable objects, and document.
  9. The writing tab updates its cache and broadcasts the bundle to other tabs.

Conditional HEAD writes are the transaction boundary for one collection and scope. Cross-collection transactions are not supported.

Cache layers

LayerContentsLifetime
MemoryDecoded hot objectsCurrent tab, bounded LRU
IndexedDBDevice-key-encrypted cached objectsPersistent browser cache
Object storageGzip-compressed public envelopes or gzip plus AES-GCM private envelopesDurable source of truth

The scope data key remains memory-only. IndexedDB has a separate non-extractable device key. Clearing cached objects retains that shared key so another tab cannot create entries that a newly generated key cannot decrypt. Logout-time key rotation requires cross-tab coordination.

Every cache key is namespaced by authority URL and scope ID, including custom cache implementations. Reusing one cache object across users or tenants cannot return another scope’s decoded values.

Provider model

The database engine depends on an ObjectStore interface rather than a cloud SDK. Provider adapters supply bytes, ETags, conditional writes, deletion, and prefix listing.

ProviderWrite authorityDurable storageBrowser read pattern
CloudflareWorkerR2Authenticated Worker broker
LocalNode processLocal filesystemSame-origin authenticated broker
AzureContainer App or Node serviceBlob StorageAuthenticated authority broker
AWSLambda or another Node hostS3Authenticated authority broker

The stored envelope, trie, and snapshot protocols do not change between providers. Cloudflare is preferred, not required.

Boundaries

  • One HEAD serialises writes within a collection and scope.
  • Document roots and declared secondary indexes become visible through the same HEAD update.
  • Equality indexes contain scalar tuples; range indexes contain one scalar field. Non-scalar predicates use bounded scans.
  • Production engines retain old generations. Destructive garbage collection is available only in an explicitly enabled, quiescent maintenance mode.
  • Every deployment uses an external OIDC identity provider. ThimbleDB stores only the stable provider-to-internal-user mapping and revocable sessions.
  • Revoking a user cannot erase plaintext they already downloaded.
  • Full-text search, joins, analytics, and cross-scope queries require derived indexes or another system.

This page is built from the repository source.

View or improve this page on GitHub