CLI Reference

The definite CLI is the single control plane for an on-prem deployment.

Install

# prebuilt binary (recommended)
curl -fsSL https://storage.googleapis.com/definite-public/definite-onprem/install.sh | sh

The install script detects your OS and architecture, downloads the matching prebuilt binary from GitHub Releases, verifies its checksum, and places definite on your PATH. Cross-platform binaries are published for macOS (arm64, x86_64) and Linux (arm64, x86_64) on every release.

To pin a version or install behind a proxy, download the asset directly from the Releases page and place it on your PATH yourself.

# from source (for development)
git clone https://github.com/definite-app/definite-onprem
cd definite-onprem
cargo build --release
./target/release/definite --help

The CLI shells out to kubectl and helm. Both must be installed and on PATH on the machine running definite. See prerequisites.md for the full dependency checklist.

Commands

definite init

Deploy Definite from a config.yaml.

definite init --config config.yaml
DEFINITE_ONPREM_SETUP_TOKEN=... definite init --config config.yaml
definite init --config config.yaml --requested-slug analytics
definite init --config config.yaml --dns-mode customer-owned --hostname analytics.customer.com
definite init --config config.yaml --dry-run            # render values, don't apply
definite init --config config.yaml --skip-preflight     # not recommended
definite init --config config.yaml --wait=false         # don't wait for pods ready

By default, init uses Definite-brokered DNS and licensing: it discovers the ingress-nginx load-balancer target, sends it to the broker with the release version, optional requested slug, and setup token, waits for DNS readiness, then uses the returned FQDN and license key in the Helm install. If no setup token is configured, the CLI automatically acquires one from cloud credentials. On GCP, audience-bound setup attestation can use a service account from DEFINITE_ONPREM_GCP_ATTESTATION_SERVICE_ACCOUNT, GOOGLE_IMPERSONATE_SERVICE_ACCOUNT, CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT, or gcloud's auth/impersonate_service_account config; the token is minted with an email claim for broker verification.

Use --dns-mode customer-owned --hostname ... only when the customer will manage DNS themselves. Do not use *.nip.io for tls: cert_manager; the CLI rejects that combination because Let's Encrypt rate-limits nip.io globally.

After DNS/license setup, init runs preflight checks, then helm upgrade --install with the rendered values.

definite doctor

Run preflight diagnostic checks without deploying.

definite doctor --config config.yaml
definite doctor --config config.yaml --skip postgres,object_store

Checks:

CategoryWhat it does
postgresConnects with tokio-postgres, runs SELECT version(), warns on <15
kubernetesRuns kubectl cluster-info, reports the control plane endpoint
object_storeValidates config shape (a real PutObject round-trip is not yet performed)
llmFor Anthropic: real API ping. For others: config validation only
agent_sandboxChecks the agent-sandbox CRDs are installed
licenseWarns if config.yaml has no license block (the deployment would be unlicensed and product API routes disabled). A warning, never a hard failure.

definite status

kubectl get pods,svc,ingress scoped to the deployment namespace.

definite status --config config.yaml

definite upgrade

Re-render and re-apply the chart with the current CLI's bundled version.

definite upgrade --config config.yaml
definite upgrade --config config.yaml --dry-run

Take an on-demand Postgres backup first. An upgrade applies pending schema migrations on API boot; a bad migration with no labelled restore point is unrecoverable. Automatic daily backups should already be on (see prerequisites.md), but take a fresh, named snapshot immediately before upgrading:

  • RDS: aws rds create-db-snapshot --db-instance-identifier <id> --db-snapshot-identifier <id>-preupgrade-$(date +%Y%m%d-%H%M%S)
  • Cloud SQL: gcloud sql backups create --instance=<instance>
  • Azure / self-managed: trigger an on-demand backup or pg_dump per backup-restore.md.

Row order after upgrade. Releases that include #1207 run pipeline and SDK DuckDB engines with preserve_insertion_order = false. If a pipeline depends on row order without ORDER BY — including a table created with CREATE TABLE ... AS SELECT ... ORDER BY and then read back without ORDER BY — set lakehouse.duckdb.preserveInsertionOrder: true, or add ORDER BY at read time. See transformations.md for details.

