Skip to main content
ExuluDatabase is an object exported from @exulu/backend that wraps IMP’s database setup routines. You call it from the utils:initdb script in your project — the same script that runs on every backend and worker boot via npm run utils:initdb. Its two initialisation methods are identical in behaviour; update() exists as a semantic alias for init() when you want to make it clear you are adding to an existing schema rather than setting it up from scratch.
For the operations side of the database (backups, the no-migration model, LiteLLM schema, and pgvector), see Self-Hosting — Database. This page documents the programmatic API.

What ExuluDatabase does

Core schema

Creates or updates ~26 core tables (agents, users, roles, sessions, variables, and more) using idempotent CREATE TABLE IF NOT EXISTS and ALTER TABLE … ADD COLUMN patterns.

Default bootstrap

On first run, creates the admin and default roles and the default admin user (admin@exulu.com). Logs and persists one API key for api@exulu.com.

Per-context tables

For each ExuluContext you pass, creates the context items table and (when an embedder is configured) the chunks table with the correct pgvector column dimensionality.

LiteLLM schema

Optionally runs initLitellmDb() to keep the LiteLLM proxy schema current. Enabled by default; pass litellm: false to skip.

API key generation

api.key.generate(name, email) creates an API user record and returns the plain-text key — the only moment it is available in clear text.

Idempotent

Safe to call on every boot. Tables and columns that already exist are left untouched; only missing structures are added.

The initdb pattern

The recommended pattern is a utils/initdb.ts script in your project that imports your contexts and calls ExuluDatabase.init():
Add it to package.json:
Run before first launch and after adding new contexts:

First-boot bootstrap

On first boot (before any of the target tables exist), init() creates:
  • An admin role with full write access to agents, API, workflows, variables, users, evals, and budget management.
  • A default role with write on agents and read on everything else.
  • A default admin user with email admin@exulu.com and bcrypt-hashed password admin.
  • One API key for api@exulu.com with admin role; the plain-text key is logged to stdout once and never stored again.
Change the default admin password immediately after first login. The default credential (admin@exulu.com / admin) is documented publicly. See Self-Hosting — Database for the first-boot warning and recovery instructions.
The first-boot API key is generated by calling generateApiKey("exulu", "api@exulu.com") internally. Subsequent boots call the same function; since the user record already exists (matched by email), the new key is returned but not persisted. Keys logged after the first boot will not authenticate. See API key persistence semantics for the exact behaviour.

Subsequent boots

On every subsequent boot, init() / update() checks each table and column for existence before acting:
  • Tables that already exist are checked for missing columns; missing columns are added with ALTER TABLE.
  • Tables that do not exist are created.
  • Role and user records are inserted only when absent.
  • One-time data migrations are gated on column-existence checks and become permanent no-ops after their first successful run.
This means you can call ExuluDatabase.init() freely in your startup code without worrying about data loss or conflicts.

Next steps

Configuration

init() and update() options, LiteLLM flag, and context table creation details.

API reference

Full method signatures, api.key.generate() return type, and key persistence semantics.