Skip to content

Development Commands

The development commands support local workflows: creating sample projects, importing existing dbt projects, running the semantic graph HTTP server, IDE integration via LSP, and scaffolding new warehouse adapter crates.


Create a self-contained sample project using DuckDB as the local execution engine. No warehouse credentials or external services are required. Useful for learning Rocky, testing model logic, and rapid prototyping.

Terminal window
rocky playground [path]
ArgumentTypeDefaultDescription
pathstringrocky-playgroundDirectory name for the playground project.

Create a playground with the default name:

Terminal window
rocky playground
Created rocky-playground/rocky.toml (DuckDB config)
Created rocky-playground/models/stg_orders.sql
Created rocky-playground/models/stg_orders.toml
Created rocky-playground/models/stg_customers.sql
Created rocky-playground/models/stg_customers.toml
Created rocky-playground/models/fct_revenue.sql
Created rocky-playground/models/fct_revenue.toml
Created rocky-playground/seeds/orders.csv
Created rocky-playground/seeds/customers.csv
Playground ready! Run:
cd rocky-playground
rocky compile
rocky test

Create a playground with a custom name:

Terminal window
rocky playground my-experiment
Created my-experiment/rocky.toml (DuckDB config)
Created my-experiment/models/...
Playground ready! Run:
cd my-experiment
rocky compile
rocky test

