DB DevOps API
ChangeOps surface: scripts, releases, target connections, executions, approvals, and the enterprise audit trail. Base path /api/db-devops, Professional+ tier (audit is Enterprise, metrics is any licensed tier), authenticated. Governed by the 5000/min DB DevOps limiter. This surface persists to Postgres via the repository layer; the legacy in-memory /api/migrations environment endpoints (IDE side) are a separate, non-durable subsystem.
Scripts — /api/db-devops/scripts
| Method | Endpoint | Description |
|---|---|---|
| GET | /scripts | List scripts with status, execution mode, environment, target |
| GET | /scripts/:id | Single script |
| POST | /scripts | Create (draft) |
| PUT | /scripts/:id | Partial update (only supplied fields change) |
| DELETE | /scripts/:id | Delete; 409 if referenced by a release/execution/approval |
| POST | /scripts/:id/approve | { "approvedBy": "email" }; notifies author |
| POST | /scripts/:id/reject | { "rejectedBy": "email", "reason": "..." }; author may edit and resubmit |
| GET | /scripts/:id/blast-radius | Latest computed blast-radius report (200 with null if none computed yet) |
| POST | /scripts/:id/blast-radius | Compute/recompute now; optional { "approvalRequestId": "uuid" }. 409 if no target connection assigned |
| POST | /scripts/:id/shadow-run | Start an async shadow execution (202); poll /shadow-runs/:id |
| GET | /scripts/:id/blast-radius/narrative | AI plain-English narration of the report (AI-tier) |
POST /api/db-devops/scripts
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | ≤ 500 chars |
sql_content | string | Yes | SQL or Mongo shell commands; ≤ 5MB (413 over cap) |
execution_mode | string | No | release (default) or asap |
environment | string | No | Defaults to development |
target_connection_id | uuid | No | Target connection; may be assigned later, re-validated before execution |
target_database | string | No | Database name on the target instance |
author | string | No | Falls back to the authenticated caller |
curl -X POST http://localhost:5002/api/db-devops/scripts \\
-H "Authorization: Bearer YOUR_TOKEN" -H "Content-Type: application/json" \\
-d '{ "name": "add-users-index",
"sql_content": "CREATE INDEX idx_users_email ON users(email);",
"execution_mode": "release", "environment": "production" }'
// 201 -> full script row; status "draft". Blast radius is computed asynchronously.
Releases — /api/db-devops/releases
| Method | Endpoint | Description |
|---|---|---|
| GET | /releases | List releases |
| GET | /releases/:id | Single release |
| GET | /releases/:id/scripts | Ordered scripts in the release |
| POST | /releases | Create (name, optional description, scheduled_date, migration_ids[]) |
| PUT | /releases/:id | Update |
| DELETE | /releases/:id | Delete |
| POST | /releases/:id/signoff | Record a release-level sign-off |
Connections — /api/db-devops/connections
Target databases for execution. The connection record's config JSONB holds dialect-specific fields plus Manager metadata (groupings, environment), so the IDE and DB DevOps share one connection list. Passwords are encrypted at rest and masked on read.
| Method | Endpoint | Description |
|---|---|---|
| GET | /connections | List |
| GET | /connections/importable | IDE connections eligible for import |
| POST | /connections/import | Import selected IDE connections |
| GET | /connections/:id | Single (masked) |
| GET | /connections/environment/:env | Filter by environment |
| GET | /connections/grouping/:grouping | Filter by grouping |
| POST / PUT / DELETE | /connections · /connections/:id | CRUD |
| POST | /connections/:id/test | Connectivity probe |
| GET | /connections/:id/databases | Enumerate databases on the instance |
// POST /connections/:id/test
{ "success": true, "message": "Connection successful", "latency_ms": 45 }
{ "success": false, "message": "ECONNREFUSED 10.0.0.5:5432" }
// GET /connections/:id/databases
[ { "name": "myapp_production", "size_bytes": 52428800 },
{ "name": "myapp_staging", "size_bytes": 31457280 } ]
Database enumeration queries the dialect's catalog (Postgres pg_database, MySQL information_schema.schemata, SQL Server sys.databases, Mongo listDatabases, Oracle v$pdbs).
Executions — /api/db-devops/executions
Two concerns share this router: durable status records (start/complete/fail/rollback transitions) and live execution against a target, which opens a driver pool (pg / mysql2 / mssql / oracledb / mongodb) after decrypting the target password.
| Method | Endpoint | Description |
|---|---|---|
| GET | /executions | Full history with status, duration, error |
| GET | /executions/recent | Recent runs |
| GET | /executions/:id | Single record |
| POST | /executions | Create a status-tracking record |
| POST | /executions/run | Execute one script live against a target |
| POST | /executions/run-release | Execute all release scripts sequentially; stops on first failure |
| POST | /executions/:id/start · /complete · /fail · /rollback | Transition state |
POST /api/db-devops/executions/run
// body
{ "script_id": "uuid", "connection_id": "uuid", "database": "optional-override" }
// 200
{ "status": "COMPLETED", "rows_affected": 1, "execution_time_ms": 45, "result": [ ... ] }
// 500
{ "status": "FAILED", "error": "relation \\"users\\" already exists" }
POST /api/db-devops/executions/run-release
Each script runs against its own target_connection_id/target_database, falling back to the request's connection_id/database. Aggregate status is FAILED if any script fails; the per-script results[] pinpoints where it stopped.
{ "release_id": "uuid", "connection_id": "uuid", "database": "optional" }
// 200
{ "status": "COMPLETED", "results": [
{ "script_id": "abc", "script_name": "create-users-table", "status": "COMPLETED", "execution_time_ms": 32 },
{ "script_id": "def", "script_name": "add-users-index", "status": "COMPLETED", "execution_time_ms": 18 } ] }
// partial failure
{ "status": "FAILED", "results": [
{ "script_id": "abc", "status": "COMPLETED", "execution_time_ms": 32 },
{ "script_id": "def", "status": "FAILED", "error": "syntax error at or near \\"SELEC\\"" } ] }
Approvals — /api/db-devops/approvals
Multi-signature approval workflow over approval_requests + approval_signatures. A request is satisfied when the required signatures are collected.
| Method | Endpoint | Description |
|---|---|---|
| GET | /approvals | List requests |
| GET | /approvals/:id | Single request with signatures |
| GET | /approvals/status/:status | Filter by status (pending / approved / rejected) |
| GET | /approvals/:id/blast-radius | Blast-radius report bound to the request |
| GET | /approvals/:id/blast-radius/narrative | AI narration (AI-tier) |
| GET | /approvals/:id/shadow-runs | Shadow runs for the request |
| GET | /approvals/:id/snapshots | Decision snapshots captured at sign time |
| POST | /approvals | Open a request for a script/release |
| POST | /approvals/:id/sign | Add a signature (approve/reject with comments) |
| DELETE | /approvals/:id/sign/:email | Revoke a signature |
Audit — /api/db-devops/audit (Enterprise)
Append-only, tamper-evident audit trail. Every mutating request is recorded by auditMiddleware with sensitive fields redacted. Retention purge is the only permitted deletion.
| Method | Endpoint | Description |
|---|---|---|
| GET | /audit | Paginated log (?limit=&offset=) |
| GET | /audit/user/:email | By actor |
| GET | /audit/action/:action | By action |
| GET | /audit/entity/:type/:id | By entity |
| GET | /audit/range?start=&end= | By date range (ISO) |
| GET | /audit/count | { "count": n } |
| GET | /audit/integrity | Verify the hash chain over the entire trail |
| DELETE | /audit/cleanup?days=90 | Retention purge; returns { "deleted": n } |
GET /api/db-devops/audit/integrity
Recomputes the tamper-evident hash chain and reports whether the trail is intact. Any break identifies the first offending row so an operator can locate the tampering or gap.
{ "valid": true, "checked": 10432, "brokenAt": null }
Settings — /api/db-devops/settings
K/V store (AI provider, approval policy, notification/session config). Sensitive values are encrypted at rest.
| Method | Endpoint | Description |
|---|---|---|
| GET | /settings | All settings |
| GET / PUT / DELETE | /settings/:key | Read / upsert / remove a single key |
| POST | /settings/batch | Upsert many keys atomically |
Discovery — /api/db-devops/discovery
Fleet inventory: hosts, scans, discovered databases, and tags.
| Method | Endpoint | Description |
|---|---|---|
| GET / POST / PUT / DELETE | /discovery/hosts · /discovery/hosts/:id | Host CRUD |
| POST | /discovery/hosts/:id/scan | Scan a host for databases |
| GET | /discovery/hosts/:id/history | Scan history |
| GET | /discovery/databases · /discovery/databases/:id | Discovered databases |
| PUT | /discovery/databases/:id/status | Update inventory status |
| POST / DELETE | /discovery/databases/:id/tags · /tags/:tagId | Tag assignment |
| GET / POST | /discovery/tags | Tag catalog |
Metrics — /api/db-devops/metrics
GET /api/db-devops/metrics returns aggregate ChangeOps counters (scripts, releases, connections, pending approvals, execution outcomes) for dashboards. Any licensed tier.
Notifications — /api/notifications
| Method | Endpoint | Description |
|---|---|---|
| GET | /notifications | Current user's notifications |
| GET | /notifications/unread-count | Unread badge count |
| POST | /notifications/:id/read · /read-all | Mark read |
| DELETE | /notifications/:id | Dismiss |
| GET / PUT | /notifications/preferences | Per-user delivery preferences |
The backend also emits these over Socket.IO (role-aware broadcast); polling these endpoints is the supported HTTP fallback.
Data protection — /api/data-protection
Subject-rights operations over user data.
| Method | Endpoint | Description |
|---|---|---|
| GET | /data-protection/export/:userId | Export a user's data (portability) |
| GET | /data-protection/access/:userId | Access report |
| DELETE | /data-protection/erase/:userId | Erasure |
IDE routes (Starter+)
The interactive IDE calls the connection/query/schema families directly. Connections are shared with DB DevOps.
| Method | Endpoint | Description |
|---|---|---|
| GET / POST | /api/connections | List / create (backend tests reachability, then encrypts password, before persisting) |
| GET / PUT / DELETE | /api/connections/:id | Read (masked) / update / delete |
| POST | /api/connections/test | Probe without persisting |
| POST | /api/connections/:id/connect · /disconnect | Open / close a live pool |
| GET | /api/connections/:id/databases | Enumerate databases |
| POST | /api/queries/execute | Run a query |
| POST | /api/queries/execute-batch · /run-script | Multi-statement / script execution |
| POST | /api/queries/explain · /validate · /preview | Plan / validate / preview |
| GET | /api/databases/:connectionId/schema | Schema introspection |
| GET | /api/databases/:connectionId/object-definition | DDL for an object |
| GET | /api/navigator/:connectionId/objects · /object-properties · /tables/:schema/:name/columns | Object Explorer tree + inspector (per-dialect) |
// POST /api/queries/execute
{ "connectionId": "conn-uuid", "query": "SELECT * FROM users LIMIT 10" }
// ->
{ "data": [ { "id": 1, "email": "admin@company.com" } ],
"columns": ["id","email"], "rowCount": 1, "executionTime": 12 }
executionTime is server-measured database time in ms (excludes browser round-trip).