Skip to content

Developer commands ​

Use blueprint dev to manage local servers and blueprint app to run the interactive client. See local development.

The commands below belong to the product’s BlueprintServer command runner. It handles serving, generation, and installation—not local process supervision. For app-plus-server startup, individual server control, logs, and watch, see development profiles. Executable paths and target names are supplied by the consuming product.

Product executable ​

A product can expose its existing target registry through a small entrypoint:

dart
import 'dart:io';
import 'package:vyuh_blueprint_server/vyuh_blueprint_server.dart';
import '../lib/targets.dart'; // Product-owned file exporting blueprintRegistry.

Future<void> main(List<String> args) async {
  exitCode = await BlueprintServer.runRegistry(blueprintRegistry, args);
}

blueprintRegistry is a BlueprintRegistry containing the product's BlueprintTarget instances. Each target supplies its Blueprint and executable bindings. This snippet wires an existing assembly; it does not create a database or authentication provider.

If the product saves this entrypoint as bin/blueprint.dart, use:

sh
dart run bin/blueprint.dart --help
dart run bin/blueprint.dart <target> --help
dart run bin/blueprint.dart targets --json
dart run bin/blueprint.dart <target> doctor --json
dart run bin/blueprint.dart <target> serve --host 127.0.0.1 --port 8080

Replace <target> with a name listed by registry help. Passing only a target defaults to serve. A single-target executable calling BlueprintServer(target: target).run(args) omits the target argument.

Host and port precedence is command flag, then HOST / PORT, then 0.0.0.0 / 8080. The endpoint path comes from the Blueprint declaration.

Generate and install ​

Append these arguments after the product executable and target:

ArgumentsBehavior
emit --out <dir> --schema-dir <NN_product>Write the product SQL tree under an empty <dir>
conformance [--out <file>] [--stdout]Write the Define/Execute/Prove conformance artifact
install or install allInvoke database installation, then seeding
install sqlInvoke database installation only
install seedInvoke seeding only
install runtimeInstall runtime artifacts through the optional runtime binding

emit writes the tree laid out as the Supabase folder: schemas/<NN_product>/10_engine/ (the target's schema contributions), 20_model/, 30_runtime/, 90_runtime_contract/, and seed/<product>/ (the target's seed contributions). Every file starts with -- GENERATED from <product> blueprint and stays under 1,000 lines. It exits 64 for a usage error or a <dir> that is a file or not empty, 65 when the Blueprint or a contribution is rejected (nothing is written then), and 73 when writing fails. Contributions may write only into schemas the model owns, the runtime schema, and the schemas an engine declares in ownedSchemas.

emit is the only generation path. An installing binding applies the same file set, schemas/ files then seed/ files, in emitted order.

Write the generated SQL into Supabase ​

The installed CLI cannot import product code, so blueprint db emit runs the product's own target command and places its output in the Supabase folder:

sh
blueprint db emit                 # write the generated folders
blueprint db emit --check         # compare only; exit 1 on any difference
blueprint db emit --product plant --profile blueprint.dev.json

Declare the product in the development profile: its target command and its SQL.

json
{
  "databases": {
    "plant": {
      "command": ["dart", "run", "bin/blueprint.dart", "plant"],
      "install": [
        "supabase/schemas/plant/00_base/*.sql",
        "supabase/seed/plant/reference/*.sql"
      ],
      "provision": ["supabase/seed/plant/provision/*.sql"]
    }
  }
}

install (schemas and reference seeds) and provision (per-tenant defaults) are ordered globs relative to the profile directory, expanded as supabase db reset expands sql_paths, each file once. provision requires install. A product that declares install must not also be installed from config.toml: sql_paths may list only its provision files, and any other file under schemas/<NN>_<product>/ or seed/<product>/ (or in its install list) that sql_paths selects is a configuration error (exit 64) naming the entries. blueprint db status and reset read the same lists (--profile, default blueprint.dev.json); reset replays the install list, then the provision list.

A product is declared only here. supabase/config.toml does not define one: if its sql_paths list the generated files of a product the profile does not declare, the command exits 64 and names the databases.<product>.install key to add. --product is required unless the profile declares exactly one product.

The profile key must be the Blueprint name. The target names seed/<product>/ and the -- GENERATED from <product> blueprint header after its Blueprint, so a target whose emitted headers or seed folder name another product stops the command with exit 65, naming both names.

The CLI runs the command in the profile directory with emit --out <tmp> --schema-dir <NN_product> appended, streams its output to stderr, and then:

  • Checks that the product's profile install list selects every emitted file, expanded as supabase db reset expands sql_paths. Uncovered files are listed and nothing is written.
  • Always manages the four canonical folders schemas/<NN_product>/{10_engine,20_model,30_runtime,90_runtime_contract}/, whether or not the target emitted into them, and the files in seed/<product>/ that start with -- GENERATED from <product> blueprint. A file or directory in a canonical folder without that header, or a header-less file at a path the target emits under seed/<product>/, is hand-owned; the command names it and changes nothing. Other files and directories in seed/<product>/ are left alone.
  • Without --check, writes missing and changed files, then removes generated files the target no longer emits, including a whole folder it stopped emitting and stale generated seed files. A canonical folder left empty is deleted. New files are written before anything is removed, so if writing fails (exit 73), running the command again completes it.
  • With --check, writes nothing and lists missing, extra and changed paths.
