This backend uses Knex + PostgreSQL for schema migrations.
For operational guidelines on safe online migrations (zero-downtime expand/contract patterns, concurrent index creation, statement timeouts, and rollback workflows), refer to the Production Database Migration Strategy Runbook.
- Works well with Node.js/TypeScript projects.
- Supports
upanddownmigrations for safe rollouts and rollbacks. - CLI is easy to run locally and in CI/CD.
- Knex config:
knexfile.cjs - Migrations directory:
db/migrations - Migration tracking table:
knex_migrations - Connection source:
DATABASE_URL
- Owner: Backend / Database team (Disciplr). For schema changes, open a PR targeting
db/migrations/and request a review from@Disciplr-Org/db.
The legacy SQL files under src/db/migrations/ are deprecated and no longer authoritative. All required schema changes are now tracked in db/migrations/.
The db/migrations/20260501000000_create_api_keys_and_idempotency_keys.cjs migration brings api_keys and idempotency_keys into the canonical Knex-managed migration flow.
- Baseline file:
db/migrations/20260225190000_initial_baseline.cjs - Creates:
vaultstablevault_statusenum type- indexes on
creator,status,end_timestamp
- Rollback drops
vaultsandvault_status.
- Set
DATABASE_URLto a writable Postgres instance. - Apply pending migrations:
npm run migrate:latest
- Check current state:
npm run migrate:status
- Create a new migration:
npm run migrate:make add_some_change
- Fill in
exports.upandexports.downin the new file. - Re-run
npm run migrate:latestand test application behavior. - If needed, rollback one batch:
npm run migrate:rollback
- One logical schema change per migration.
- Always implement both
upanddown. - Keep migration files immutable after merge.
- Prefer additive, backward-compatible changes for zero-downtime deploys.
Run migrations in deployment pipelines before starting app instances on new code.
This repository includes a CI example at .github/workflows/ci.yml that:
- starts PostgreSQL in GitHub Actions
- runs
npm run migrate:latest - verifies state with
npm run migrate:status - asserts migrations are clean with no pending files after application
Example deployment step:
npm ci
npm run migrate:latest
npm run build
npm run startExample GitHub Actions job fragment:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run migrate:latest
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- run: npm run buildThis repository also runs Soroban contract verification in CI through .github/workflows/ci.yml.
The CI workflow now includes a separate contracts job that:
- checks out the repository
- sets up the Rust toolchain
- caches the Cargo registry and the Soroban contract
targetartifacts - installs
cargo-contract - builds
contracts/accountability_vault - runs
cargo testforcontracts/accountability_vault/src/test.rs
This keeps on-chain contract code verified alongside the existing Node/TypeScript suite.
- Immediate rollback path for the last batch:
npm run migrate:rollback
- Keep database backups/snapshots in production for disaster recovery.
- For destructive changes, follow the expand/contract operational procedures documented in the Migration Strategy Runbook.
This migration closes the schema drift between the Knex-managed vaults / milestones tables and the PersistedVault / PersistedMilestone TypeScript interfaces used by vaultStore.ts.
| Change | Detail |
|---|---|
| Column rename | start_timestamp → start_date |
| Column rename | end_timestamp → end_date |
| Column added | verifier VARCHAR(255) NOT NULL |
| Column added | updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() |
| Index dropped | idx_vaults_end_timestamp (references old column name) |
| Index created | idx_vaults_end_date on end_date |
| Enum value added | 'draft' added to vault_status |
| Status default changed | status default changed from 'active' to 'draft' |
| Milestones aligned | Adds sort_order, amount, due_date columns if missing |
Important: If any vault rows have
status = 'draft'when rollback runs, those rows are automatically updated tostatus = 'active'before the enum value is removed. This is logged (without row data) and is not silent.
Steps performed by exports.down:
- Any
'draft'rows are updated to'active'(with a warning log). statusdefault is restored to'active'.'draft'is removed fromvault_statusvia the create-new-enum / cast / drop-old / rename pattern.idx_vaults_end_dateis dropped;idx_vaults_end_timestampis recreated.updated_atandverifiercolumns are dropped.end_date→end_timestampandstart_date→start_timestampare renamed back.- Any milestones columns added in
upare dropped.
prisma/schema.prisma was updated in the same change:
VaultStatusenum gainsDRAFTstartTimestamp/endTimestamprenamed tostartDate/endDatewith@mapdirectivesverifier Stringfield addedupdatedAt DateTime @updatedAt @map("updated_at")added- Status default changed to
DRAFT - Index updated to
@@index([endDate])
The migration emits structured JSON log entries for each step (step name + status only). No column values — wallet addresses, amounts, or destinations — are logged at any level.