definite logs

Stream logs from a component.

definite logs api
definite logs api --follow
definite logs job-runner --tail 500

Components: api, frontend, job-runner. Lakehouse queries run as embedded DuckDB inside the API and job-runner pods, so there is no separate lakehouse component to tail.

definite discover

Search the ontology and semantic layer together before answering an analytical question. Multiple unquoted terms are joined into one query.

definite discover visitors sessions traffic website
definite discover monthly visitors --format json

The response keeps ontology and semantic hits separate, bounds each result set, reports per-layer errors and truncation, and suggests next actions. A partial layer failure still exits successfully with the healthy layer's results; only failure of both layers makes the command fail.

definite run query

Run SQL against the lakehouse, or inspect query history.

# Run a SQL statement (positional, a file, or '-' for stdin).
definite run query "select count(*) from analytics.sales"
definite run query --file ./report.sql

# List recently-run queries — deduped by SQL text, newest first.
definite run query history

query history is backed by GET /api/v1/queries/recent. Each row shows the run timestamp, status, author, row count, duration, and the SQL. Respects --format (table | json | csv).

definite run integration

List, inspect, create, update, and test saved integrations.

definite run integration list
definite run integration get supabase-prod --format json
definite run integration test supabase-prod --format json

cat > postgres-integration.json <<'JSON'
{
  "public": {
    "host": "db.example.com",
    "port": "5432",
    "database": "postgres",
    "user": "postgres",
    "sslmode": "require"
  },
  "secrets": {
    "password": "..."
  }
}
JSON

definite run integration create \
  --name supabase-prod \
  --type postgres \
  --config postgres-integration.json

definite run integration update supabase-prod --config postgres-integration.json

--config - reads JSON from stdin. The config file may contain public, secrets, or both; each must be a JSON object. Secret values are sent in the request body and stored encrypted by the API. Do not pass passwords or API tokens as shell arguments.

definite connect

Open a local DuckDB session against the deployment.

# Standard path. In postgres_catalog mode this starts a credential-free
# localhost SQL relay and exposes R.query('<sql>') in local DuckDB.
definite connect
duckdb -init ~/.definite/connect-init.sql

# Admin-only postgres_catalog escape hatch. Writes direct DuckLake ATTACH
# credentials to a 0600 init file and exits; no relay stays running.
definite connect --direct-attach
duckdb -init ~/.definite/ducklake-direct-init.sql

In postgres_catalog mode the standard relay keeps the catalog DSN and object-store credentials server-side. It accepts read-only SQL through R.query(...) and uses the same table-access checks and query limits as the Query page:

FROM R.query('SHOW TABLES');
FROM R.query('SELECT * FROM main.orders LIMIT 10');

--direct-attach is available only to admins. It writes the deployment's DuckLake catalog DSN and object-store credentials to the local init script so DuckDB can ATTACH 'ducklake:postgres:...' directly; keep that file private.

definite run load

Upload a local file to the deployment's configured object store, and optionally register it as a lakehouse table in one shot. Bytes stream directly from the CLI to object storage via a presigned PUT URL — they do not traverse the API pod.

# Upload only — prints the object_uri the lakehouse can read.
definite run load ./sales.csv

# Upload + register as a lakehouse table (replaces if it exists).
definite run load ./sales.csv --table analytics.sales --mode replace

# Append into an existing table (created on first call if missing).
definite run load ./new-events.parquet --table raw.events --mode append

# Explicit format override (auto-detect uses the file extension).
definite run load ./events.log --file-format json --table raw.events --mode replace

Flags:

FlagMeaning
--table SCHEMA.NAMERegister the staged file as a lakehouse table. Omit for stage-only.
--mode replace|appendRequired when --table is set; no default. replace is CREATE OR REPLACE TABLE; append creates the table on first call then INSERT INTO.
--file-format csv|parquet|jsonOverride format detection. Auto-detected from .csv, .parquet, .json/.ndjson/.jsonl.

