stackbone db

This page has two halves with two different targets. The Explorer verbs follow the usual target resolution and pagination rules; every verb here emits the shared JSON envelope under --json.

The stackbone db ... family runs the same migration engine that stackbone dev runs on boot, so you can drive it from the shell. migrate up is advisory-locked and atomic per file: a failed file rolls back on its own and leaves the journal at the last good entry. The journal records every applied tag.

The two halves target different databases. The migrate verbs run the migration engine against the workspace database (the connection comes from STACKBONE_POSTGRES_URL, or is discovered from a running stackbone dev session for this project) and they take no --agent. The read-only Explorer verbs (query, schemas, table) talk to a running installation's runtime and do take --agent, like the rest of the runtime surface.

By default the schema lives at src/schema.ts and migrations under .stackbone/migrations. A workspace's agents and workflows all talk to the one install Postgres, so they share this root schema. An optional root agent.yaml can override either path (database.schema, database.migrations). See Project manifests. agent.yaml is optional here: with none present the migrate verbs use the convention defaults.

The verbs that talk to the database (migrate up and migrate status) read the connection string from STACKBONE_POSTGRES_URL when it is set. If it is not set, they discover it from a running stackbone dev session for this project: dev records its database connection while it runs, so you do not have to export the variable in a separate shell. With neither the variable set nor a live dev session the command exits 3 (no project).

Command Purpose
stackbone db migrate up Apply every pending migration (advisory-locked, atomic per file). Supports --target <tag>.
stackbone db migrate create <name> Generate a new SQL migration by diffing src/schema.ts against the journal.
stackbone db migrate status Classify each migration as applied, pending or drifted (informational).
stackbone db query [sql] Run one read-only SELECT against the installation. SQL from the positional, --file <path> or stdin.
stackbone db schemas List the schemas and tables visible to the installation, with row estimates.
stackbone db table <schema> <table> Browse rows of one table with cursor pagination (--limit, --cursor, --order asc|desc).

stackbone db migrate up

Apply every pending migration under .stackbone/migrations against the workspace database. Uses an advisory lock so concurrent invocations are safe, and the journal records each applied tag. Takes no --agent.

Flag Type Description
--target string Stop after applying the migration with this tag (inclusive). Useful for staged rollouts.

JSON payload

{
  "schema_version": 1,
  "applied": [{ "tag": "0001_init", "appliedAt": "2026-05-16T10:00:00Z" }],
  "skipped": [{ "tag": "0000_bootstrap", "appliedAt": "2026-05-15T09:00:00Z" }],
}

appliedAt is best effort: it comes from a second read of the journal. If that read does not come back, the command emits the entry without the field rather than failing.

Exit codes: 0 ok, 3 no project (no database connection, STACKBONE_POSTGRES_URL unset and no running stackbone dev session to discover it from), 1 generic (migration failure, atomic per-file rollback already ran).

stackbone db migrate create

stackbone db migrate create <name> generates a new SQL migration by diffing src/schema.ts against the migrations journal. Wraps drizzle-kit generate with an in-memory config so you do not need a drizzle.config.ts in the project. It never opens a database connection, so it works with no STACKBONE_POSTGRES_URL and no running stackbone dev. Takes no --agent.

The name positional becomes the migration filename suffix. Must match ^[a-z0-9]+(?:_[a-z0-9]+)*$ (lowercase letters, digits and single underscores, e.g. add_users, add_users_email).

Renames need an interactive terminal. A rename looks like one table or column removed and another added. The migration engine cannot tell a rename from a drop-and-create, so it asks you to confirm. Run stackbone db migrate create in your terminal (without --json or --yes) so you can answer that prompt. With --json, with --yes, in CI, or under the stackbone dev auto-migrate watcher there is no one to answer. The command then fails with a message telling you to re-run it in your terminal instead of writing no migration.

JSON payload

{
  "schema_version": 1,
  "migration_file": ".stackbone/migrations/0002_add_users.sql",
}

Exit codes: 0 ok, 4 not found (the resolved schema file does not exist on disk), 1 generic (invalid name, agent.yaml parse error, drizzle-kit failure).

stackbone db migrate status

Classify each migration as applied, pending or drifted (informational only: it reports drift instead of failing on it). Takes no --agent.

JSON payload

{
  "schema_version": 1,
  "entries": [
    { "tag": "0000_bootstrap", "state": "applied", "appliedAt": "2026-05-15T09:00:00Z" },
    { "tag": "0001_init", "state": "pending" },
  ],
}

Exit codes: 0 ok (regardless of drift), 3 no project, 1 generic (cannot reach the DB).

stackbone db query

stackbone db query [sql] runs one ad-hoc read-only SELECT against the targeted installation. The SQL comes from the positional argument, then --file <path>, then stdin, in that precedence order.

Flag Type Description
--agent string Installation id to target. Defaults to the local-dev installation linked to this project.
--file string Read the SQL from this file instead of the positional argument.

The installation's runtime enforces a single SELECT and rejects anything else server-side. It runs the statement as a stackbone_viewer role inside a transaction with a statement timeout. It also caps results: the runtime wraps a query that does not end in its own LIMIT at 1000 rows, and sets truncated: true when the query hits that ceiling.

JSON payload

{
  "schema_version": 1,
  "columns": [
    { "name": "id", "data_type": "integer" },
    { "name": "email", "data_type": "text" },
  ],
  "rows": [{ "id": 1, "email": "a@b.com" }],
  "truncated": false,
  "duration_ms": 12,
}

Exit codes: 0 ok · 3 no project or dev_not_running · 1 generic (no SQL provided, unreadable or empty --file, non-SELECT SQL rejected by the runtime).

stackbone db schemas

List the schemas and tables visible to the targeted installation, with cheap row estimates.

Flag Type Description
--agent string Installation id to target. Defaults to the local-dev installation linked to this project.

JSON payload

{
  "schema_version": 1,
  "schemas": [
    {
      "name": "public",
      "tables": [{ "name": "users", "estimated_rows": 1200 }],
    },
  ],
}

estimated_rows is null when the runtime has no estimate for that table.

Exit codes: 0 ok · 3 no project or dev_not_running · 1 generic.

stackbone db table

stackbone db table <schema> <table> browses rows of one table with cursor pagination. Both positionals must be unquoted Postgres identifiers (start with a letter or underscore, then letters, digits or underscores, max 63); the CLI validates them before making the network call.

Flag Type Description
--agent string Installation id to target. Defaults to the local-dev installation linked to this project.
--cursor string Opaque cursor from a previous page (nextCursor).
--limit number Maximum number of rows to return (1-200, default 50).
--order enum Sort order on the cursor column: asc or desc (default asc).

JSON payload Each column carries full metadata here, more than the name and type db query returns.

{
  "schema_version": 1,
  "columns": [
    {
      "name": "id",
      "data_type": "integer",
      "is_nullable": false,
      "is_primary_key": true,
      "ordinal_position": 1,
    },
  ],
  "rows": [{ "id": 1, "email": "a@b.com" }],
  "cursor_column": "id",
  "nextCursor": "eyJpZCI6MX0",
  "prevCursor": null,
}

cursor_column names the primary-key column the cursor encodes. It is null when the table has no single-column primary key and the browse fell back to LIMIT/OFFSET pagination, so the order is best effort.

Exit codes: 0 ok · 3 no project or dev_not_running · 1 generic (bad schema or table identifier, non-integer --limit, --order other than asc or desc).

BUILT WITH ❤️ FROM CANADA AND SPAIN