InventDB
All articles Architecture

Inside InventDB: one engine from the disk to the AI

An InventDB instance is a single process that holds the storage engine, the query engine, the access rules and every API. This article walks through those layers from the disk upwards, follows one request through all of them, and states what the design costs.

A typical business application runs on several systems at once: a database, an API server, a search service, a job runner and an AI integration. Each has its own copy of who may see what, its own deployment and its own way of failing. InventDB puts those pieces into one engine, written in Rust, running as one process per customer.

Two products are built on that engine. InventDB Serverless is the managed database: the engine and every interface to it, with no AI inside. InventDB SOAR is the database with the business engine: the same database plus workflows, reports, analytics, views and forms, running on Claude or on an open-weight model served by our own inference engine. Everything below the business engine is identical in both.

The layers of one instance

The layers of one InventDB instance, from the interfaces at the top to the encrypted storage engine at the bottom, with the AI gateway beside the business engine Apps, AI agents, the console and your own code ONE INSTANCE, ONE PROCESS Interfaces REST API · SQL over HTTP · MCP · webhooks and change feed Request pipeline sign-in, caller attached, grants checked per table Business engine, InventDB SOAR only workflows · reports · analytics · views · forms Query engine and row rules parser · rule-based planner · four execution paths Storage engine write-ahead log · segments · indexes on every field AES-256-GCM on records, indexes and the log InventDB SOAR only AI gateway key, allowance, metering Claude on Amazon Bedrock in the customer's region
One process holds every layer. Only the business engine in InventDB SOAR makes calls outside the instance, through the AI gateway to the model.

Every SQL query and every record write passes through one of two engine entry points, whichever interface it came from. That single property explains most of the design. An access rule, an audit stamp or a durability guarantee written at those entry points holds for the REST API, SQL, an AI agent and a workflow alike, because none of them has a separate path to the data.

Storage: segments, a log and an index on every field

Records are JSON documents grouped by namespace and type, so a table is named like pms.leases. Records change shape without migrations, and nested fields and arrays are queryable. A type is stored as a series of segments. One segment is active and takes every write; when it holds 100,000 records it is sealed and a fresh active segment opens. Records in a sealed segment are never rewritten: an update writes the new version into the active segment, and the old copy is filtered out of results. That stability is what lets the engine build column files for numeric aggregates once, when a segment is sealed.

Inside a segment, records sit in an append-only record file, each with a CRC32 checksum, and an id index maps every _id to its position in that file. By default every field is indexed. An indexed field gets three B-link trees: one from value to record ids for lookups and ranges, one from value to count and sum that answers GROUP BY and global aggregates without reading records, and one from id back to value. Each segment also keeps a zone map, the minimum and maximum of each field, and a Bloom filter over its ids, so a query can skip segments that cannot hold a match.

Writes take a shorter route. A write is appended to a write-ahead log and fsynced, the in-memory indexes are updated, and the record goes into an in-memory table that reads consult first. Only then does the call return. Every five seconds a background checkpoint writes the logged changes into the segment files and truncates the log. If the process dies between checkpoints, the next start replays the log, so an acknowledged write is never lost. Records, index pages and the log are all encrypted with AES-256-GCM, on a volume that is itself encrypted. One instance holds up to 250 million records.

The query engine reads indexes first and records last

Clients send SQL as text over HTTP. The engine parses it, folds constant expressions such as LOWER('Plumbing') into literals so they can use an index, and refuses any statement that still contains an unfilled placeholder. A rule-based planner then picks a strategy for each clause from the shape of the query and the indexes that exist. It keeps no table statistics and produces exactly one plan.

The plan is labelled with one of four execution paths, ordered by how little they read. COUNT(*) without a filter comes from segment metadata. Aggregates over indexed fields come from the count trees and column files. A GROUP BY on an indexed field is computed from ids alone. Only the fourth path fetches and parses records, and for ORDER BY ... LIMIT n it ranks candidates from index entries and fetches just the top n. Shapes the planner does not cover go to a general executor that evaluates the statement row by row, so every supported query runs, some of them more slowly. The SQL endpoint accepts SELECT only; writes go through the REST API.

Access rules are enforced inside the engine

When a request is authenticated, the server attaches the caller to it: the user, their role and the app they signed in through. Grants decide which namespaces and types each role may read, write or delete. Row rules attached to grants are joined into the WHERE clause of every query and checked against every record written on the caller's behalf. Because the check happens at the engine's two entry points, a query or a change gets the same answer whether it came through the REST API, SQL, an MCP tool, a form or a workflow step. The engine also stamps every record with who created it and who last changed it.

