CLI Reference

On this page

Single binary. All commands listed below.

Commands

CommandDescription
runExecute the pipeline
sandboxPreview changes before committing
initCreate a new project
newCreate a model file
editOpen file in $EDITOR
sqlRun arbitrary SQL
queryQuery a table directly
statsProject overview
historyRun history for a model
describeModel details and schema
describe blueprintBlueprint API contract introspection
validateStatic validation of models and blueprints
lineageColumn-level lineage
authOAuth2 provider management
checkpointRun all maintenance
flushFlush inlined data to Parquet
mergeMerge small Parquet files
expireExpire old snapshots
cleanupDelete unreferenced files
orphanedDelete orphaned files
rewriteRewrite files with many deletes
--jsonMachine-readable output
versionPrint version

run

ondatrasql run                    # All models in DAG order
ondatrasql run staging.orders     # Single model

sandbox

ondatrasql sandbox                # All models
ondatrasql sandbox staging.orders # One model

See Preview Changes.


init

ondatrasql init

new

ondatrasql new staging.orders.sql
ondatrasql new raw.api_users.sql
ondatrasql new sync.hubspot.sql

edit

ondatrasql edit staging.orders      # Model
ondatrasql edit env                 # .env
ondatrasql edit catalog             # config/catalog.sql
ondatrasql edit macros/audits       # config/macros/audits.sql
ondatrasql edit macros/constraints  # config/macros/constraints.sql
ondatrasql edit macros/warnings     # config/macros/warnings.sql
ondatrasql edit macros/helpers      # config/macros/helpers.sql
ondatrasql edit macros/masking      # config/macros/masking.sql
ondatrasql edit variables/constants # config/variables/constants.sql
ondatrasql edit variables/global    # config/variables/global.sql
ondatrasql edit variables/local     # config/variables/local.sql
ondatrasql edit secrets             # config/secrets.sql
ondatrasql edit settings            # config/settings.sql
ondatrasql edit sources             # config/sources.sql
ondatrasql edit extensions          # config/extensions.sql

sql

ondatrasql sql "SELECT COUNT(*) FROM staging.orders"
ondatrasql sql "SELECT * FROM staging.orders" --format json
ondatrasql --json sql "SELECT * FROM staging.orders"     # Versioned envelope

ondatrasql --json sql returns a versioned envelope (distinct from --format json, which is a row-oriented dump):

{"schema_version": 1, "query": "...", "rows": [...], "warnings": []}

warnings is always emitted ([] when empty) so typed clients decode unconditionally — same shape contract as history --json and run --json.

query

ondatrasql query staging.orders
ondatrasql query staging.orders --limit 10 --format csv
ondatrasql --json query staging.orders                   # Versioned envelope
FlagDescription
--formatcsv, json, markdown (or md)
--limitMaximum rows returned

ondatrasql --json query returns a versioned envelope:

{"schema_version": 1, "table": "...", "limit": N, "rows": [...], "warnings": []}

Same contract as --json sql above — warnings is always present as an array.


stats

ondatrasql stats

history

ondatrasql history staging.orders
ondatrasql history staging.orders --limit 5
ondatrasql --json history staging.orders         # Versioned envelope

--json history emits a versioned envelope with snake_case keys:

{
  "schema_version": 1,
  "model": "staging.orders",
  "limit": 50,
  "runs": [
    {
      "id": 12345,
      "time": "2026-01-15T08:00:00Z",
      "model": "staging.orders",
      "kind": "merge",
      "run_type": "incremental",
      "rows": 421,
      "duration_ms": 1830,
      "run_id": "dag-2026-01-15-08-00-00"
    }
  ],
  "warnings": []
}

warnings is always emitted ([] when empty) so typed clients can decode unconditionally. Numeric fields (id, rows, duration_ms) are JSON numbers, not strings — parse failures from the underlying SQL row are surfaced in warnings rather than silently coerced to 0.

describe

ondatrasql describe staging.orders

