Database
The D1 schema, the versioned patch/migration convention, and how to apply schema changes.
eetr-auth persists everything in Cloudflare D1 (SQLite). The schema is managed with a snapshot
plus versioned patches.
Authoritative source
db/README.md in the repo is authoritative on schema mechanics (it lives next to schema.sql and
db/patches/). This page is the public overview.
Snapshot vs patches
db/schema.sql— the complete authoritative snapshot of the current schema, for a fresh install. It seeds the default OIDC scopes (openid,profile,email) with idempotentINSERT OR IGNOREso a new install can do OpenID Connect out of the box.db/patches/<version>.sql— incremental patches named after released schema versions (e.g.0.1.0.sql). A patch describes only the delta from the previous released schema — not a copy of the full snapshot.
Rule of thumb:
schema.sql= the latest full schema for a fresh database.patches/<version>.sql= how an existing database catches up from the previous released version.
Migration strategy
db:migrate / db:migrate:remote do the following:
- Read
schema_metadata.schema_version. - If the metadata table doesn't exist, assume version
0.0.0. - If the database has no application tables yet, apply
schema.sqlas the clean-install snapshot. - Otherwise, apply every patch in
db/patcheswith a version greater than the current, in ascending semver order. - Each patch updates
schema_metadata.schema_versionto its own version at the end.
Not every app release needs a database patch. If a release doesn't change the schema, no new patch is added and the schema version stays put.
Applying the schema
From the repository root:
| Command | Use |
|---|---|
npm run db:schema | Apply the fresh snapshot to local D1 |
npm run db:schema:remote | Apply the fresh snapshot to remote D1 |
npm run db:bootstrap | Both local + remote |
npm run db:migrate | Apply versioned patches to local D1 (upgrades) |
npm run db:migrate:remote | Apply versioned patches to remote D1 (upgrades) |
Fresh install vs upgrade
Use the db:schema* commands for a fresh installation. Use db:migrate* only when upgrading an
existing database with versioned patches. On a clean install, npm run setup:remote applies the
snapshot for you.
Schema domains
The database is organized around users, OAuth clients, tokens, and environments:
Key tables include users, user_totp (encrypted TOTP secrets), clients, environments,
scopes, authorization_codes, tokens, refresh_tokens, passkeys, password_policies, and
token_activity_log.
Creating an admin user
Create an admin (users.is_admin = 1) in both local and remote D1 with username + email. The
script stores a random placeholder password hash; finish setup via password reset (remote requires a
configured site URL and working email).
npm run db:create-admin -- <username> <email>- Local seed admin:
npm run db:seed-local-admin(admin/admin, MD5, local-only) - Seed default remote admin:
npm run db:seed-remote-admin(admin/admin, Argon2id)
Authoring the next schema release
When the schema changes in a future release:
- Update
schema.sqlso it matches the new latest schema. - Add / continue the unreleased patch file, named with the target schema release version, with only the delta from the previous released schema.
- Make the patch idempotent enough for the expected upgrade path.
- End the patch by updating
schema_metadata.schema_versionto the released version.