ExitMeaning
0Written, or --check found no difference
1--check found a difference
64Usage, profile or config error (including a missing databases.<product> entry, sql_paths listing a file of a product that declares install, or sql_paths listing a product's generated files that the profile does not declare), or a hand-owned file in a generated location
65An emitted file that the install list does not select, that is outside the generated locations or lacks the header, or that names another product
69The target command could not be started
70The target command exited non-zero; the message target command exited N gives its code
73A file-system error while writing

db emit needs no database.

conformance writes JSON to --out, or to the target's conformanceOutput (default build/blueprint/conformance.json); --stdout prints it instead. It exits 0 when the declaration is conformant and 65 when it is not; the artifact is written either way.

Installation changes the configured database and follows the product binding's semantics. It is not a universal migration command. install all and install sql accept --reset, which resets managed objects; use the product's forward migration procedure for existing data. A normal dev launcher must not silently reset data.

What is still product-specific ​

The server runner owns serve, emit, install, conformance, and structural target doctor checks. The separate CLI package owns dev, individual serve, targets, doctor, status, logs, stop, watch, and trusted local-template create. It delegates target execution and validation to the product registry.

Products still supply executable/provider bindings, health routes, database setup and migrations, and a development profile. The CLI does not provision identity or storage. See development profiles and the local development workflow.

Migrations ​

Database commands work on one target, printed first as Target: ... without its password: --database-url URI, else BLUEPRINT_DATABASE_URL (or the variable --database-env names), else the checkout's local Supabase database. The standalone migration snapshot|plan|check|apply|status and seed commands no longer exist; a product's migrations are the files below.

A product's migration files ​

A product whose profile entry databases.<product> declares install ships as migration files in supabase/migrations/, which Blueprint applies and records in its own ledger, blueprint_migrations.product_migrations, keyed by product and version. See product migrations for the model, the ledger, the state store, deployment and a worked example.

sh
blueprint db migration new baseline --product plant   # first: the baseline
blueprint db migration new add_notes --product plant  # later: a delta
blueprint db migration new --check --product plant    # local: exit 1 when one is needed
blueprint db reset --allow-reset [--from-tree] [--force]  # replay migrations, then provision
blueprint db migration up --product plant [--dry-run]     # apply the pending files
blueprint db migration up --product plant --allow-remote  # deploy: BLUEPRINT_DATABASE_URL
blueprint db migration adopt --product plant              # once: move rows out of Supabase's history

up applies each pending file in one transaction with its ledger row, prints the target as host:port/database (never the URI), and runs no provision SQL. Deploy these files with up, not supabase db push, which records them in the Supabase CLI's own history instead of the ledger.

FlagCommandEffect
--allow-destructivemigration newConsent to every removal, and allow destructive operations on objects no definition derives
--decisions FILEmigration newAnswers for changes that need one (renames, conversions, label maps, redirects, removals)
--accept-renames[=old->new,...]migration newAccept every detected rename, or only the listed ones
--acknowledge-unplannedmigration newRecord unplanned statements you migrated by hand
--checkmigration newWrite nothing; exit 1 when a migration is needed (local database only)
--forcemigration new, resetDrop or capture even while other schemas depend on the product
--from-treeresetInstall the product's files and record no migration in the ledger
--dry-runmigration up, migration adoptList what would be applied or moved; change nothing
--allow-remotemigration up, migration adopt, install, resetAllow a host that is not a loopback address
--allow-out-of-ordermigration upApply pending files older than the newest applied one
--include-retiredmigration adoptAlso move the product's rows that have no migration file here
--database-env NAMEevery database commandDatabase URI variable (default BLUEPRINT_DATABASE_URL, else the local Supabase database)
--database-url URIevery database command but migration up and adoptThe database; wins over the variable

migration new exits 0 (written, no changes, --check clean), 1 (--check found drift), 64, 65 (review needed or capture refused), 69, 70 (the product command failed, or wrote no definition or migration steps) or 73. migration up exits 0 (applied, up to date, dry run), 1 (a file failed, or the database reported another error), 64, 65 (refused with nothing changed, or a schema is missing after the files were applied) or 69 (unreachable, a failed connection, or a lock held elsewhere for 10 seconds). migration adopt exits 0, 1 (the database reported an error), 64, 65 (a row of the product has no migration file here; see --include-retired) or 69.

Supported changes and limitations ​

The PostgreSQL migration engine supports ordinary tables, columns, defaults, nullability, constraints, indexes, views, functions, triggers, policies, sequences, and object and column grants. Some objects are rebuilt inside the transaction, so rehearsal should also measure locking and runtime against production-sized data.

Identity columns, partitioned tables, materialized views, domains, and default privileges are blocked. Changes to enum labels require a staged replacement and are currently blocked too. Cross-schema renames, automatic lifecycle state mappings, and arbitrary function dependency changes are not complete. Do not use this as an unattended production upgrader.