Requires: the deployment must have jobRunner.staging.uri set in its values.yaml and matching object-store HMAC credentials. Azure backends are not supported in v1; S3, GCS, and MinIO are. When staging isn't configured, the command returns a 503 with a remediation message and leaves the rest of the API unaffected.

definite transform

Author a local folder of SQL models, persist its dependency graph server-side, inspect versioned lineage metadata, and run it through the automation runner.

# Scaffold a starter project.
definite transform init transformations

# Parse the local SQL files and print the inferred graph and execution order.
definite transform plan transformations

# Persist the graph and create/update the linked automation pipeline.
definite transform apply transformations --name core-models

# List persisted transformation projects.
definite transform list

# Inspect the persisted project, graph, latest definition, and metadata.
definite transform get core-models --format json

# List immutable applied versions for the project.
definite transform versions core-models --format json

# Inspect a historical version; include the full AST only when needed.
definite transform get core-models --version 3 --format json
definite transform get core-models --version 3 --include-ast --format json

# Show source/output lineage across projects, or filter it down.
definite transform lineage --format json
definite transform lineage --project core-models --source-table orders --format json

# Queue a run through the linked automation pipeline.
definite transform run core-models --format json

Common flags:

FlagMeaning
--name NAMEProject display name. Defaults to the directory name for plan and apply.
--slug SLUGStable project slug. Defaults to a slugified project name.
--default-schema SCHEMADefault destination schema for models without a header override.
--description TEXTSet the project description on apply. Omitted, the stored description is kept; "" (or whitespace-only) clears it.
--compute-profile NAMESet the project's default compute profile. Omitted, the stored profile is kept; default resets.
--cron "M H DOM MON DOW"Set the linked pipeline's schedule. Omitted, an existing schedule is preserved.
--cron-timezone TZIANA timezone for --cron. Omitted, the pipeline's stored timezone is kept (UTC for a new pipeline).
--clear-cronRemove the linked pipeline's existing schedule.
--disabledCreate the linked automation disabled, or pause it on update. Omitted, the enabled state is preserved.
--enableRe-enable a paused pipeline on apply.
--version NWith transform get, fetch a historical applied version instead of the current graph.
--include-astWith transform get --version, include full parser AST JSON for versioned model SQL.
--project SLUG_OR_IDWith transform lineage, filter lineage to one transformation project.
--source-schema S / --source-table TWith transform lineage, filter by upstream lakehouse source.
--output-schema S / --output-table TWith transform lineage, filter by downstream transformation output.
--trigger-type TYPETrigger label recorded by transform run; defaults to manual.

Use --format json for automation, Fi handoff, or audit workflows. Table output is intentionally compact; JSON preserves ids and metadata such as linked automation ids, version ids, run ids, source-lineage edges, source refs, parser status, AST summaries, and exact run SQL snapshots when those fields are present in the deployed API.

transform versions, transform lineage, transform get --version, and --include-ast require a deployment API that exposes the matching version and lineage endpoints. Older API builds can still use the core plan/apply/list/get/run workflow, but may not return version history, source lineage, or full AST payloads.

Transformation versioning has three distinct SQL concepts:

ConceptMeaning
Editable sourceSQL model files in the local project folder or source editor.
Applied version SQLServer-side model definition saved by transform apply.
Run SQL snapshotExact compiled SQL inside the automation run's snapshotted definition. Use this for debugging past runs.

The /transformations UI uses the same concepts: it filters projects by status, source table, and output table; shows project models, dependency edges, selected model SQL, source/output refs, versions, and linked automation metadata; and can queue a run or open the linked automation/run. Exact run SQL snapshots and full AST payloads are opt-in detail/debug payloads, not default list data. Full AST is available through --include-ast and the equivalent UI parser-debug expansion when that payload is exposed by the deployed API.

See SQL transformations for the model-file format, lineage behavior, AST behavior, and current limits.

definite run maintenance

Inspect and run DuckLake table maintenance operations against a deployed API. All three subcommands respect --format (table | json | csv).

# Show file/snapshot statistics for every lakehouse table.
definite run maintenance stats

# Dry-run a destructive op to see what it would remove.
definite run maintenance preview --operation expire_snapshots --older-than-days 30

