Data and mutations
Nimbus stores JSON documents in tables and pushes query results to subscribers when data changes. The guarantees behind that — what a write means, when it becomes durable, when it becomes visible, and why every protocol surface behaves identically — come from a small set of deliberately rigid rules in the engine and storage layers. This page explains those rules from the perspective of someone deciding whether to trust them.
Documents and tables
Section titled “Documents and tables”A document is a JSON object — a map of fields — that lives in exactly one
table within one tenant. Nimbus manages three system fields on every
document: _id (a unique, validated identifier), _creationTime, and
_updateTime. You never set these; they appear on every document the API
returns.
Tables are not declared up front. Writing to a table name that doesn’t exist yet creates it in the same transaction as the write. A table is a namespace with a stable internal identity, not an object you must provision — which is what makes the next property possible.
Schema is optional
Section titled “Schema is optional”A table without a schema accepts any document. That is the default state of every table, and it is a real mode of operation, not a temporary loophole.
When you do set a schema for a table, you get, per table:
- Required fields — writes missing a required field are rejected.
- Type checks — declared fields must match their declared type; fields the schema doesn’t mention are still allowed.
- Indexes — single-field and composite indexes the query planner can use.
- Access policy — optional declarative read/write rules the engine enforces for every surface.
Two properties of schema changes matter for trust:
- Adding a schema constrains; it never revokes. A schema adds validation to future writes. It never makes a previously schemaless table read-only — conforming writes always remain possible, and documents already in the table stay readable.
- Schema changes are atomic. Replacing a table’s schema and rebuilding its index entries happen in one storage transaction. There is no window where the schema is new but the indexes reflect the old definition.
Validation happens at write time, inside the mutation path described next — so it is enforced identically no matter which protocol or runtime originated the write.
One mutation path
Section titled “One mutation path”Every mutation in Nimbus — an insert from the native HTTP API, a write
arriving over a WebSocket session, a scheduled job firing, a
ctx.db.insert(...) inside a function running in the V8 runtime, or a
write translated from the Firestore, MongoDB, DynamoDB, Cloud Functions,
or Convex dialects — flows through one engine-owned execution path.
There is no fast lane and no internal bypass.
That single path is where the guarantees attach:
- Schema validation runs against the table’s schema, if one exists.
- Authorization enforces the table’s access policy against the caller’s normalized principal.
- Index maintenance stays in lockstep with the document write.
- Subscription fan-out is triggered for every committed change.
Because adapters and the function runtime converge on the same path, none of these can be skipped by choosing a different door into the system. The protocol adapters translate dialects; they do not own write semantics. The runtime’s host bridge calls the same engine methods an HTTP handler does.
Two variants of the path deserve a note:
- Scheduled mutations carry an execution id that is recorded in the same transaction as the write. If the server crashes and the scheduler re-runs a claimed job, the write applies at most once.
- Function mutations are transactional units. A mutation function that performs several reads and writes executes against a stable snapshot, stages its writes locally, and commits once — after validating that nothing it depended on changed underneath it (optimistic concurrency validation). On conflict the unit fails as a whole rather than committing an interleaved partial result.
Durability, then visibility
Section titled “Durability, then visibility”Nimbus separates “your write is safe” from “your write is readable”, and sequences them strictly:
- Admission. Asynchronous mutations enter a per-tenant admission gate. Under overload, shedding happens here — before any durable work — so a mutation that is admitted keeps its commit guarantee.
- Durable append. The mutation is appended to the tenant’s durable journal in commit order. This is the acknowledgement point: a mutation is only acknowledged after its journal record is durably appended.
- Ordered apply. A journal worker materializes the document and index changes and advances the tenant’s applied watermark.
- Visibility. Reads and subscriptions consult applied state only. A read never observes a write that is durable but not yet applied, and never observes apply effects out of order.
One consequence worth internalizing: once a mutation crosses the durable commit point, it is committed even if the client disconnects before seeing the response. Disconnection before the commit point can cancel the work; disconnection after it is a transport failure, not a rollback.
Storage atomicity
Section titled “Storage atomicity”At the bottom of the write path, three effects always travel together in a single storage transaction:
- the document write itself,
- the index entries that write implies, and
- the commit-log (journal) record describing it.
If the transaction commits, all three exist; if it fails, none do. This holds on every storage provider — the embedded engines and the external SQL backends alike, each using its own native transaction to enclose all three effects. There is no state in which a document exists without its index entries, or a commit record exists without its document write. That invariant is what lets indexes serve queries without cross-checking, and lets the journal act as the authoritative history for recovery and replay.
How subscriptions observe commits
Section titled “How subscriptions observe commits”A subscription is a registered query. When you subscribe, the engine evaluates the query once and delivers the initial result; after that, the engine — not the client — decides when you hear about changes:
- After each applied commit (or batch of commits), the engine computes which registered subscriptions a change could affect and enqueues re-evaluation work on a tenant-local delivery worker, off the mutation’s return path.
- Invalidation is conservative by design: inserts and deletes are checked against the changed document, while updates conservatively wake any subscription on that table whose result might be affected. A spurious wakeup costs a re-evaluation; a missed one would cost correctness, so Nimbus only errs in the first direction.
- Delivery is monotonic per subscription. The engine tracks the latest sequence delivered to each subscription and never publishes older state after newer state.
- Under write storms, overlapping wakeups for the same subscription are coalesced: subscribers receive the latest applied result rather than a backlog of intermediate states. You see the current truth, not necessarily every transition.
Because fan-out is computed from applied commits — the output of the single mutation path — every write from every surface reaches subscribers. A document changed through the MongoDB wire protocol updates a live Convex query, because both are just commits to the same engine.
What this adds up to
Section titled “What this adds up to”- A write acknowledged is a write durably journaled, validated, and authorized — regardless of which API produced it.
- A write observed is a write whose document, indexes, and history are consistent with each other.
- A subscription reflects applied state, in order, without regressions.
- A schema is a constraint you add when ready, not a ceremony the database imposes.
Related pages
Section titled “Related pages”- How Nimbus works — the architecture these guarantees live inside.
- Tenant isolation — the boundary around each tenant’s data and execution.
- Developers — build against any of the protocol surfaces.
- Storage backends — the persistence providers behind the storage contract.