Database (SQLite ⇄ Postgres)
One schema on two drivers, ingest of run results, and a verified switch between drivers.
What the database stores, how DB_DRIVER selects SQLite or Postgres, what ingest writes, and how
sdods db switch copies and verifies data.
Toggle
DB_DRIVER=sqlite bun run sdods db migrate # default: .sdods/sdods.db
docker compose up -d postgres
DB_DRIVER=postgres DATABASE_URL=postgres://sdods:sdods@localhost:5432/sdods bun run sdods db migrate
bun run sdods db statusThe SQLite file lives at <workspace>/.sdods/sdods.db. The path is resolved against the workspace
root — the nearest directory above you holding sdods.workspace.yaml — not against the directory
you happen to be in, so sdods users, sdods serve and sdods run open the same database from
anywhere inside the workspace. Set SQLITE_PATH to put it somewhere else; an explicit value is
used exactly as given.
Kysely serves one schema on both drivers; primary keys are application-generated UUID v7 strings, timestamps are ISO text on SQLite and timestamptz on Postgres, JSON is text or jsonb.
What is stored
| Group | Tables |
|---|---|
| Platform | organizations, workspaces, projects, environments, users, roles, sessions, api_tokens, audit_log |
| Results | runs, scenarios, scenario_attempts, steps, artifacts |
| Learning | heal_events, locator_stats, flaky_stats |
| Data | datasets, dataset*rows, user_pool, user_leases, plus td*<slug>\_<name> test-data tables |
| Automation | integrations, issue_links, agent_jobs, proposals, schedules, schedule_runs |
The runner never needs the database unless a scenario uses a db dataset, a database lease store, or ingest is enabled.
Ingest
Scenarios are counted, attempts are kept; a scenario that failed once and passed on retry is flaky. Re-ingesting the same files is idempotent, and shards merge into one run.
bun run sdods run -p demo-shop -e staging -l api --ingest
bun run sdods report ingest --run-id <id> .sdods/runs/<id>/messages.ndjsonSwitch drivers
bun run sdods db switch postgres --target-url postgres://sdods:sdods@localhost:5432/sdods --dry-run
bun run sdods db switch postgres --target-url postgres://sdods:sdods@localhost:5432/sdods --yes
bun run sdods db switch sqlite --yesThe switch migrates the target, exports every table in foreign-key order, imports in one transaction, verifies row counts, and only then rewrites .env. db export and db import expose the two halves; for routine backups use db backup / db restore <dir> --yes, which add a manifest and take a safety copy before restoring. db prune --keep-runs 200 --keep-days 90 deletes old runs and their artifact directories (never a run that is still going); db retention --keep-days 90 --auto on makes the server do it daily. db stats shows which database is in use and how much space it and the artifacts take.