Import an existing dbt project as a runnable Rocky repo. Parses dbt_project.yml + profiles.yml, translates each .sql model body (expanding {{ ref(...) }} / {{ source(...) }} to plain identifiers; leaving other Jinja with a # TODO: dbt-jinja-not-translated comment), and writes a self-contained Rocky directory layout that rocky compile and rocky plan + rocky apply can use directly.

Terminal window
rocky import-dbt --dbt-project <PATH> [flags]
FlagTypeDefaultDescription
--dbt-project <PATH>PathBuf(required)Path to the dbt project directory (containing dbt_project.yml).
--output-dir <PATH>PathBufrocky-outOutput directory for the emitted Rocky repo (rocky.toml + models/ + seeds/ + MIGRATION-NOTES.md). Refuses to write into a non-empty directory unless --overwrite is set.
--target-adapter <KIND>stringprofile-derivedOverride the Rocky adapter type. Accepts duckdb, databricks, snowflake, bigquery. Defaults to whatever <dbt_project>/profiles.yml declares — or duckdb when the profile can’t be parsed or maps to an unsupported warehouse.
--overwriteboolfalseReplace contents of --output-dir if it already exists and is non-empty. Without this flag, the importer refuses to write into a non-empty directory so it never silently clobbers existing work.
--manifest <PATH>PathBufauto-detectPath to target/manifest.json. When present, the importer uses dbt’s compiled manifest (Jinja already resolved). Auto-detected from <dbt_project>/target/ if omitted.
--no-manifestboolfalseForce regex-based import (skip manifest.json even if available). Useful when the manifest is stale or you want to verify the regex path.
<output-dir>/
├── rocky.toml derived from dbt_project.yml + profiles.yml
├── models/
│ ├── _defaults.toml catalog + schema defaults from dbt_project.yml
│ ├── <name>.sql translated dbt model body (Jinja stripped or commented)
│ └── <name>.toml [strategy] + [target] sidecar
├── seeds/ verbatim copy of <dbt_project>/seeds/
└── MIGRATION-NOTES.md summary: counts, known limitations, required env vars

Connection secrets (passwords, API tokens, service-account JSON) are emitted as ${VAR} env-var placeholders in rocky.toml; they are never inlined. The required env vars are listed in MIGRATION-NOTES.md so users know what to export before rocky plan + rocky apply.

materialized mapping: view → ephemeral, table → full_refresh, incremental (with unique_key) → merge, incremental (without) → incremental. Anything else falls back to full_refresh with a TODO line in MIGRATION-NOTES.md. Profile types Rocky doesn’t natively support stub a DuckDB [adapter] so the project still compiles, with the original type preserved under “Not Translated”.

Listed in every emitted MIGRATION-NOTES.md:

  • Singular dbt tests (custom SQL files in tests/) — not translated.
  • Macros and dbt_packages/ — skipped. The hybrid-dbt-packages POC is the documented escape hatch.

The four built-in dbt generic tests (unique, not_null, accepted_values, relationships) translate to native Rocky [[checks]] on the matching per-model sidecar (shipped in engine v1.27.0). Tests referencing columns Rocky didn’t translate are listed in MIGRATION-NOTES.md under “Not Translated” rather than silently dropped.

Import a dbt project end-to-end. The default output dir is ./rocky-out/:

Terminal window
rocky import-dbt --dbt-project ~/projects/acme-dbt
{
"version": "1.30.0",
"command": "import-dbt",
"import_method": "regex",
"project_name": "demo_project",
"imported": 1,
"failed": 0,
"warnings": 0,
"imported_models": ["orders"],
"warning_details": [],
"failed_details": [],
"emission": {
"out_dir": "rocky-out",
"rocky_toml_path": "rocky-out/rocky.toml",
"migration_notes_path": "rocky-out/MIGRATION-NOTES.md",
"models_translated_count": 1,
"models_skipped_count": 0,
"seeds_copied_count": 0,
"adapter_type": "duckdb",
"original_dbt_adapter_type": "duckdb",
"required_env_vars": []
}
}

import_method is "manifest" when a compiled target/manifest.json was found (pre-resolved Jinja), or "regex" when Rocky parsed the raw .sql files directly. The emission block is populated whenever --output-dir writes succeeded.

Import into a custom directory and override the target adapter (e.g. migrating a Postgres dbt project to Rocky-on-Snowflake):

Terminal window
rocky import-dbt --dbt-project ~/projects/acme-dbt --output-dir ./acme-rocky --target-adapter snowflake

Re-run after fixing the dbt source, replacing the previous emit:

Terminal window
rocky import-dbt --dbt-project ~/projects/acme-dbt --output-dir ./acme-rocky --overwrite

End-to-end migration in one shot — emit, then compile to verify the result is loadable:

Terminal window
rocky import-dbt --dbt-project ~/projects/acme-dbt --output-dir ./acme-rocky --overwrite \
&& rocky compile --models ./acme-rocky/models/

Start an HTTP API server that exposes the compiler’s semantic graph. Provides REST endpoints for model metadata, lineage, and compilation results. Useful for editor integrations, dashboards, and custom tooling.

Terminal window
rocky serve [flags]

rocky serve binds 127.0.0.1 (loopback) by default. Every endpoint except /api/v1/health requires a Bearer token. Two operating modes:

  • Loopback only (default) — bind stays on 127.0.0.1. Authentication is still on, so external processes (LSP, dashboards) need the token, but a misconfigured network won’t expose model SQL to the LAN.
  • Non-loopback bind--host 0.0.0.0 (or any non-loopback address) requires --token <secret> (or the ROCKY_SERVE_TOKEN env var). rocky serve refuses to start otherwise. This prevents the “I just wanted to try it from another machine” foot-gun shipping model SQL to the LAN unauthenticated.

CORS is empty-by-default. Browser apps must declare every allowed origin via --allowed-origin <ORIGIN>. Permitted methods: GET, POST, OPTIONS. Permitted headers: Authorization, Content-Type.

FlagTypeDefaultDescription
--models <PATH>PathBufmodelsDirectory containing model files.
--contracts <PATH>PathBufDirectory containing data contract definitions.
--host <HOST>String127.0.0.1Bind host. Non-loopback (0.0.0.0, etc.) requires --token.
--port <PORT>u168080Port to listen on.
--token <SECRET>StringBearer token required by every API request except /api/v1/health. Falls back to ROCKY_SERVE_TOKEN env var when omitted. Required when --host is non-loopback.
--allowed-origin <ORIGIN>String (repeatable)[]Add an origin to the CORS allowlist. Repeat for multiple origins (e.g. --allowed-origin http://localhost:5173 --allowed-origin https://dashboard.example.com).
--watchboolfalseWatch for file changes and auto-recompile.

Start the server with defaults (loopback, token via env var):

Terminal window
export ROCKY_SERVE_TOKEN=$(openssl rand -hex 32)
rocky serve
Compiled 14 models in 42ms
Listening on http://127.0.0.1:8080
Endpoints:
GET /api/models - List all compiled models
GET /api/models/:name - Get model details
GET /api/lineage/:name - Column-level lineage
GET /api/dag - Full dependency graph

Call an authenticated endpoint:

Terminal window
curl -H "Authorization: Bearer $ROCKY_SERVE_TOKEN" http://127.0.0.1:8080/api/models

/api/v1/health is exempt and reachable without the token.

Start with file watching on a custom port:

Terminal window
rocky serve --port 3000 --watch --token "$ROCKY_SERVE_TOKEN"
Compiled 14 models in 42ms
Watching models/ for changes...
Listening on http://127.0.0.1:3000

Expose to the LAN with explicit token + origin allowlist:

Terminal window
rocky serve \
--host 0.0.0.0 \
--token "$ROCKY_SERVE_TOKEN" \
--allowed-origin https://dashboard.internal \
--port 9090

Start with contracts:

Terminal window
rocky serve --models src/models --contracts src/contracts --port 9090 --watch
  • rocky lsp — IDE integration via Language Server Protocol
  • rocky compile — one-shot compilation without the server
  • rocky lineage — CLI lineage (the server exposes the same data via HTTP)

Start a Language Server Protocol server for IDE integration. Provides diagnostics, completions, hover information, and go-to-definition for Rocky SQL models.

Terminal window
rocky lsp

No command-specific flags. The LSP server communicates over stdin/stdout per the LSP specification.

Start the LSP server (typically called by an editor, not directly):

Terminal window
rocky lsp

Configure in VS Code (settings.json):

{
"rocky.lsp.path": "rocky",
"rocky.lsp.args": ["lsp"]
}

Configure in Neovim (with lspconfig):

require('lspconfig').rocky.setup({
cmd = { "rocky", "lsp" },
filetypes = { "sql" },
root_dir = function(fname)
return require('lspconfig.util').root_pattern('rocky.toml')(fname)
end,
})
  • rocky serve — HTTP API server (alternative integration method)
  • rocky compile — the LSP uses the same compilation engine

Scaffold a new warehouse adapter crate. Creates the directory structure, Cargo.toml, and trait implementation stubs for building a custom adapter (e.g., BigQuery, Redshift, Snowflake).

Terminal window
rocky init-adapter <name>
ArgumentTypeDefaultDescription
namestring(required)Adapter name (e.g., bigquery, redshift, snowflake).

Scaffold a BigQuery adapter:

Terminal window
rocky init-adapter bigquery
Created crates/rocky-bigquery/Cargo.toml
Created crates/rocky-bigquery/src/lib.rs
Created crates/rocky-bigquery/src/connector.rs
Created crates/rocky-bigquery/src/auth.rs
Adapter scaffold ready at crates/rocky-bigquery/
Implement the WarehouseAdapter trait in src/connector.rs to get started.

Scaffold a Snowflake adapter:

Terminal window
rocky init-adapter snowflake
Created crates/rocky-snowflake/Cargo.toml
Created crates/rocky-snowflake/src/lib.rs
Created crates/rocky-snowflake/src/connector.rs
Created crates/rocky-snowflake/src/auth.rs
Adapter scaffold ready at crates/rocky-snowflake/
Implement the WarehouseAdapter trait in src/connector.rs to get started.

Manage lifecycle hooks configured in rocky.toml.

List all configured hooks.

Terminal window
rocky hooks list

Fire a synthetic test event to validate hook scripts.

Terminal window
rocky hooks test <EVENT>
ArgumentTypeDescription
EVENTstringEvent name (e.g., pipeline_start, materialize_error)
Terminal window
$ rocky hooks test pipeline_start
Firing test event: pipeline_start
Hook 'bash scripts/notify.sh': OK (exit 0, 120ms)

Compare a dbt project against its Rocky import to verify correctness.

Terminal window
rocky validate-migration [flags]
FlagTypeDefaultDescription
--dbt-projectpathrequiredPath to the dbt project
--rocky-projectpathPath to the Rocky project (defaults to current directory)
--sample-sizenumberNumber of sample rows for data comparison
Terminal window
$ rocky validate-migration --dbt-project ~/dbt-project
Validating 12 models...
stg_customers: PASS (schema match, row count match)
fct_orders: PASS (schema match, row count match)
dim_products: WARN (column order differs)

Discover and inspect the adapters available on your system. Rocky follows the cargo-subcommand convention: any executable on your PATH whose name starts with rocky- is treated as a process adapter named after the suffix, so a rocky-snowplow binary registers as the adapter snowplow.

Walks PATH, runs each rocky-* candidate, and prints one row per discovered adapter.

Terminal window
rocky adapter list [--output json]

The table shows NAME, VERSION, DIALECT, and PATH. An adapter that fails to initialize still appears in the listing with its error, so a broken install is visible rather than silently skipped. The bundled rocky-lsp language server is filtered out. Passing --output json returns each adapter’s full manifest.

Resolves rocky-<name> on PATH, runs it, and prints its manifest (name, version, SQL dialect, and the path it resolved to).

Terminal window
rocky adapter info snowplow

Run conformance tests against a warehouse adapter.

Terminal window
rocky test-adapter [flags]
FlagTypeDefaultDescription
--adapterstringAdapter name. Resolves a built-in adapter (databricks, snowflake, duckdb) first, then falls back to an installed rocky-<name> executable on your PATH.
--commandstringPath to a process adapter binary
--adapter-configstringJSON config to pass to the adapter
Terminal window
$ rocky test-adapter --adapter duckdb
Running conformance tests for duckdb...
19/19 core tests passed
3/7 optional tests passed (4 skipped: not supported)

Health checks for your Rocky project: config validation, local state store integrity, adapter connectivity, pipeline consistency, state backend configuration, live state read/write, and auth verification.

Terminal window
rocky doctor # Run all checks
rocky doctor --check config # Run only the config check
rocky doctor --check auth # Verify credentials + connectivity for all adapters
rocky doctor --check state_rw # Round-trip a marker object against the state backend
rocky doctor --verbose # Add per-check context to human-readable output

The auth check pings each registered warehouse adapter (via SELECT 1 or an adapter-specific cheaper query) and each discovery adapter. Reports per-adapter pass/fail with latency.

The state_rw check (v1.13.0+) runs a put → get → delete probe against the configured state backend so IAM and reachability problems surface at cold start rather than at end-of-run upload. Local backend is a no-op; tiered probes both legs.

The --verbose flag (v1.20.0+) prints extra per-check context inline: config path, state file size, adapter type and credential signal (token, oauth_client, oauth_token, key_pair, password, service_account, adc, env, none), pipeline kind (replication / transformation / quality / snapshot), and state backend. JSON output is unchanged when --verbose is not passed — the new details array on each HealthCheck only serializes when populated, so existing consumers see byte-stable envelopes.

See the CLI Reference for the full check list and JSON output format.


Inspect project contents without running a pipeline.

Terminal window
rocky list pipelines # Pipeline definitions (type, adapters, depends_on)
rocky list adapters # Adapter configurations (type, host)
rocky list models # Transformation models (target, strategy, contract, deps)
rocky list sources # Replication source configurations
rocky list deps <model> # What this model depends on
rocky list consumers <model> # What depends on this model

All subcommands support --output json via -o json. Models are discovered from the models/ directory (and immediate subdirectories for the common models/{layer}/ layout).

See the CLI Reference for full examples and JSON output schemas.