JSON output (--json) field names use snake_case (target, kind, materialization, source_file, etc.) for consistency with the rest of the machine-readable surface. v0.31 changed this from PascalCase — pipelines that parsed the previous output will need to migrate.

The output includes a blueprint field cross-linking to describe blueprint <name> when the model fetches from a registered lib function.

describe blueprint

Static introspection of a blueprint’s API contract — fetch/push args, function signatures, supported kinds, HTTP endpoints. Reads only lib/, never opens the catalog.

ondatrasql describe blueprint                    # List all blueprints
ondatrasql describe blueprint riksbank           # Full description (human)
ondatrasql --json describe blueprint riksbank    # Structured JSON
ondatrasql --json describe blueprint riksbank --fields=fetch.args,fetch.mode

JSON output is the primary form for agent use. The --fields flag projects through dotted paths so agents fetch only what they need; unknown paths return an error rather than empty values. Schema is versioned via schema_version at the top level — bump signals a breaking change.

--fields projection emits null for schema-valid paths whose underlying struct field is omitempty-zero (e.g. fetch.poll_interval on a sync blueprint). This is not the same shape as the unprojected detail JSON, which omits zero fields via omitempty. The trade-off is intentional: a null answer carries more information than an absent key (it confirms the path is valid). Clients that want the unprojected shape should call describe blueprint <name> without --fields.

validate

Runs all static validators against models and blueprints without opening the catalog. Catches what would otherwise surface mid-execution: parser issues, strict-fetch / strict-push contract violations, LIMIT/OFFSET in pipeline models, blueprint API-dict errors, DAG cycles, and external-reference INFO findings.

ondatrasql validate                              # Full project (models/ + lib/)
ondatrasql validate models/raw/foo.sql           # One file
ondatrasql --json validate                       # Buffered JSON report
ondatrasql validate --output ndjson              # Stream one line per file
ondatrasql validate --strict                     # Promote WARN to exit 1

Severity levels: BLOCKER (would fail at run — includes deprecated directives, which the parser rejects outright), WARN (validate’s own environment is degraded so downstream findings may be incomplete — extension load failed, blueprint parse error, walk skipped, etc.), INFO (cannot be verified without external state, e.g. external table references). Each finding carries a stable rule-ID like parser.multi_statement or strict_fetch.cast_required — these are public contract, never renamed without a schema_version bump.

validate.* WARN findings always trigger exit 1 regardless of --strict because they signal that validate’s own analysis was incomplete — CI consumers can’t treat a degraded run as a clean one.

lineage

ondatrasql lineage overview              # All models
ondatrasql lineage staging.orders        # One model
ondatrasql lineage staging.orders.total  # One column

See Lineage & Metadata.


auth

ondatrasql auth                   # List providers
ondatrasql auth google-sheets     # Authenticate
ondatrasql auth fortnox           # Authenticate

OAuth2 uses your own app credentials — set <PREFIX>_CLIENT_ID, <PREFIX>_CLIENT_SECRET, <PREFIX>_AUTH_URL, <PREFIX>_TOKEN_URL, and <PREFIX>_SCOPE in .env. (The hosted-broker “managed” flow via ONDATRA_KEY / oauth2.ondatra.sh was removed in v0.36.0.)

Alternatively, skip ondatrasql auth and let an orchestrator inject a pre-obtained access token via ONDATRA_OAUTH_TOKEN_<PREFIX> — it takes precedence over the stored credentials (see Set Up OAuth).

Refresh tokens are stored in the state.tokens table inside the state catalog (see config/state.sql) and auto-refresh on every pipeline run. The state catalog is encrypted at rest via DuckDB’s ENCRYPTION_KEY option using ONDATRA_STATE_KEY from .env.

ondatrasql auth requires config/state.sql to exist — run ondatrasql init first in a fresh project.

See Environment Variables for OAuth2 variable reference.


–json

Machine-readable output on stdout. Human output (status banners, progress, warning logs from internal subsystems) is routed to stderr.