Passwords are hashed with bcrypt. AI agents that connect over MCP sign in through OAuth 2.1 and then act as the person who authorised them: their tool calls are checked against that person's grants, and their queries and changes against that person's row rules.

Four ways in, one set of rules

Programs work with the engine through four surfaces. The REST API reads and writes records and runs multi-step transactions. SQL over HTTP at /sql answers SELECT queries with JSON. The MCP server at /mcp gives AI agents 51 typed tools. Webhooks and a change feed let other systems follow changes as they are made.

Every instance serves its live API reference at /api/docs and its OpenAPI spec at /api/openapi.json, generated from the same code that handles the requests. The web console is part of the platform as well: the same process serves it, and it calls the same API as any other client.

The business engine in InventDB SOAR

InventDB SOAR adds a set of services on top of the database. A workflow starts on a trigger: an email arriving, a webhook call, a record changing or a schedule coming due. Each step acts on data or reaches an outside system through Gmail, Google Calendar, SMS or a webhook, and a step that needs a person raises a notification and waits for the decision. Steps run under the workflow owner's role and row rules. A report is an HTML template with SQL behind every figure. Analytics turns a plain-English question into SQL, runs it under the asker's rules and shows the SQL beside the answer. Views save a query with its layout, and forms are generated from the data and its foreign keys.

The model behind these features is the one the administrator chose: Claude on Amazon Bedrock, or an open-weight model served by InventDB Sparkle, the inference engine we built. The instance reaches either through the AI gateway: it calls the gateway with its own key, and the gateway checks that key and the monthly AI allowance, meters the call and forwards it to the model. InventDB Serverless has none of this and sends nothing to a model.

One request, start to finish

Here is an app asking for the ten most expensive active leases:

POST /sql
Authorization: Bearer <token>
Content-Type: application/json

{ "sql": "SELECT unit, tenant, rent FROM pms.leases WHERE status = 'Active' ORDER BY rent DESC LIMIT 10" }
  1. The request reaches the instance over TLS. The server caps the size of the request body, at 100 MB by default.
  2. The token is verified and the caller is attached to the request. Every engine call from here on runs as that person.
  3. The server records the time of the request. The platform reads this last-activity time to decide whether the instance can sleep.
  4. The SQL route lists every table the statement names and checks the caller's grant on each. One missing grant returns 403 and nothing runs.
  5. The engine call moves to a pool of blocking threads, so a long query cannot hold up the threads serving other requests. A query has a five-minute budget by default and stops early if the client disconnects.
  6. The engine joins the caller's row rules to the WHERE clause, parses and plans the statement, and runs it. Writes that have not reached a checkpoint yet are read from memory, so the query sees every acknowledged write.
  7. The rows go back as a JSON array.

A write through the REST API takes the same first three steps, and then the route checks the caller's grant on the type. The engine checks the record against the caller's row rules and any foreign keys, appends it to the log, fsyncs, updates the indexes and returns.

Where an instance runs

Each customer gets their own InventDB process on their own virtual machine, with their data on their own encrypted volume. No other business shares the process, the machine or the volume. Instances are deployed in-region, in the US, the EU or India. When an instance has had no requests for a set idle period, the platform stops it, and the next request starts it again; the volume and everything on it persist throughout. The stop is orderly, with a final checkpoint, so a woken instance does not need crash recovery.

What this design costs

  • One machine per instance. An instance grows by moving to a larger machine, up to 250 million records. Durability means fsynced to the instance's own volume; there is no synchronous replica, and protection against losing the volume comes from backups.
  • Indexing every field costs writes. Each indexed field adds three tree updates to every insert. In return most filters, counts and groupings never read a record.
  • A rule-based planner has blind spots. Without statistics it cannot notice that a different join order would be cheaper. Our benchmarks page publishes the categories where PostgreSQL is faster alongside the ones where InventDB is.
  • Sleeping has a wake-up cost. The first request to a sleeping instance waits while the instance starts.

Trying it

Every instance documents itself, so the quickest way to see these layers is to open /api/docs on your own instance and send the request above with your own token. InventDB Serverless gives you the database and the four interfaces; InventDB SOAR adds the business engine on the same storage, query engine and rules. The articles linked below go one layer deeper.