# Compact a single table (waits for the run to finish).
definite run maintenance run --operation compact --schema analytics --table sales

# Rewrite files whose delete ratio exceeds a threshold.
definite run maintenance run --operation rewrite --schema analytics --table sales --delete-threshold 0.25

# Flush inlined event rows to Parquet files.
definite run maintenance run --operation flush_inlined_data --schema raw --table events

# Destructive ops require --yes (or --dry-run to plan safely).
definite run maintenance run --operation cleanup_old_files --older-than-days 30 --yes
definite run maintenance run --operation expire_snapshots --older-than-days 30 --dry-run

Operations: compact, rewrite, flush_inlined_data, checkpoint, expire_snapshots, cleanup_old_files, delete_orphaned_files, full.

Flags (run):

FlagMeaning
--operation OPMaintenance operation to run. Required.
--schema S / --table TOptional together for compact and rewrite (omit both for whole-lake); optional for flush_inlined_data (--table requires --schema); rejected for other ops.
--delete-threshold FDelete-ratio threshold; only valid with rewrite.
--older-than-days NAge threshold. Required for expire_snapshots, cleanup_old_files, delete_orphaned_files; rejected for other ops.
--dry-runPlan the operation without applying changes.
--yesConfirm a destructive operation. Required for the three destructive ops unless --dry-run is set.

preview accepts only --operation (one of the three destructive ops) and --older-than-days. The run subcommand polls the run to a terminal state (succeeded / failed / cancelled), streaming step output and logs; it exits non-zero if the run fails. Use --timeout-seconds to bound the overall poll budget.

definite run event-source

Manage browser-safe public write keys for the event ingest collector. Every subcommand requires an admin token and respects --format (table | json | csv).

# Create a source and print the full evpub_... key once.
definite run event-source create \
  --name "Marketing site" \
  --schema raw \
  --table events \
  --origin https://www.example.com

# Add several allowed browser origins.
definite run event-source create \
  --name "Product app" \
  --schema raw \
  --table product_events \
  --origin https://app.example.com \
  --origin https://admin.example.com \
  --max-batch-rows 250 \
  --max-payload-bytes 524288

# Create a server-kind source for SaaS webhook senders (e.g. Read.ai) that
# can only set a URL. No --origin needed; the write key rides the URL.
definite run event-source create \
  --name "Read.ai webhooks" \
  --schema readai \
  --table raw_meetings \
  --kind server \
  --default-event-name meeting_end \
  --max-payload-bytes 8388608

# Inspect, rotate, or revoke sources.
definite run event-source list
definite run event-source rotate <source_id>
definite run event-source revoke <source_id>

Public event keys are append-only and scoped to the destination table configured on the source. Browser-kind keys are safe to embed in browser code for the listed origins; server-kind keys skip the Origin allowlist and are passed as a ?write_key= query parameter by webhook senders. Normal Definite API tokens are neither. See Event Ingest for client-side and webhook examples.

definite run permission

Manage workspace access from the CLI. Covers all three layers of the permission model (see Permissions for the concepts): application roles, content sharing, and data-access roles. Every subcommand requires an admin token and respects --format (table | json | csv).

# --- application roles ---
definite run permission users                              # list users + role
definite run permission set-role <user_id> --role editor   # viewer|editor|admin

# --- content sharing (kind = app|thread|project|query|automation|integration|agent) ---
definite run permission grants app <app_id>                          # who can see it
definite run permission share app <app_id> --grantee <user_id> --access edit
definite run permission share app <app_id> --grantee everyone --access view
definite run permission unshare app <app_id> --grantee <user_id>

# --- data-access roles ---
definite run permission data-access roles                            # list roles
definite run permission data-access create-role --name analyst --description "Sales read" --default
definite run permission data-access update-role <role_id> --name analysts --default false
definite run permission data-access delete-role <role_id>
definite run permission data-access set-grants <role_id> --grant sales.orders --grant marketing.*
definite run permission data-access role-users <role_id>             # who holds the role
definite run permission data-access assign <role_id> --user <user_id>
definite run permission data-access unassign <role_id> --user <user_id>

