Skip to content

Command-Line Client

Goal

Use ravo for inspection, catalog automation, recipe validation, preview diagnostics, recovery/backup, and local export with machine-readable results.

Last reviewed: 2026-09-01 against the current ravo-cli/v1 implementation and committed CLI contract tests.

Applies to

  • The ravo executable built from this repository.
  • Scripts and headless workflows that need the same engine and services as Ravo Studio.

Prerequisites

  • A prepared Ravo build and a local path to the ravo executable.
  • A writable catalog/output location for commands that create or export files.

Locate and run the CLI

For the macOS Debug build:

./build/mac_clang_debug/Ravo/cli/ravo --version --json

The corresponding executable is under build/<preset>/Ravo/cli/ on Windows and Linux. The CLI is local and does not start the old application.

JSON protocol

Add --json to any supported command. A successful response has this shape:

{
  "data": {},
  "diagnostics": [],
  "ok": true,
  "type": "ravo.cli.result",
  "version": 1
}

A failure keeps the same outer protocol and includes a structured error:

{
  "diagnostics": [],
  "error": {
    "code": "conflict",
    "context": {},
    "message": "..."
  },
  "ok": false,
  "type": "ravo.cli.result",
  "version": 1
}

Without --json, success is written as JSON data and failures are written to stderr in human-readable form. Service logging stays in the per-user log file so it cannot corrupt a machine-readable stdout stream.

Start-to-finish catalog example

Replace the placeholders with paths from your workspace:

RAVO=./build/mac_clang_debug/Ravo/cli/ravo
CATALOG="/work/Ravo Library.sqlite"

"$RAVO" catalog create --path "$CATALOG" --json
"$RAVO" catalog import --catalog "$CATALOG" --input "/photos/2026" --json
"$RAVO" catalog list --catalog "$CATALOG" --json
"$RAVO" catalog preview --catalog "$CATALOG" --asset-id <asset-id> --json
"$RAVO" catalog preview --catalog "$CATALOG" --asset-id <asset-id> \
  --roi 0.25,0.25,0.2,0.15 --output /tmp/roi.png --json
# ROI JSON includes gpu_backend (`metal` when GPU RCD ran).
"$RAVO" catalog export --catalog "$CATALOG" --asset-id <asset-id> \
  --output "/exports/photo.png" --format png --json

Directory import is recursive and returns item-level statuses. Keep the asset ID returned by catalog list or an imported item for later commands.

If Ravo Studio already has that library open, committed CLI writes appear in the window within about one second. For selection-relative work, the same CLI can use Studio's owner-only local ravo-studio-control/v1 endpoint; it is not a network listener and does not expose Assistant credentials.

Top-level commands

Command Purpose
--version Return the Ravo version and ravo-cli/v1 protocol name.
operations List registered versioned engine operations and their descriptors.
develop-fields List every closed Develop --set field name, kind, and range.
inspect <input> Inspect a supported RAW input's format, dimensions, and camera identity.
lut inspect <file.cube> Validate a bounded 3D LUT and report its canonical path, size, domain, title, and content fingerprint.
noise calibrate ... / noise inspect ... Fit or validate a deterministic camera-noise profile artifact without changing a photo or catalog.
recipe validate <recipe> Parse and validate a recipe without rendering it.
recipe import-xmp <xmp> ... Convert a strictly supported leftover darktable XMP subset, or a Lightroom CRS preset, into a versioned recipe file.
recipe style-create ..., style-validate ..., or style-apply ... Create, validate, or apply complete and selective .rstyle.json artifacts.
render <input> ... Render a validated recipe to an atomic PNG output using CPU.
catalog ... Create, query, edit, preview, history-manage, synchronize recovery, back up, verify, and export a Ravo catalog.
studio ... Discover a live Studio session, inspect its selected recipe, commit strict Develop fields, and publish its latest effect.

Control the selected photo in a running Studio

List live sessions and inspect the one associated with the current checkout:

