Skip to content

Concepts

Project Configuration

Configure your SQLBuild project with sqlbuild_project.toml and sqlbuild_local.toml.

SQLBuild projects are configured with two files in the project root:

  • sqlbuild_project.toml - shared project configuration, committed to version control
  • sqlbuild_local.toml - local developer overrides, gitignored

Macro, constant, and enum visibility is not configured in either file. Declaration scopes use filesystem conventions; see Declaration Scopes.

Most projects need only one committed sqlbuild_project.toml. Define shared targets such as dev and prod there, including clone policies and team-wide defaults. Do not maintain separate complete project files for each environment.

Create sqlbuild_local.toml only when a developer or execution environment needs different target selection, credentials, schemas, adapter settings, or variables. SQLBuild loads it automatically and merges its explicitly configured values over the shared project config.

project/
sqlbuild_project.toml # committed: shared targets and behavior
sqlbuild_local.toml # gitignored: this developer's overrides

Add the local file to .gitignore:

sqlbuild_local.toml

A complete example:

name = "waffle_shop"
adapter = "duckdb"
default_target = "dev"
[connections.local]
database = "waffle_shop_control.duckdb"
[settings]
default_audit_severity = "warn"
[defaults]
materialized = "table"
[constants]
collection_rendering = "value_list"
[targets.prod]
connection = "local"
schema = "prod"
[targets.dev]
connection = "local"
schema = "dev"
[path_defaults.staging]
materialized = "view"
Field Description
name Project name. Used in fingerprint tracking and manifest generation.
adapter Database adapter: duckdb, motherduck, snowflake, bigquery, databricks, postgres, or sqlserver. See Adapters.
default_target Name of the target to build against when none is selected (see Targets).

Define reusable connections under [connections.<name>], then reference one by name from each target. Connections own endpoint, authentication, and compute settings. Targets own the authoritative database, schema, variables, and operational policy.

[connections.local]
database = "my_project.duckdb"
[targets.dev]
connection = "local"
schema = "dev"

Multiple targets can reuse one connection while keeping separate namespaces and policies:

[connections.warehouse]
account = "my_org-my_account"
user = "${ENV:SNOWFLAKE_USER}"
password = "${ENV:SNOWFLAKE_PASSWORD}"
warehouse = "TRANSFORM_WH"
[targets.prod]
connection = "warehouse"
database = "ANALYTICS"
schema = "PROD"
[targets.dev]
connection = "warehouse"
database = "ANALYTICS"
schema = "DEV_ALICE"

A database or schema present in a named connection is connection/session metadata only; it does not satisfy the mandatory namespace strategy for a named target. Put the target’s authoritative database and schema on [targets.<name>]. SQLBuild validates connection references while loading configuration, without opening a warehouse connection, and reports an unknown targets.<name>.connection name as an offline configuration error.

For migration only, SQLBuild still maps legacy [connection] to an implicit connection and legacy [targets.<name>.connection] blocks to target-specific implicit connections. These forms are compatibility syntax, not the canonical format for new or updated projects.

A target is a named build context - the database and schema you build into, plus execution policy (for example dev and prod). Each target references a named connection and can configure:

Field Description
schema Schema for all models in this target; required for named targets
loader_schema Default write schema for managed source loaders; falls back to schema
database Database for all models in this target
connection Name from [connections.<name>] used for endpoint, authentication, and compute
vars Target-specific project variables
defer_sources_to Target name to read managed source data from (see Loaders)
clone Clone policy (see below)
[connections.warehouse]
account = "my_org-my_account"
warehouse = "TRANSFORM_WH"
[targets.prod]
connection = "warehouse"
database = "analytics"
schema = "analytics_prod"
loader_schema = "raw_prod"
defer_sources_to = "prod"
[targets.dev]
connection = "warehouse"
database = "analytics"
schema = "analytics_dev"
loader_schema = "raw_dev"
defer_sources_to = "prod"
[targets.staging]
connection = "warehouse"
database = "analytics"
schema = "staging"

Managed loader writes use the active target’s loader_schema, falling back to its model schema. Managed source reads use the target named by defer_sources_to, or the active target itself when deferral is omitted. In the example, load --target dev writes to raw_dev, while models built in dev read from raw_prod.

SQLBuild rejects targets on the same warehouse/database when their managed loader writes resolve to the same schema. Two targets may read the same schema through deferral, but they cannot both own loader writes there.

