ExuluDatabase has two entry points — init() and update() — that share the same options. Both are idempotent; call either on every boot without risk to existing data.
init() and update()
init() communicates “setting up from scratch”; update() communicates “adding to an existing schema”. Pick the name that best describes your intent — the behaviour is identical.
Options
Array of
ExuluContext instances for which to create or verify database tables. Pass all contexts your application uses, not just new ones — the routine is idempotent and skips tables that already exist.When
true (the default), also runs initLitellmDb(), which executes a guarded prisma db push against the database pointed to by LITELLM_DATABASE_URL. Set to false if you are not running the LiteLLM proxy, or if you manage the LiteLLM schema separately.What init() creates
Core tables
init() iterates over every known schema and either creates the table or adds any missing columns. The core schemas created or updated on every run include (plural table names as stored in Postgres):
All core tables include
id (UUID primary key), createdAt, and updatedAt timestamp columns.
Default roles
Created only when absent:Default admin user
Created atemail: "admin@exulu.com" with password admin (bcrypt-hashed), super_admin: true, and the admin role. Created only when no user with that email exists.
Default API key
init() calls generateApiKey("exulu", "api@exulu.com") at the end of every run. The key is logged to stdout. It is persisted in the users table only on the first call — subsequent calls find the existing api@exulu.com user and return a freshly generated key that is never stored. See API reference — persistence semantics.
Per-context tables
For eachExuluContext in the contexts array, init() calls three idempotent helpers:
-
Items table —
context.tableExists()returns false →context.createItemsTable(). The table name defaults to{context.id}_itemsand includes the context’s field schema plus the standardid,createdAt,updatedAtcolumns. -
Chunks table — Only when
context.embedderis configured.context.chunksTableExists()returns false →context.createChunksTable(). The chunks table stores thecontenttext, anembedding VECTOR(N)column (whereNis the model’svectorDimensions),metadata JSONB, and a foreign key to the items table. -
Entity tables — Calls
ensureEntityTables(context), which is a no-op when the entity layer is not configured on the context.
Adding a new context
When you add a new context to an existing application, pass it alongside your existing contexts:newContext’s tables are created.
LiteLLM schema
Whenlitellm is not false, initLitellmDb() runs prisma db push against LITELLM_DATABASE_URL. This keeps the LiteLLM proxy schema current without a separate migration step.
Pass litellm: false when:
- You are not running the LiteLLM proxy.
LITELLM_DATABASE_URLis not set.- You manage the LiteLLM schema outside of IMP’s boot sequence.
Complete initdb example
Environment variables
ExuluDatabase reads two environment variables:
PostgreSQL connection string used by the Knex client. Format:
postgresql://user:password@host:port/database.Secret used for bcrypt password hashing and encrypted variable storage. Generate with
openssl rand -base64 32.Next steps
API reference
Full method signatures and
api.key.generate() persistence semantics.Self-hosting — database
Backups, the no-migration model, and the first-boot API key warning.