ravo studio sessions --json
ravo studio state --json

state reports the process/session and state revisions, catalog path/revision, primary and selected asset IDs, browse mode, current and saved recipes, baseline-relative modified_operations, pending changes, and the displayed preview identity. If more than one Studio session matches the checkout, pass the session_id explicitly:

ravo studio state --session-id <session-id> --json

Commit one ordered parameter batch and optionally obtain its exact rendered effect:

ravo studio develop --session-id <session-id> \
  --set exposure=0.6 --set saturation=-0.1 \
  --output "/work/studio-result.png" --json

Without explicit --expect-session-revision and --expect-selection-revision, the CLI observes a fresh state and carries those revisions in the mutation. It retries only when incidental session state moved while the asset, selection revision, and recipe remained identical. Supplying either expectation makes the binding strict and any mismatch returns conflict; a request is never redirected to a newly selected photo.

To render the current recipe without saving or changing it, including a pending in-memory slider value:

ravo studio preview --session-id <session-id> \
  --output "/work/current-effect.png" --max-edge 1600 --json

Image bytes do not cross the control socket. The CLI renders the snapshot's canonical recipe through CatalogService and the existing CPU preview path, rechecks selection and recipe revisions, then atomically publishes a new PNG. The JSON result identifies its MIME type, dimensions, color profile, byte size, SHA-256, and caller-owned lifecycle. Existing output paths return conflict.

Inspect an input

ravo inspect "/photos/source.cr2" --json

The current inspection command is the RAW inspection path. For a supported RAW file it reports format, display dimensions, is_raw, camera identity, CFA family/size, the sensor-default demosaic mode, white-balance coefficients, DNG OpcodeList2/3 support and optional-skip state, and the normalized input URI. Raster files should be checked through catalog import and preview instead. Inspection does not import, edit, or write the source.

Inspect available operations

ravo operations --json

The command is useful for tooling that wants the current versioned operation descriptors instead of hard-coding an assumed registry. Operation availability does not make an unsupported legacy mask, blend, or history state importable.

Discover Develop --set fields

ravo develop-fields --json
ravo catalog fields --json

Both commands return the same inventory. Neither needs a catalog. Each closed numeric or toggle field includes name, kind, minimum, and maximum. Text fields currently include watermarkText and lut3dFile; set advertised text fields with --set-text name=value. --watermark-text remains a convenience spelling for the watermark value. Canonical-mask names are not a closed list; the result includes prefixes for colorHarmonizerMask and graduatedMask. Unknown, duplicate, non-finite, or out-of-range --set values fail closed.

Validate or import a recipe

Validate an existing recipe:

ravo recipe validate "/work/photo.recipe.json" --json

Convert an XMP sidecar into a new recipe file:

ravo recipe import-xmp "/work/photo.xmp" \
  --asset-id asset-123 \
  --input "file:///photos/photo.cr2" \
  --output "/work/photo.recipe.json" --json

The converter accepts only strict, evidenced leftover darktable state, or a Camera Raw Settings (crs:) document. Empty leftover history and specific default-unmasked operation records have supported mappings. A CRS preset maps onto accepted Develop owners. Unknown crs: keys, Kelvin/tint white balance, custom DCP profiles, and mixed darktable+CRS documents return unsupported. Adobe Standard is not applied and is listed in omitted. Masks, custom leftover blend state, multiple or conflicting instances, unknown data, malformed payloads, and unsupported history combinations also return unsupported or validation; they are not silently approximated.

Create and list a catalog

ravo catalog create --path "/work/Ravo Library.sqlite" --json
ravo catalog list --catalog "/work/Ravo Library.sqlite" --json

create refuses to replace an existing database. list returns asset IDs, media types, normalized URIs, review state, tags, metadata, capture values, and whether an asset has edits. capture.captured_at is ISO local time with exact source subseconds and the source UTC offset only when the file supplied one; unzoned times never gain Z, and the field is null when no local capture time exists. capture.gps is null without a complete latitude/longitude pair; coordinates are scaled-decimal numbers with at most six fractional digits, and altitude_m is omitted when altitude is absent. Filter by a catalog tag with:

ravo catalog list --catalog "/work/Ravo Library.sqlite" \
  --tag landscape --json

Inspect recovery state and create a catalog backup

Every durable asset, review, metadata, recipe, or history mutation advances an asset-local recovery generation. The derived checksummed JSON is stored under <catalog>.ravo/sidecars/, never beside an original and never as a second live edit authority.

List pending generations for the catalog, or inspect one asset even when it is already synchronized:

ravo catalog sidecar-status --catalog "/work/Ravo Library.sqlite" --json
ravo catalog sidecar-status --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --json

Synchronize all pending generations, or one named asset:

ravo catalog sidecar-sync --catalog "/work/Ravo Library.sqlite" --json
ravo catalog sidecar-sync --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --json

sidecar-status without an asset returns only pending states. A sync result reports the support root, pending counts before/after, and each published artifact's asset ID, generation, path, byte count, and SHA-256. A catalog write can be committed even when recovery publication fails; structured context then reports that recovery remains pending for a later open, close, sync, or backup.

Create a backup at a directory that does not yet exist, then verify it without opening its snapshot as a live catalog:

ravo catalog backup --catalog "/work/Ravo Library.sqlite" \
  --backup "/backups/Ravo-2026-08-31" --json
ravo catalog backup-verify --backup "/backups/Ravo-2026-08-31" --json

Creation drains pending recovery, snapshots and integrity-checks the database, removes rebuildable preview rows, copies the exact recovery generations, and publishes the directory without replacement. The backup contains catalog.sqlite, manifest.json, and sidecars/; its JSON result reports hashes, byte counts, schema/revision identity, and verified: true. backup-verify accepts no --catalog argument and is read-only.

Catalog backups deliberately exclude originals and preview files. Back up the referenced originals separately. Restore to a new absent path, rebuild previews, or configure verified retention with:

ravo catalog backup-restore --backup "/backups/Ravo-2026-08-31" \
  --output "/work/Restored Ravo Library.sqlite" --json
ravo catalog preview-rebuild --catalog "/work/Restored Ravo Library.sqlite" --json
ravo catalog backup-policy --catalog "/work/Ravo Library.sqlite" \
  --schedule-dir "/backups/Ravo" --interval-minutes 1440 \
  --retention-count 7 --enabled true --json
ravo catalog backup-run --catalog "/work/Ravo Library.sqlite" --json

backup-restore never overwrites or merges a destination. Scheduled retention removes only canonical artifacts that reverify as the current catalog.

List stable direct folders and explicitly relink a missing one:

ravo catalog folders --catalog "/work/Ravo Library.sqlite" --json
ravo catalog folder-relink --catalog "/work/Ravo Library.sqlite" \
  --folder-id <folder-id> --replacement "/new/photo/folder" --json
ravo catalog folder-remove --catalog "/work/Ravo Library.sqlite" \
  --folder-uri <folder-uri> --json

Relink is identity-checked and transactional; it does not search by a similar filename or write an original.

Named collections persist in the catalog:

ravo catalog sets --catalog "/work/Ravo Library.sqlite" --json
ravo catalog set-create --catalog "/work/Ravo Library.sqlite" --name "Job" \
  --asset-id <id> --json
ravo catalog list --catalog "/work/Ravo Library.sqlite" --set-id <set-id> --json

A smart collection stores a versioned library query (--kind smart --query). Selecting a set is session state in Studio; the set itself survives reopen.

Virtual copies and stacks:

ravo catalog version-create --catalog "/work/Ravo Library.sqlite" --asset-id <id> --json
ravo catalog stack --catalog "/work/Ravo Library.sqlite" \
  --asset-id <id> --asset-id <id> --pick-id <id> --json
