Railbase
GPTClaude

Data & multi-tenancy

The single-file vault store, collections, and the file-per-tenant model.

Updated

Video guide —watch on YouTube ↗

Railbase has no external database. All of your data lives in a single encrypted file managed by an in-process engine. This keeps operations trivial: one process, one file to back up.

The vault

The store is an encrypted, MVCC document store kept in one file — pb_data/railbase.vault by default. "MVCC" means readers never block writers, so the single-file model still serves concurrent traffic well. The file is encrypted at rest with a master key (pb_data/.secret) and unlocked at boot with your vault password (see Installation and Security).

What this means for you:

  • No connection string, no DB server, no migrations service. Schema migrations apply automatically on boot.
  • Backups are a file copy. A snapshot is a byte-exact copy of the .vault file — see Backups & restore.
  • Keep it on local disk. Use the VPS's local disk, not a network mount, for correct file locking and durability.
  • A single document can be up to 4 MiB. Documents larger than a storage page are split across pages transparently; past 4 MiB a write is rejected with an explicit error. Store large payloads as files, not document fields. (The REST create endpoint additionally caps a request body at 1 MiB.)

Collections

Data is organized into collections (think tables). The core owns the system collections (users, sessions, audit log, settings); each plugin owns its own, namespaced by slug (e.g. an inventory plugin's collections are prefixed so they never collide with another plugin's).

When you uninstall a plugin its collections are left dormant rather than deleted, so reinstalling restores your data. A separate, backup-gated purge permanently removes them — see Installing plugins.

Multi-tenancy

For deployments that serve multiple isolated organizations, Railbase uses row-level tenancy inside the one vault file. Tenants are registered in the built-in _tenants collection — from the CLI:

./railbase tenant create acme
./railbase tenant list

A collection opts into tenancy with the .Tenant() mixin in the schema. Its rows then carry a tenant_id column, and the scope comes from the request: a call with an X-Tenant: <tenant-uuid> header (the TypeScript SDK's rb.setTenant(id)) writes and reads only that tenant's rows — the value is stamped on insert and filtered on read. Requests without the header operate at site scope. Roles can likewise be granted per tenant (RBAC).

Tip

A single-tenant deployment is just the default case: no tenant setup, no X-Tenant header, no .Tenant() mixins required. Reach for multi-tenancy only when you actually host separate organizations on one instance. Note that backups operate on the whole vault file — i.e. on all tenants at once.