Notes:

  • share takes --grantee <user_id> or the literal everyone (workspace-wide view). --access is view (default) or edit; integrations accept view only — credential changes stay admin-only.
  • unshare's --grantee is a user ID or everyone.
  • set-grants replaces a role's whole grant set — always pass the full intended list. A grant is schema.table; * is the only wildcard (sales.* = every table in sales, *.* = every table).
  • update-role patches only the flags you pass (--name, --description, --default <bool>); omitted fields are left unchanged.
  • set-role refuses to demote the last remaining admin.

These are the same operations available in the web UI under Settings → Members & access and in the Fi permissions skill.

definite run app

Manage data apps — single-file React + DuckDB-WASM dashboards served by the on-prem API at /apps/<slug>. The CLI handles scaffolding from a built-in template, uploading the built bundle, and basic lifecycle ops.

# Scaffold a new app from a built-in template into ./<slug>/.
definite run app scaffold revenue-explorer                           # default: refined
definite run app scaffold pipeline --template tufte-pipeline         # Tufte-styled report

# Iterate, then build + upload.
cd revenue-explorer
npm run build                                          # → dist/index.html
definite run app upload revenue-explorer .             # snapshots src/ for the Source tab
definite run app upload revenue-explorer . --force     # intentional replace; saves prior version

# Lifecycle.
definite run app list                                  # slug, name, updated_at
definite run app get revenue-explorer                  # metadata
definite run app get revenue-explorer --source         # download editable source
definite run app delete revenue-explorer

Uploads are create-only by default and reject an existing slug. Use --force only when you intentionally want to replace an app; the previous HTML/source is kept in the app's version history for restore.

Built-in templates (selected via --template):

  • refined (default) — SaaS-style dashboard: KPI tiles, charts, a sortable DataTable, dark theme, runtime's SaasKpiCard + EChart + DataTable. Use for plain "show me X" dashboards.
  • tufte-pipeline — dense, document-style analytical report: sparklines, small multiples, range-frame bars, drillable inline numerics, EB Garamond + IBM Plex typography. Use when the user wants an executive report or anything that should look more like a report than a tile grid. See frontend/templates/tufte-pipeline/README.md for the design vocab + the resource shapes its starter app expects.