ravo catalog list --catalog "/work/Ravo Library.sqlite" --json
ravo catalog list --catalog "/work/Ravo Library.sqlite" --stack-expanded --json

Listing hides non-pick stack members unless --stack-expanded is passed.

Import files or directories

ravo catalog import --catalog "/work/Ravo Library.sqlite" \
  --input "/photos/one.jpg" --input "/photos/raw" --json

The --input option can be repeated. Each item is reported as imported, duplicate, unsupported, or failed. Add is the default and stores references plus rebuildable previews.

Mounted-card Copy/Move uses the same service contract:

ravo catalog import --catalog "/work/Ravo Library.sqlite" \
  --input "/Volumes/CARD/DCIM" --mode copy \
  --destination "/photos/client-job" --organize month \
  --rename-template "job-{date}-{sequence}-{stem}{ext}" \
  --second-copy "/backup/client-job" --preview standard --json

The rename grammar accepts only {date}, {stem}, {sequence}, and {ext}; omitting {ext} appends the source extension. Primary and second-copy roots must already exist and be distinct. Every planned media/XMP collision rejects before publication, and each item reports its primary/second paths plus copies_verified. --no-recursive limits a directory input to its direct children. Move deletes a reverified source only after requested copies verify and the primary asset is cataloged.

Generate a preview

ravo catalog preview --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --max-edge 1600 --json

The result contains the preview cache path, dimensions, and an original_missing flag. --max-edge must be a positive integer when supplied.

Run a non-persistent Develop probe

catalog probe renders the current recipe without changing the recipe or preview records:

ravo catalog probe --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --set exposure=0.5 --set toneEqMidtones=0.2 \
  --max-edge 512 --json

Use --baseline to probe the synthesized product baseline instead of the stored edit:

ravo catalog probe --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --baseline --set contrast=0.1 --json

The result includes output profile, dimensions, channel statistics, clipping counts, display-luma mean, and gpu_backend (cpu when the preview RGB path stayed on the CPU; otherwise the Engine QRhi backend such as metal). Unknown, duplicate, non-finite, or out-of-range Develop fields fail before publication. The command guarantees recipe_unchanged: true and preview_records_unchanged: true on success.

Optional --output writes a throwaway display PNG of the in-memory probe pixels. It is not a catalog preview record, must end in .png, and never replaces an existing file:

ravo catalog probe --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --baseline --set exposure=0.5 \
  --output "/tmp/probe.png" --json

Save Develop values

catalog develop applies values and saves the resulting recipe:

ravo catalog develop --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --exposure-ev 0.5 \
  --set highlights=-0.2 --set toneEqMidtones=0.15 \
  --set watermarkEnabled=1 --watermark-text "RAVO {stem}" --json

Apply a Lightroom CRS preset onto the current Develop recipe:

ravo catalog develop --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --from-xmp "/presets/look.xmp" --json

That overlays mapped look groups and keeps crop, masks, Retouch, and profiles. --from-xmp can be combined with later --set values.

Apply a chosen Develop field set from one catalog asset onto explicit destinations. Unrelated destination edits stay. Preflight rejects empty or duplicate IDs, unknown fields, missing assets, and a stale --revision with no writes. Per-item failures and cancellation leave completed photos committed:

ravo catalog develop-apply --catalog "/work/Ravo Library.sqlite" \
  --from-asset asset-source --asset-id asset-a --asset-id asset-b \
  --fields exposure,temperature --json

Convenience flags are available for --exposure-ev, --saturation, and --contrast. Use repeated --set name=value for the numeric Develop fields exposed by the current recipe contract, including geometry, profiles, white balance, color, RAW repair, lens, tone, and effect fields. Values must be finite and each field keeps its own validation bounds. Discover the current names and ranges with ravo develop-fields --json.