ondatrasql run --json 2>/dev/null | jq -s '.'

Stream separation is required. --json only guarantees clean JSON on stdout; stderr may contain unstructured warnings (e.g. context cancellations, transient I/O messages from external lib calls). Tooling that merges streams (2>&1, default systemd StandardOutput=journal + StandardError=journal) WILL produce non-JSON lines mixed into the stream. For service units, redirect stderr to a separate sink:

[Service]
StandardOutput=file:/var/log/ondatrasql.json
StandardError=file:/var/log/ondatrasql.err

The --json stream may carry two record shapes, distinguishable by the presence of kind:

  • Model-result envelopes (most common). One per model that the runner attempted. No kind field; carry schema_version: 2.
  • DAG-level warnings. Non-fatal issues not tied to a specific model. Tagged kind: "dag_warning"; carry their own independent schema_version: 1. See the next subsection.

schema_version is per-envelope: each shape versions independently, so a bump in one doesn’t force a re-encode of the other.

Model-result envelope

FieldDescription
schema_version2. Bump signals breaking shape change. (v2 made errors/warnings always-emit instead of omitempty.)
modelTarget table
kindtable, append, merge, scd2, tracked
run_typeskip, backfill, incremental, full
run_reasonWhy this run type was chosen (omitted when empty)
rows_affectedRows written (0 for skip)
duration_msExecution time
statusok, skip, or error
errorsError messages — always emitted as [] when none
warningsSchema evolution / validation warnings — always emitted as [] when none
dag_run_idDAG run correlation ID (omitted when empty)
sandboxtrue for sandbox mode runs (omitted when false)

DAG-level warning envelope

Both run --json and sandbox --json may emit a separate envelope tagged with kind: "dag_warning" for non-fatal issues that aren’t tied to a specific model (currently only state-store GC failures during the pre-flight — RunGC’s orphan recovery + stale-claim reap). Typed consumers should branch on kind:

{"schema_version": 1, "kind": "dag_warning", "source": "_gc", "message": "state GC: ..."}
FieldDescription
schema_version1. Bump signals breaking shape change.
kinddag_warning — distinguishes from model-result envelopes
source_gc (more sources may be added; consumers should treat unknown values as opaque)
messageHuman-readable warning text

A dag_warning always coincides with exit 1 since GC failure is a non-invocation runtime error per the exit-code contract below.

version

ondatrasql version

Exit codes

Exit codes follow the eslint/ruff convention and apply to every subcommand, not just validate:

  • 0 — clean (command succeeded, no findings, no errors)
  • 1 — findings or runtime failure: any validate BLOCKER, any validate.* WARN (degraded run — extension load failed, blueprint parse error, walk skipped, etc.), any WARN with --strict, OR any non-invocation runtime error (DuckDB error, network failure, write error, etc.)
  • 2 — invocation error: bad flag, unknown command, unexpected/extra args, file or scope outside models//lib/, invalid CLI value (e.g. --limit=abc)

validate.* WARN findings always trigger exit 1 regardless of --strict because they signal validate’s own analysis was incomplete — CI consumers can’t treat a degraded run as a clean one.

The exit-2 contract is enforced for the following surfaces (regression-tested in cli_contract_test.go): version, init, stats, lineage, history, query, sql, describe, describe blueprint, edit, new, auth, validate, plus the unknown_command fallthrough. CI scripts that gate on exit code can rely on 1 vs 2 to distinguish “real failure” from “you typed it wrong”.


Maintenance

ondatrasql checkpoint             # Run all maintenance in order
ondatrasql flush                  # Flush inlined data to Parquet
ondatrasql merge                  # Merge small Parquet files
ondatrasql expire                 # Expire old snapshots
ondatrasql cleanup                # Delete unreferenced files
ondatrasql orphaned               # Delete orphaned files
ondatrasql rewrite                # Rewrite files with many deletes

See Maintain DuckLake Storage.