Both templates compile to a single self-contained dist/index.html via npm run build and share the same node_modules layout (pre-baked at /opt/data-apps-template/node_modules in the fi-sandbox image so there's no npm install round trip in the agent).

definite export-helm

Print the rendered Helm values or full chart to stdout. Escape hatch for SREs who want to take over with raw helm.

definite export-helm --config config.yaml --format yaml    # values only
definite export-helm --config config.yaml --format chart   # `helm template` output

definite version

Print the CLI and bundled chart version.

definite bootstrap

Install the cluster-level prerequisites that definite init assumes are already present. Run this once against a fresh cluster, before definite doctor / definite init.

definite bootstrap                            # install missing prerequisites
definite bootstrap --dry-run                  # print what would be installed, change nothing
definite bootstrap --acme-email ops@acme.com  # ClusterIssuer ACME contact email

bootstrap installs, in order:

PrerequisiteWhat it provides
Ingress controllerHTTP/S routing for the deployment's Ingress resource.
cert-manager (+ CRDs)TLS certificate issuance for tls: cert_manager deployments.
letsencrypt-prod ClusterIssuerThe issuer the ingress references for automatic Let's Encrypt certs.
agent-sandbox CRDsCustom resources the Fi runtime uses to dispatch per-run sandboxes.

cert-manager is installed with its leader-election lease pinned to the cert-manager namespace (--set global.leaderElection.namespace=cert-manager). The chart defaults this lease to kube-system, which is Google-managed on GKE Autopilot and rejects writes — without the override cainjector never acquires the lease and the webhook CA is never injected. The override is harmless on non-Autopilot clusters, so it is always applied.

After cert-manager is healthy, bootstrap creates the letsencrypt-prod ClusterIssuer (production Let's Encrypt ACME, http01 solver). The Helm chart's Ingress is annotated cert-manager.io/cluster-issuer: letsencrypt-prod, so without this issuer TLS certs never provision. The ACME contact email defaults to hello@definite.app; override it with --acme-email. Use --ingress-class (default nginx) if your ingress controller uses a different class. --skip-cert-manager skips both cert-manager and the ClusterIssuer (use it for tls: disabled / manual).

Production Let's Encrypt is for real DNS names that you control. Avoid convenience wildcard domains such as *.nip.io with tls: cert_manager: Let's Encrypt rate-limits nip.io globally at the registered-domain level, and cert-manager will loop on 429 errors instead of issuing a trusted cert. definite init rejects that combination; use real DNS, tls: manual, or tls: disabled for local/no-trust testing.

The command is idempotent: helm upgrade --install and kubectl apply both converge on re-run. --dry-run reports the planned actions — every helm and kubectl command, plus the full ClusterIssuer manifest — without touching the cluster. Anything bootstrap installs can also be installed by hand with helm / kubectl; it is a convenience, not a hard dependency. definite doctor re-checks for these prerequisites and points back here when one is missing.

definite login

Authenticate the CLI against a running deployment so subsequent commands (definite run …) can act on your behalf without re-entering credentials.

definite login deployment.acme.internal           # prompts for password
definite login deployment.acme.internal --password "$PW"
echo "$PW" | definite login deployment.acme.internal --password-stdin

login authenticates against the deployment's local Postgres auth path. It prompts interactively for a password by default; --password takes it as a flag (visible in shell history — prefer --password-stdin for scripts and CI).

On success the issued token is written to ~/.definite/credentials.json with file mode 0600 (owner read/write only). The file stores the current api_url, token, email, and expires_at; running definite login against another deployment overwrites the saved credentials.

definite run resolves credentials in this order: an explicit --token flag, then the DEFINITE_TOKEN environment variable, then the stored credentials for the target hostname. If none resolve, the command fails with a message telling you to run definite login.

OIDC/SSO deployments authenticate through the browser-based web login rather than definite login; CLI login covers the local Postgres auth path.

definite license

Inspect a deployment's live license entitlement.

definite license status                            # plan + activation status
definite license status --config config.yaml
definite license status --api-url http://localhost:8000 --token "$TOKEN"

license status calls GET /api/v1/license on the deployment and prints the plan, activation status, expiry, and enabled features. This is the live entitlement the API obtained by activating against the central Definite API. It requires a bearer token (--token or DEFINITE_TOKEN).

A deployment is licensed by one mechanism: a license key in the definite-secrets Kubernetes Secret under license-key. In the default brokered install flow, definite init obtains that key from the broker. For customer-owned/manual flows, add a license block to config.yaml; the CLI renders it into the Secret, and the API reads it as LICENSE_KEY and activates against the central Definite API. Advanced installs may leave the license block unset and have an external secret store sync license-key into the same Secret; the API env is still wired, but the pod must restart after secret rotation. There is no separate "apply a key to the CLI" step.

To license — or re-license — a running deployment, add a license block to config.yaml (see config.md) and run definite upgrade.

Environment variables

VariablePurpose
DEFINITE_LOGtracing-subscriber filter, e.g. debug or definite=debug,reqwest=info. Default: info.
DEFINITE_CHART_PATHOverride the bundled Helm chart location. Useful for development.
DEFINITE_ONPREM_SETUP_TOKENBroker setup token for the default DNS/license flow. Secret; never printed.
DEFINITE_SETUP_TOKENLegacy/fallback broker setup token env var.
DEFINITE_BROKER_API_URLOverride the broker API base URL.
DEFINITE_BROKER_REQUESTED_SLUGOptional preferred brokered DNS slug.
DEFINITE_ONPREM_GCP_ATTESTATION_SERVICE_ACCOUNTOptional GCP service account to impersonate when minting broker setup attestation identity tokens.
DEFINITE_TOKENAuth token for definite run. Checked after --token and before stored credentials from definite login.
${ANY_VAR} (in config.yaml)Substituted at config load time. ${VAR} must be set or load fails loud.

Exit codes

CodeMeaning
0Success
1Generic failure (config error, preflight failed, helm error)