Texture uses texture in [-2,2], textureDetailThreshold in [0.01,100] original-input pixels, and integer textureIterations in [1,5]. It is ordered before Sharpen; texture=0 is identity and is omitted from a canonical recipe.

Use repeated --set-text name=value for advertised text fields. For example, the following selects a profile-explicit 3D LUT; space index 3 is Linear Rec709 and interpolation index 0 is tetrahedral:

ravo catalog develop --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --set-text lut3dFile="/looks/portrait.cube" \
  --set lut3dInputSpaceIndex=3 --set lut3dOutputSpaceIndex=3 \
  --set lut3dInterpolationIndex=0 --set lut3dStrength=1 --json

Run ravo lut inspect "/looks/portrait.cube" --json first when diagnosing a file. An enabled LUT is validated even at zero strength; resource errors never become a hidden identity fallback.

Offline camera-noise calibration

Calibration consumes an explicit version-1 sample document. Means and variances use black-subtracted uint16 sensor code values; they are not display RGB or normalized [0,1] values. Each sample also carries the number of sensor observations represented by that estimate:

{
  "identity": {"iso": 800, "make": "Sony", "model": "Example Camera"},
  "samples": [
    {"count": 4096, "signal_mean": 256, "variance": 153},
    {"count": 4096, "signal_mean": 2304, "variance": 1177},
    {"count": 4096, "signal_mean": 4352, "variance": 2201},
    {"count": 4096, "signal_mean": 6400, "variance": 3225},
    {"count": 4096, "signal_mean": 8448, "variance": 4249},
    {"count": 4096, "signal_mean": 10496, "variance": 5273},
    {"count": 4096, "signal_mean": 12544, "variance": 6297},
    {"count": 4096, "signal_mean": 14592, "variance": 7321}
  ],
  "schema": "ravo.camera-noise-samples",
  "units": "black_subtracted_uint16_code_values",
  "version": 1
}

Fit and inspect a profile with:

ravo noise calibrate samples.json --output camera-iso800.rnoise.json --json
ravo noise inspect camera-iso800.rnoise.json --json

The fitter requires 8–1024 samples spanning at least 256 code values, rejects malformed/non-finite or insufficient inlier data, and never invents fallback coefficients. Output is deterministic, versioned and SHA-256 protected. The destination must not already exist; the command never modifies the sample file, a photo, a catalog, or an implicit profile directory. Current denoisers do not automatically load this artifact.

--watermark-text sets the one bounded text field. The current fixed-font contract accepts its documented ASCII subset plus newline and the {stem} and {asset_id} tokens; arbitrary SVG paths, system fonts, and EXIF variables are not CLI compatibility inputs.

To read the stored versioned recipe without changing it:

ravo catalog recipe --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --json

Review state, tags, and metadata

Set a rating from 0 to 5:

ravo catalog rate --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --rating 4 --json

Add or remove tags:

ravo catalog tag --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --add "landscape, dusk" --json
ravo catalog tag --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --remove dusk --json

Write catalog metadata:

ravo catalog metadata --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --title "Evening ridge" \
  --description "Captured after sunset" --creator "A. Photographer" \
  --copyright "2026 A. Photographer" --json

Without write flags, tag and metadata return the current values. These operations do not rewrite the original file.

History and snapshots

List recipe history:

ravo catalog history --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --json

Create and restore a labeled snapshot:

ravo catalog snapshot --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --label "Client proof" --json
ravo catalog restore --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --history-id 7 --json

Restore validates the stored recipe before making it current. An invalid or foreign history ID returns not_found or a structured validation error.

Export from the catalog

Render a selected asset:

ravo catalog export --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --output "/exports/photo.jpg" \
  --format jpeg --quality 92 --jpeg-subsampling auto \
  --metadata no-location --json

Export an exact original copy:

ravo catalog export --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --output "/exports/original.cr2" \
  --format original --json

PNG example:

ravo catalog export --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --output "/exports/photo.png" --format png \
  --png-bit-depth 8 --png-compression 5 --json

