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:
| Category | What it does |
|---|---|
postgres | Connects with tokio-postgres, runs SELECT version(), warns on <15 |
kubernetes | Runs kubectl cluster-info, reports the control plane endpoint |
object_store | Validates config shape (a real PutObject round-trip is not yet performed) |
llm | For Anthropic: real API ping. For others: config validation only |
agent_sandbox | Checks the agent-sandbox CRDs are installed |
license | Warns 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_dumpperbackup-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 withoutORDER BY— including a table created withCREATE TABLE ... AS SELECT ... ORDER BYand then read back withoutORDER BY— setlakehouse.duckdb.preserveInsertionOrder: true, or addORDER BYat read time. Seetransformations.mdfor 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:
| Flag | Meaning |
|---|---|
--table SCHEMA.NAME | Register the staged file as a lakehouse table. Omit for stage-only. |
--mode replace|append | Required 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|json | Override 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:
| Flag | Meaning |
|---|---|
--name NAME | Project display name. Defaults to the directory name for plan and apply. |
--slug SLUG | Stable project slug. Defaults to a slugified project name. |
--default-schema SCHEMA | Default destination schema for models without a header override. |
--description TEXT | Set the project description on apply. Omitted, the stored description is kept; "" (or whitespace-only) clears it. |
--compute-profile NAME | Set 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 TZ | IANA timezone for --cron. Omitted, the pipeline's stored timezone is kept (UTC for a new pipeline). |
--clear-cron | Remove the linked pipeline's existing schedule. |
--disabled | Create the linked automation disabled, or pause it on update. Omitted, the enabled state is preserved. |
--enable | Re-enable a paused pipeline on apply. |
--version N | With transform get, fetch a historical applied version instead of the current graph. |
--include-ast | With transform get --version, include full parser AST JSON for versioned model SQL. |
--project SLUG_OR_ID | With transform lineage, filter lineage to one transformation project. |
--source-schema S / --source-table T | With transform lineage, filter by upstream lakehouse source. |
--output-schema S / --output-table T | With transform lineage, filter by downstream transformation output. |
--trigger-type TYPE | Trigger 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:
| Concept | Meaning |
|---|---|
| Editable source | SQL model files in the local project folder or source editor. |
| Applied version SQL | Server-side model definition saved by transform apply. |
| Run SQL snapshot | Exact 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):
| Flag | Meaning |
|---|---|
--operation OP | Maintenance operation to run. Required. |
--schema S / --table T | Optional together for compact and rewrite (omit both for whole-lake); optional for flush_inlined_data (--table requires --schema); rejected for other ops. |
--delete-threshold F | Delete-ratio threshold; only valid with rewrite. |
--older-than-days N | Age threshold. Required for expire_snapshots, cleanup_old_files, delete_orphaned_files; rejected for other ops. |
--dry-run | Plan the operation without applying changes. |
--yes | Confirm 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:
sharetakes--grantee <user_id>or the literaleveryone(workspace-wide view).--accessisview(default) oredit; integrations acceptviewonly — credential changes stay admin-only.unshare's--granteeis a user ID oreveryone.set-grantsreplaces a role's whole grant set — always pass the full intended list. A grant isschema.table;*is the only wildcard (sales.*= every table insales,*.*= every table).update-rolepatches only the flags you pass (--name,--description,--default <bool>); omitted fields are left unchanged.set-rolerefuses 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 sortableDataTable, dark theme, runtime'sSaasKpiCard+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. Seefrontend/templates/tufte-pipeline/README.mdfor 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:
| Prerequisite | What it provides |
|---|---|
| Ingress controller | HTTP/S routing for the deployment's Ingress resource. |
| cert-manager (+ CRDs) | TLS certificate issuance for tls: cert_manager deployments. |
letsencrypt-prod ClusterIssuer | The issuer the ingress references for automatic Let's Encrypt certs. |
| agent-sandbox CRDs | Custom 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
| Variable | Purpose |
|---|---|
DEFINITE_LOG | tracing-subscriber filter, e.g. debug or definite=debug,reqwest=info. Default: info. |
DEFINITE_CHART_PATH | Override the bundled Helm chart location. Useful for development. |
DEFINITE_ONPREM_SETUP_TOKEN | Broker setup token for the default DNS/license flow. Secret; never printed. |
DEFINITE_SETUP_TOKEN | Legacy/fallback broker setup token env var. |
DEFINITE_BROKER_API_URL | Override the broker API base URL. |
DEFINITE_BROKER_REQUESTED_SLUG | Optional preferred brokered DNS slug. |
DEFINITE_ONPREM_GCP_ATTESTATION_SERVICE_ACCOUNT | Optional GCP service account to impersonate when minting broker setup attestation identity tokens. |
DEFINITE_TOKEN | Auth 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
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic failure (config error, preflight failed, helm error) |