The active target is determined by (in order of precedence):

  1. --target on the command line (highest priority)
  2. sqlbuild_local.toml target field
  3. default_target in sqlbuild_project.toml
  4. No target (models build to the default schema)

A typical developer keeps shared target definitions in sqlbuild_project.toml and selects their normal target once in the optional local file:

sqlbuild_local.toml
target = "dev"
[targets.dev]
connection = "warehouse"
schema = "dev_alice"
loader_schema = "raw_alice"
[connections.warehouse]
user = "alice"
password = "${ENV:SNOWFLAKE_PASSWORD}"

Commands then use dev automatically. An explicit command such as sqb build --target prod still takes precedence for that invocation.

Targets can declare whether they allow cloning to or from:

[targets.prod]
schema = "prod"
[targets.prod.clone]
allow_as_clone_origin = true
allow_as_clone_destination = false
[targets.dev]
schema = "dev"
[targets.dev.clone]
allow_as_clone_origin = false
allow_as_clone_destination = true

Both policies default to false. sqb clone --from prod --to dev requires allow_as_clone_origin = true on prod and allow_as_clone_destination = true on dev.

Project-wide model defaults. Any field you can set in a MODEL() header can be set here as a default:

[defaults]
materialized = "table"
incremental_strategy = "delete_insert"
replay_on_change = "full"
tags = ["managed"]

These apply to all models unless overridden by path defaults or the model’s own MODEL() header.

Collection constants default to parenthesized SQL value lists. Set a project-wide default when lists and sets should instead compile to first-class adapter-native arrays:

[constants]
collection_rendering = "array"

collection_rendering accepts value_list (the SQLBuild default) or array. A public constant’s render_as field or a model-local constant(...) wrapper overrides the project setting. The complete precedence order is declaration override, project setting, then value_list.

This setting does not make unsupported adapter features portable. In particular, SQL Server rejects native arrays, and BigQuery rejects nested arrays. See Collections and Rendering for syntax, adapter output, and value-list usage constraints.

Per-directory model defaults. Useful for applying different config to different parts of your project:

[path_defaults."models/staging"]
materialized = "view"
tags = ["staging"]
[path_defaults.marts]
materialized = "table"
tags = ["marts"]
replay_on_change = "full"

Path matching uses the path below models/. A model at models/staging/stg_orders.sql matches the staging path default.

Configuration is layered in this order, with later layers overriding earlier ones:

  1. Project defaults (defaults)
  2. Path defaults (path_defaults) - if the model’s path matches
  3. MODEL() header - the model’s own config

Most keys are overridden by the more specific layer, but three merge instead:

  • tags are unioned across layers. A model with tags [marts] in its header that matches a path default with tags [managed] will have both tags.
  • row_diff_exclude_columns lists are unioned across layers.
  • row_diff_tolerances mappings are deep-merged across layers, so a header tolerance for one column adds to (rather than replaces) tolerances declared in defaults or path defaults.

Diff sampling values use ordinary replacement precedence. row_diff_sample_rows = 0 explicitly disables an inherited sample for a path or model. CLI --sample-rows, --sample-seed, and --exhaustive override the compiled configuration for one invocation.

Global feature toggles:

[settings]
sql_analysis = true
query_change_tracking = true
column_contract_mode = "implicit"
concurrency = 1
microbatch_concurrency = false
auto_load_sources = true
table_promotion_mode = "staged"
default_audit_severity = "warn"
default_audit_run_scope = "final"
Field Default Description
sql_analysis true Enable SQL syntax analysis, column binding, type inference, output inference, and semantic validation at compile time
query_change_tracking true Track query fingerprints for change detection
column_contract_mode implicit Controls whether column declarations on models without a contract declaration activate static shape/nullability validation. implicit preserves that validation; explicit treats columns as metadata and audit attachment unless the model declares contract enforced. Model-level contract enforced and contract none override this setting. Explicit type enforcement remains independent. See Contracts.
concurrency 1 Maximum parallel model execution (currently serial only)
microbatch_concurrency false Explicitly permit models with batch_concurrency > 1; concurrent batches use immutable coordination facts
auto_load_sources true Automatically run source loaders before building dependent models during sqb build. See Loaders.
table_promotion_mode adapter default staged (CTAS to staging, audit, then promote) or immediate (CTAS directly to target, then audit)
default_audit_severity warn Default severity for audits: warn or error
default_audit_run_scope final Default run scope for audits: final or delta_and_final
  • staged (default for most adapters): Materializes into a staging table, runs audits, then swaps into the target. If audits fail, the production table is untouched.
  • immediate: Creates the table directly at the target location. Audits run after materialization. Simpler but no pre-promotion safety net.