TIFF example:

ravo catalog export --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --output "/exports/photo.tif" --format tiff \
  --tiff-compression deflate_predictor \
  --tiff-compression-level 6 --tiff-sample-type uint8 \
  --tiff-grayscale-if-neutral --tiff-resolution-dpi 300 --json

Batch example with deterministic portable names:

ravo catalog export-batch --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --asset-id asset-456 \
  --output-dir "/exports/delivery" \
  --filename-template '{stem}-{sequence}{ext}' \
  --format jpeg --quality 92 --metadata no-location --json

The output directory must already exist. Batch preflight rejects duplicate asset IDs, duplicate expanded names, missing sources, and any existing target before writing the first item. Runtime failure or cancellation stops at the failed item and reports any earlier completed outputs in structured context.

Accepted format spellings are png, jpeg/jpg, tiff/tif, and original/copy/original-copy. Existing output files return conflict and are never overwritten implicitly.

Refresh capture metadata from the current original without touching the file:

ravo catalog refresh-metadata --catalog "/work/Ravo Library.sqlite" \
  --asset-id asset-123 --json

Create and apply a Recipe Style

ravo recipe style-create source.recipe.json --name "Warm repair" \
  --output warm-repair.rstyle.json --json
ravo recipe style-validate warm-repair.rstyle.json --json
ravo recipe style-apply warm-repair.rstyle.json --asset-id target-asset \
  --input file:///photos/target.jpg --output target.recipe.json --json

Styles are versioned Recipe templates. Output paths must be new; unknown/newer/malformed state and legacy .dtstyle fail instead of dropping operations. The commands above create and apply schema-v1 complete-replacement styles.

Studio can also create schema-v2 selective presets. To apply either schema to an existing target recipe, use the explicit target form:

ravo recipe style-apply selected.rstyle.json \
  --target-recipe target.recipe.json --output merged.recipe.json --json

A schema-v2 style overlays only its sorted selected logical fields and preserves unselected target state. It requires --target-recipe; the older --asset-id / --input form remains valid only for schema-v1 complete styles and will not silently widen a selective preset.

Render a standalone recipe to PNG

ravo render "/photos/source.cr2" \
  --recipe "/work/photo.recipe.json" \
  --output "/exports/photo.png" --backend cpu \
  --width 1600 --height 1067 --json

The standalone render path accepts only --backend cpu. --width and --height are optional positive integers. The recipe must validate, and the output is published atomically as PNG. Use catalog export when you need the catalog's current saved recipe and format-specific encoder options.

Exit codes

With or without JSON, a failed command returns a non-zero exit status:

Error code Exit status Meaning
invalid_argument 2 Command syntax or argument shape is invalid.
not_found 3 The catalog, asset, original, or history entry is not found.
validation 4 Data or an option violates a contract.
unsupported 5 The input or requested capability is outside the current product boundary.
io or conflict 6 A file/database operation failed or the destination exists.
cancelled 7 The operation was cancelled.
internal 70 An unexpected internal failure occurred.

Common questions

Can I parse stdout safely in automation?

Yes. Use --json and read the single protocol object. Diagnostics are an empty array in the current protocol, and service logs stay out of stdout.

Why did catalog probe not create a preview file?

Probe does not write a catalog preview record or change the stored recipe. Pass --output /path/to/probe.png when you want a throwaway display PNG of the in-memory result. That file is still not a catalog cache entry, and an existing path returns conflict.

Why did an unknown --set field fail?

Develop fields are versioned and validated. Discover the current names, kinds, and ranges with ravo develop-fields --json. ravo operations --json lists engine operation descriptors, not --set field names. Ravo does not ignore a typo.

Can the CLI export high-precision PNG and TIFF files?

Yes. PNG 16-bit and TIFF uint16/float16/float32 requests use engine-owned higher-precision samples from the active recipe. Mismatched low-level RGB8 sources still fail closed instead of fabricating precision.