InventDB
All articles Security

Encryption at rest that cannot be switched off

Every InventDB instance encrypts what it stores with AES-256-GCM, and the engine has no setting that turns encryption off. This article covers what is encrypted, how the cipher also detects damaged data, how passwords are kept, and what encryption at rest does not protect against.

When encryption at rest is an option, someone has to remember to turn it on, and there is a way to turn it off. InventDB has no such switch. The engine refuses to open a database without a key, and the contents of your records, indexes, files and log pass through AES-256-GCM before they reach the disk.

Encryption at rest answers one specific question: what does a person learn if they get hold of the disk, a snapshot of the volume or a copy of a backup without going through the database? This article explains what InventDB encrypts, the second job that authenticated encryption does, how passwords are stored, how data travels to the instance, and the things encryption at rest was never meant to cover.

What the engine encrypts

Each InventDB instance is single-tenant: one customer's process on its own virtual machine, writing to its own encrypted volume. On top of the volume's encryption, the engine encrypts the contents of every kind of file it keeps for your data:

  • Records. Each record is encrypted on its own as the engine appends it to a segment's record file.
  • Index pages. Every page of the B-link trees behind the id index and the property indexes is encrypted when it is written.
  • Column files. The columnar files the engine builds when a segment is sealed, which serve aggregates and GROUP BY, are encrypted as whole files.
  • Search indexes for records. The per-segment files behind semantic search and keyword search over your records are encrypted the same way.
  • Attachments. Files attached to records are stored inside the engine in chunks, and every chunk is encrypted.
  • The write-ahead log. The contents of each log entry are encrypted before the entry is written, so a write's values are encrypted from the first moment they touch the disk, before the write is folded into a segment.

All of them use the same cipher, AES-256-GCM. Backups copy these files as they are, so a backup kept in object storage in the same region holds the same encrypted files, and object storage applies its own server-side encryption on top. A copy of a backup on its own cannot be read.

Data is protected by TLS on the way in, encrypted by the engine before it is written to the encrypted volume, and copied as encrypted files into backups Your application REST, SQL or MCP TLS 1.3 YOUR INSTANCE Engine AES-256-GCM on every write authenticated on every read Encrypted volume records and index pages column and vector files full-text index files attachment chunks log entry contents Backups object storage in the same region same encrypted files and storage encryption
TLS protects data on the way to the instance, the engine encrypts it before it is written, the volume underneath is encrypted, and backups carry the same encrypted files.

Why there is no off switch

The engine's open call takes a 256-bit key as a required argument, and the server always supplies one. There is no configuration that stores data in plaintext. An instance cannot drift into a state where encryption was quietly disabled, and there is no setup step for anyone to forget.

It also keeps our measurements honest. Every performance number we publish was measured with encryption on, because the engine has no other way to run. On the published 143-query set over 10 million records, InventDB measures 6.3x PostgreSQL, warm, with InventDB encrypted and PostgreSQL 18 tuned and unencrypted. The categories PostgreSQL wins are published alongside on our benchmarks page.

Authenticated encryption also catches damage

GCM is an authenticated mode. Alongside the ciphertext it stores a tag computed from the key and the data, and the engine checks that tag every time it reads a record, an index page or a log entry back, before it uses any of the bytes. If anything on disk has changed since the engine wrote it, whether through a deliberate edit or a bit flipped by failing hardware, the check fails and the read returns a decryption error instead of altered data.

For a database this matters as much as secrecy does. A storage fault that silently turns a balance of 100 into 900 is worse than one that fails, because the wrong value would be read, used in a calculation and copied into a report. With authenticated encryption the read fails and the damage is visible. Index pages, column files and log entries also carry CRC32 checksums, a separate check against the same class of fault. Our article on what happens when the power goes out covers how the engine detects and repairs damaged files.

Passwords are hashed rather than encrypted

Encryption is reversible by design: with the key, you get the data back. A password should not be recoverable at all, so InventDB never encrypts one. When a person sets or changes a password, the server hashes it with bcrypt at cost factor 12, which runs 4,096 rounds of bcrypt's expensive key setup, and stores only the resulting string. The plaintext is discarded.

Each hash carries its own random salt, so two people with the same password get different stored values, and a login compares the presented password against the stored hash in constant time. The cost factor is deliberate: every check takes the same slow, fixed amount of work, which makes guessing passwords from a stolen copy expensive. The user store that holds these hashes is itself on the encrypted disk, so an attacker would first need that store in readable form and would then still face bcrypt.

On the way to the instance

Encryption at rest protects files. Data moving over the network is protected separately, by TLS. Your instance's public address is served over HTTPS, negotiating TLS 1.3 with current clients and accepting TLS 1.2 from older ones. The REST API, SQL over HTTP and the MCP endpoint for AI agents all use that address, so a request and its response are encrypted between your application and the instance's address in both directions.

What encryption at rest does not cover

Encryption at rest has a narrow job, and it is worth being exact about its edges.

  • Anyone the database lets in. Encryption at rest protects data from people who go around the database. A person or an agent with a valid token and a grant reads plaintext, because the engine decrypts for them. Who that is, and which rows they see, is decided by sign-in, roles, grants and row rules, which our articles on sign-in, roles and grants and row-level security describe.
  • Data in use. The engine decrypts what it reads in order to filter, join and aggregate it, so data being processed inside the running process is not encrypted. The scope of this feature is files on disk.
  • Identifiers. The engine encrypts record values. Some identifiers, such as record ids and the names of your namespaces and types, are used to organise the files and are protected by the encrypted volume rather than by the engine's own encryption. If an id is itself sensitive, such as an email address, let the engine generate the id and store the sensitive value as a field.
  • Very long passwords. bcrypt reads only the first 72 bytes of a password, a property of the algorithm. Characters past that point do not change the hash.

What you need to do

Nothing. Encryption is on from the first write on every instance of InventDB Serverless and InventDB SOAR, because both run on the same engine and the engine has no unencrypted mode. A record you create over the REST API, like this one:

POST /db/crm/contacts
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Asha Rao",
  "email": "asha@example.com",
  "tier": "gold"
}

travels over TLS, is checked against your grants, and is written to the log and then to its segment in encrypted form. Reading it back is an ordinary query, and the engine decrypts for you because you are signed in and hold a grant on the type:

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

{ "sql": "SELECT name, email FROM crm.contacts WHERE tier = 'gold'" }

Nothing in either request mentions encryption, and nothing needs to. The protection that does depend on your choices is access control: who gets an account, which roles and grants they hold, and which rows their rules allow.