Rules configuration belongs in the shared sqlbuild_project.toml so local and CI compilation use the same checks:

[rules]
select = ["SQBRSQL", "SQBRGRAPH", "XSQBRARCH"]

Rules are opt-in. Exact codes activate individual checks and prefixes activate a family. Built-in codes begin with SQBR; repository-defined codes begin with XSQBR. See Compiler-integrated Rules for configuration, authoring, and suppressions.

Variables are simple string substitutions available in model SQL via the @@name syntax:

[vars]
schema_prefix = "analytics"
retention_days = "90"

Target-specific variables override project-level ones:

[vars]
schema_prefix = "analytics"
[targets.prod.vars]
schema_prefix = "prod_analytics"

See Macros for details on variable substitution and how variables interact with macros.

Configuration for the sqb janitor command, which archives and then deletes stale warehouse relations:

[janitor]
enabled = false
retention_days = 14
archive_retention_days = 14
delete_tracked_only = true
exclude_patterns = ["audit_*", "tmp_*"]
Field Default Description
enabled false Whether janitor is active
retention_days 14 How many days a relation must be stale before it is archived
archive_retention_days 14 How many days an archive is kept before it is deleted. 0 deletes it in the same run.
delete_tracked_only true Only clean relations that appear in fingerprint tracking
exclude_patterns [] Glob patterns for relations to skip
direct_state_history_versions 20 How many state-history rows to keep per identity in _sqlbuild_fingerprints and _sqlbuild_source_freshness

See janitor for the archive and delete lifecycle.

Configuration for scenario snapshot capture safety limits and local type overrides:

[scenario.snapshot_limits]
max_rows_per_relation = 10000
max_total_rows = 50000
max_bytes_per_relation = 10485760
max_total_bytes = 52428800
Field Default Description
max_rows_per_relation none Maximum rows per captured relation
max_total_rows none Maximum total rows across all relations in one scenario
max_bytes_per_relation none Maximum JSONL bytes per relation file
max_total_bytes none Maximum total JSONL bytes per scenario

Local type overrides for DuckDB replay are configured per adapter dialect:

[scenario.local_type_overrides.snowflake]
"OBJECT" = "JSON"
"ARRAY" = "JSON"

See Scenarios for details on local type overrides and capture limits.

Configuration for running SQLBuild alongside an existing dbt project:

[dbt]
project_dir = "../dbt_project"
profiles_dir = "../profiles"
target_path = "../dbt_project/target"
target = "dev"
Field Description
project_dir Path to the dbt project root (where dbt_project.yml lives)
profiles_dir Path to the directory containing profiles.yml
target_path Path to dbt’s target/ directory (where manifest.json is written)
target dbt target name override (optional)

Paths can be absolute or relative to the SQLBuild project root. See Using SQLBuild with dbt for setup and usage details.

Configuration for AI agent skill file installation:

[skills]
targets = ["agents", "claude"]
auto_update = false
Field Default Description
targets ["agents", "claude"] Agent targets to install. OpenCode consumes .agents; use opencode only as an explicit override.
auto_update false Refresh stale SQLBuild-owned generated files from the installed package during normal commands; custom collisions are never overwritten.

See skills CLI reference for usage details.

Local developer overrides. This optional file is loaded automatically and should be gitignored. Only put values that differ from the shared project configuration here.

target = "dev"
[targets.dev]
schema = "dev_alice"
loader_schema = "raw_alice"
[connections.local]
database = "my_local.duckdb"
[settings]
sql_analysis = false
concurrency = 4
[vars]
debug_mode = "true"
Field Description
target Override which target is active for this developer
adapter Override the database adapter (e.g. use DuckDB locally while prod uses Snowflake)
connections Override named connection fields; entries merge by connection name
settings Override global settings (only explicitly set fields take effect)
vars Developer-specific variable overrides (merged on top of project + target vars)

Project and local configuration merge named connections by name and merge their explicitly configured fields. Target blocks merge the same way and may override the connection reference, database, schema, loader_schema, variables, source deferral, and policy fields. Unspecified values continue to come from sqlbuild_project.toml; a local reference to an unknown merged connection still fails offline during configuration loading.

This replaces the common dbt pattern of switching profiles or setting environment variables to change targets. Each developer sets their target, named connection, and preferences once in sqlbuild_local.toml and it persists across sessions.