Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CLI commands

The faucet binary exposes these commands. Pass --log-level <level> (or set FAUCET_LOG) to control logging.

CommandWhat it does
faucet run [config]Run the pipeline(s) in a config file.
faucet validate [config]Parse, expand, and validate a config without running it.
faucet preview [config]Run only the source side and print records to stdout.
faucet schema <target>Print the JSON Schema for the whole config (config), a connector, a transform, or any block.
faucet listList every compiled-in source, sink, and transform with a one-line description.
faucet init [name]Scaffold a commented config skeleton from connector schemas.
faucet new connector <name> --kind <source|sink>Scaffold a ready-to-build connector crate.
faucet search <term>Search the connector registry for connectors by name/keyword.
faucet install <name>Print how to enable/obtain a connector from the registry.
faucet conformance [name]Score each connector against the SDK contract; print its maturity tier + capabilities.
faucet plan [config]Read-only preview of what a config would do — zero writes.
faucet dev <config> --sample <f>Watch + re-run a sample on save with a live diff (cli-dev).
faucet doctor [config]Probe every connector (auth/network/permissions) and print a checklist.
faucet test <specs…>Run fixture-based offline pipeline tests from one or more spec files.
faucet replicate [config]Bulk-snapshot a table, then hand off to CDC for a gap-free mirror.
faucet schedule [config]Run a pipeline on a cron schedule (long-running foreground process).
faucet serveRun a long-running HTTP control plane: submit / poll / cancel pipeline runs over REST.
faucet completions <shell>Print a shell tab-completion script (bash / zsh / fish / powershell / elvish).
faucet migrate [config]Upgrade a config written against an older grammar to the current shape (idempotent).
faucet doctor --offline [config]Static, credential-free config lints (no network) — dangling/unused auth, unused vars, no-op sink batch_size.
faucet fmt [config] [--check]Canonicalize a config (stable key order); --check is a CI gate.
faucet explain [config]Plain-English narration of what a pipeline does (offline, zero I/O).
faucet history [config]Terminal view of the run history in a config’s catalog: store.
faucet run … --output json|ndjsonMachine-readable end-of-run summary (per-row + totals) for scripting.

[config] is optional for run / validate / preview / doctor / replicate / schedule: if omitted, faucet auto-discovers faucet.yaml.yml.json in the current directory.

run

faucet run pipeline.yaml
faucet run                              # auto-discover faucet.yaml in cwd
faucet run --from-env                   # build the pipeline entirely from FAUCET_* env vars
faucet run pipeline.yaml --env-file prod.env
faucet run pipeline.yaml --no-env-file
faucet run pipeline.yaml --clock 2026-03-01          # backfill: set ${now.*} clock to midnight UTC
faucet run pipeline.yaml --clock 2026-03-01T02:00:00-08:00  # backfill: precise RFC 3339 timestamp

Flags:

FlagPurpose
--clock <value>Override the clock used by ${now.*} tokens. Accepts an RFC 3339 timestamp (2026-03-01T00:00:00Z) or a bare date (2026-03-01, treated as midnight UTC). Default: process start time in UTC. Use this for backfills — run the same config with a different date without changing the file.
--profile <name>Select a named overlay from the config’s profiles: block (see Config composition). Overrides FAUCET_PROFILE.
--env-file <path> / --no-env-fileSame .env handling as validate / preview.
--from-envBuild the pipeline entirely from FAUCET_* environment variables; mutually exclusive with a positional config path.
--select <id> / --only <glob> / --skip <id|glob>Runtime matrix-row selection by id. --select/--only force-include by name (bypassing the status gate); --skip removes last. See Row selection. Env: FAUCET_SELECT / FAUCET_SKIP.
--status <tier>Additively widen the eligible readiness set beyond {mandatory, active}: available / draft / archived. Env: FAUCET_STATUS.
--tag <t>Narrow the eligible set to rows carrying any listed tag (union). Env: FAUCET_TAGS.
--include-parents <off|eligible|all>Parent/depends_on inclusion policy for a narrowed run set (default off). Overrides selection.include_parents:. Env: FAUCET_INCLUDE_PARENTS.
--param <NAME=VALUE>Supply a value for a declared params: entry. Repeatable; coerced to the declared type. A required param with no value is an error naming it.
--param-env <NAME[=VALUE]>Override an environment variable for this run’s ${env:VAR} resolution only. Bare NAME takes the value from the caller’s environment (so a secret stays out of the process arguments). The process environment is not modified. Repeatable.
--tuiShow a live full-screen terminal UI while the pipeline runs: per-invocation source→sink route, records in/out, records/s, errors, DLQ counts, bookmark age, and a scrolling log pane. Press q (or Ctrl-C) to cancel cooperatively — in-flight invocations stop at their next page boundary and flush their sinks. Requires a binary built with the cli-tui feature (cargo install faucet-cli --features cli-tui); on a non-TTY stdout (CI, pipes) the flag logs a notice and runs normally. When the config has an observability.prometheus block, the /metrics endpoint stays up alongside the TUI; OTLP metrics export is skipped under --tui (traces are unaffected).
--quietSuppress the inline live progress line.

Live progress line

On an interactive terminal, faucet run shows a lightweight inline progress line per active matrix row — row_id src→sink <in> in / <out> out <r>/s page <p> <elapsed> — updated a few times a second and drawn on stderr so piped stdout stays clean for records. It is auto-disabled on a non-TTY stdout (CI, pipes) and under --quiet, both of which fall back to the periodic tracing progress logs; --tui (when built in) supersedes it. Requires the cli-progress build feature, which ships in the default build. The numbers come from the same in-process Prometheus recorder the TUI samples — no extra hot-path cost.

validate

Reports one line per expanded matrix row. Use it in CI to catch config errors before deploying.

faucet validate pipeline.yaml

When the config contains secrets-manager directives (${vault:…}, ${aws-sm:…}, etc.), faucet validate resolves them as a real preflight and prints one confirmation line per reference (never the value):

secret: vault:secret/data/faucet/api#token → resolved
ok: 'my-pipeline' rows=1 (roots=1, children=0) execution=(defaults)
  - default [root] source=rest sink=jsonl delivery=at-least-once

Each row line ends with the derived end-to-end delivery guarantee for that row’s source × sink × config — at-least-once, effectively-once (atomic watermark), or effectively-once (keyed upsert) — computed regardless of the requested delivery: mode, so an upsert-keyed row is reported as effectively-once even without delivery: exactly_once. See delivery.

Pass --no-secrets to validate grammar and structure only, skipping all secret fetches. This is useful in CI environments that lack credentials, or in local development before vault access is available:

faucet validate --no-secrets pipeline.yaml

A config declaring params: reports its trigger surface and validates against type-shaped placeholders for any required param, so a parameterized config passes CI without inventing values:

params: 4 declared (required: api_token, tenant_id) — validated against placeholders; pass --param NAME=VALUE to bind for real

Pass --param NAME=VALUE (repeatable) to switch to strict binding and check one concrete invocation; --param-env NAME[=VALUE] overrides an environment variable for the validation only.

Composition flags

When a config uses composition (extends: / profiles: / !include), validate resolves it like run does:

faucet validate app.yaml --profile prod        # select a named overlay
faucet validate app.yaml --show-composed       # print the fully merged config
  • --profile <name> selects a named overlay from profiles: (also settable via FAUCET_PROFILE; the flag wins). An undeclared name is a clear load-time error.
  • --show-composed prints the fully composed document — bases merged, the selected profile applied, !include fragments substituted, and the extends: / profiles: metadata stripped — before ${...} interpolation. It’s the fastest way to confirm a multi-file setup resolves to what you expect.

validate accepts the same row-selection flags as run (--select/--only/--skip/--status/--tag/--include-parents). When the config uses the readiness ladder or tags — or a selector is passed — it prints a run-selection report listing each row’s resolved status, its tags, and whether the selection would RUN or skip it, and surfaces selection errors (empty run set, missing ancestor, unknown token) in CI without a run.

discover

faucet discover conn.yaml                      # print a generated config to stdout
faucet discover conn.yaml -o pipeline.yaml     # write it to a file (--force to overwrite)
faucet discover conn.yaml --include 'public.*' --exclude '*.tmp_*'
faucet discover conn.yaml --source warehouse   # introspect a named pipeline.sources template
faucet discover conn.yaml --json               # machine-readable dataset list

Connects to the config’s source, enumerates the datasets behind it (tables / collections / indices / object-store prefixes), and emits a ready-to-run config with one matrix row per dataset — the input document with its matrix: block replaced, secrets echoed as raw ${…} references. The generated config passes faucet validate. Supported sources: postgres, mysql, mssql, sqlite, mongodb, elasticsearch, bigquery, snowflake, s3, gcs.

FlagPurpose
--source <name>Which pipeline.sources template to introspect (default default, the singular pipeline.source).
--include <glob> / --exclude <glob>Repeatable *-wildcard filters on dataset names (no includes = everything; excludes win).
-o, --output <file> / --forceWrite the generated config to a file instead of stdout; --force overwrites.
--jsonEmit the discovered DatasetDescriptor list as JSON instead of a config.
--profile / --env-file / --no-env-fileSame semantics as run / validate.

See the source discovery cookbook.

preview

Runs the first root row’s source and prints records (via the stdout sink). Children aren’t previewed because they need parent records to resolve ${parent.path} tokens.

faucet preview pipeline.yaml --limit 10
faucet preview app.yaml --profile dev --limit 5   # preview with a named profile overlay

--profile <name> / FAUCET_PROFILE selects a named overlay from profiles: before previewing. Same semantics as run and validate.

plan

A read-only “what would this config change” preview — it runs the sink’s non-mutating check() probe and pure schema/lineage analysis but never writes to any sink.

faucet plan pipeline.yaml
faucet plan pipeline.yaml --sample fixtures.jsonl        # preview output schema/volume offline
faucet plan pipeline.yaml --live --limit 20 --json       # capped read-only source pull, JSON out
faucet plan pipeline.yaml --diff                         # config-change diff vs the last run
faucet plan pipeline.yaml --diff --json                  # machine-readable diff (CI gate)

Reports, for the selected row (--row, default the first root): the resolved source/sink/write-mode/delivery guarantee, the transform chain in lifecycle order, which quality/contract/masking/drift policies are in effect, and the lineage column ops. Given a sample (--sample <fixture> offline, or --live --limit N for a capped read-only source pull), it also reports the inferred output schema, the sink schema delta (adds / widenings / incompatible via diff_schema when the sink exposes current_schema(); “schemaless — no delta” otherwise), and a volume estimate. The data pass runs through the offline harness, so no sink is ever written. Offline by default; --resolve-secrets opts into the real secrets path.

plan --diff — config-change preview (#374)

A terraform plan-style diff of the current config against what last ran. On every successful faucet run / replicate / schedule --once, a redacted snapshot of the resolved + expanded config is recorded into the catalog store (best-effort — recording never fails a run). faucet plan --diff re-expands the current config, loads the last snapshot, and renders a per-row semantic diff:

Pipeline: hibob   (last run 2026-07-18 09:12 UTC)

  + people             NEW ROW — will be created
  ~ payroll            CHANGED
      source.config.page_size      100  ->  500
      source.config.path           /v1/pay  ->  /v1/payroll
  ~ timeoff            CHANGED
      source.config.token          (secret rotated)
  - benefits           REMOVED — no longer in the run set
  = employees          unchanged

Summary: 1 to create, 2 to change, 1 removed, 1 unchanged.

Because the diff operates on the resolved + expanded model, a one-line ${vars.x} edit that fans out across many rows shows up as the real per-row effect, and two textually-different files that resolve to the same movement show no diff. Requires a catalog: block (faucet schema catalog) and the catalog build feature. --diff resolves secrets so the diff matches what run recorded; every secret-sourced value is stored only as a stable <secret:sha256:…> token, so a rotated credential surfaces as “secret rotated” and no secret is ever persisted. On a first run (nothing recorded yet) every row is reported as new.

dev

A watch-and-diff authoring loop (requires the cli-dev build feature). Re-runs a sample through the offline harness on every config save and prints the schema, DLQ count, errors, and a diff vs the previous run.

faucet dev pipeline.yaml --sample fixtures.jsonl

Watches the config file’s directory and the directories of any extends: / !include fragments, so editing an included fragment re-triggers a run. In a non-TTY (CI) or with --once it runs a single pass and exits. Debounce the watcher with --debounce-ms.

schema

faucet schema config          # the WHOLE config document (top-level grammar)
faucet schema source rest
faucet schema sink bigquery
faucet schema transform keys_case
faucet schema dlq
faucet schema execution
faucet schema contract
faucet schema masking
faucet schema sla
faucet schema notifications
faucet schema secrets
faucet schema triggers
faucet schema catalog
faucet schema params

faucet schema config prints a composed JSON Schema for the entire faucet.yaml / faucet.json document — the top-level grammar (version, name, vars, auth, pipeline, matrix, execution, and every optional block such as schedule / lineage / quality / dlq / resilience that is compiled into your binary) plus per-connector type discrimination: the source / sink positions become a oneOf over the connector kinds your binary knows, each branch embedding that connector’s own config schema. Point an editor at it for autocomplete and validation as you type — see Editor setup.

faucet schema transform <name> prints the inline config schema for a transform (e.g. keys_case lists the valid mode: values). Run faucet list to see which transforms are compiled into your binary.

faucet schema execution prints the schema for the top-level execution: block, including concurrency, error handling, and adaptive batch sizing.

faucet schema masking prints the JSON Schema for the pipeline.masking: (PII detection + column-masking) block — see masking.

faucet schema sla prints the schema for the top-level sla: (freshness/volume SLA) block — see SLA monitoring.

faucet schema params prints the schema for one entry of the top-level params: (typed run parameters) block — see Parameters & pipeline templates.

faucet schema secrets prints the directive grammar and auth requirements for all four secrets-manager backends in machine-readable JSON — useful for tooling that needs to understand the interpolation syntax without reading the docs.

faucet schema triggers prints the JSON Schema for the --triggers file format (the TriggersFile / TriggerSpec / TriggerKind types). Requires the triggers Cargo feature.

faucet schema catalog prints the JSON Schema for the top-level catalog: (Data Movement Catalog store) block — see the catalog cookbook. Requires the catalog Cargo feature.

init

faucet init my_pipeline --source postgres --sink bigquery

Required fields are surfaced with a typed placeholder and a # REQUIRED marker; optional fields are commented out so connector defaults apply. The interactive mode (--interactive) is gated behind the cli-interactive feature.

Singer discovery. For the Singer bridge source, add --discover --executable <tap> to run the tap’s --discover, write the returned catalog to catalog.json, and scaffold a config that inlines the catalog and lists the discovered streams (with stream: left empty for you to choose):

faucet init --source singer --discover --executable tap-github -o pipeline.yaml

faucet doctor then verifies the tap resolves on PATH and that the selected stream exists in the catalog.

new

Scaffold a new connector crate (not a config) that follows every repo convention — ready to cargo build and publish:

faucet new connector acme --kind source            # → faucet-source-acme/
faucet new connector acme --kind sink --common      # + a faucet-common-acme/ crate
faucet new connector acme --kind source -o crates/  # write into crates/

The generated crate has the standard module layout (config.rs, stream.rs or sink.rs), a JsonSchema-deriving config, config_schema() / connector_name() overrides, the #![cfg_attr(docsrs, feature(doc_cfg))] crate-root line, the [package.metadata.docs.rs] block, system-name-first crates.io keywords, a README, and a passing unit test — so cargo test is green out of the box with a trivial passthrough. Fill in the TODOs, then publish. See Authoring a connector.

search / install / list --available

Discover connectors from the connector registry — a curated, feature-independent index of every built-in connector plus community faucet-source-* / faucet-sink-* crates.

faucet search kafka              # matches on name, description, keywords, crate
faucet search cdc --json         # machine-readable
faucet list --available          # the whole registry; ● = in this binary, ○ = installable
faucet install bigquery --kind sink
faucet install my-connector --index ./my-registry.json

faucet install <name> never runs anything — it prints the recipe:

  • a built-in already compiled in → “already available”;
  • a built-in not compiled in → cargo install faucet-cli --features <kind>-<name>;
  • a community connector → a copy-pasteable custom-binary snippet (see Custom binaries).

--index <path> points any of these at a custom/mirror index instead of the built-in one. Ambiguous names (a connector that is both a source and a sink, e.g. postgres) need --kind source|sink.

conformance

Score every compiled-in connector against the faucet SDK contract and print its maturity tier — 🟢 Stable, 🟡 Experimental, 🟠 Beta, ⚪ Draft — plus its capability badges (exactly-once, discover, upsert, schema-evolution).

faucet conformance                     # score every connector, highest first
faucet conformance --all               # same; explicit form for CI
faucet conformance --kind sink         # sinks only
faucet conformance postgres            # a detailed scorecard (+ badge URL) for one connector
faucet conformance --json              # machine-readable scorecards
faucet conformance --min-tier stable   # CI gate: non-zero exit if any connector is below Stable

The score (0–100) is computed from authoritative, instantiation-free signals: a verified cli/connectors/registry.json entry (40) + a real config schema (30) form the Stable gate at 70; documentation, exactly-once delivery, and the kind-specific capability (source discovery / sink upsert + schema evolution) are bonuses on top. Every conforming built-in is Stable with capability badges; an incomplete third-party connector (missing a verified entry or a schema) lands at Experimental / Beta.

--min-tier <tier> turns the report into an opt-in CI gate: the command exits non-zero if any scored connector is below the named tier — combine it with --kind / a NAME to scope the gate. A single-connector scorecard also prints a shields.io badge URL third-party authors can drop into their crate README. The per-connector tier is mirrored in cli/connectors/registry.json (validated against this score in CI) and shown in faucet list and the connector conformance & tiers page.

doctor

faucet doctor pipeline.yaml                  # checklist; exit code = # of failed probes
faucet doctor pipeline.yaml --timeout-secs 5 # per-probe timeout (default 10)
faucet doctor pipeline.yaml --json           # machine-readable, for CI gating
faucet doctor app.yaml --profile prod        # probe with a named profile overlay applied

Runs a fast, non-mutating preflight against every connector in the config so misconfiguration surfaces before a real run. For each root invocation it probes the source, sink, and state store and prints a green/red checklist with elapsed times; the exit code equals the number of failed probes (clamped to 255).

  • Sources reuse the real read path — the probe pulls a single page and stops (never the full dataset). Sources whose first page would block or mutate use a targeted probe instead: webhook (port bindable), websocket (TCP connect), postgres-cdc (slot reachable), kafka (cluster metadata).
  • Sinks run a read-only connect/auth/metadata call — SELECT 1, HeadBucket, PING, tables.get, cluster health, fetch_metadata, or a directory-writable check for file sinks. Never a real write.
  • State stores do a sentinel put/get/delete that leaves no residue.
  • SLA (when a top-level sla: block is configured) reads the persisted run history and reports staleness of the last successful run vs max_staleness_secs and volume-baseline warm-up state — read-only.

Child invocations (parent/child matrix rows) are listed but not probed — their configs depend on parent records that only exist at run time. Probe messages are scrubbed for resolved secrets before printing.

--profile <name> / FAUCET_PROFILE selects a named overlay from profiles: before probing (same semantics as run and validate).

See the Troubleshooting cookbook page for reading the output and common failures.

test

faucet test tests/*.yaml                    # run every case; exit code = # of failed cases
faucet test tests/orders.yaml --filter null # only cases whose name contains "null"
faucet test tests/*.yaml --json             # machine-readable { total, passed, failed, tests }
faucet test tests/*.yaml --clock 2026-03-01 # default ${now.*} clock for cases without clock:

Runs fixture-based, fully-offline pipeline tests. Each case in a spec file feeds sample records through the real transform → quality → contract path with an in-memory source, sink, and DLQ — the configured source and sink are never built or contacted — and asserts the output records, DLQ routing, counts, or an expected failure. The exit code equals the number of failed cases (clamped to 255), so CI gates on it directly.

Flags:

FlagPurpose
--filter <substring>Run only cases whose name contains the substring.
--jsonEmit the JSON report instead of the human checklist.
--clock <value>Default ${now.*} clock for cases without clock: (RFC 3339 or YYYY-MM-DD).
--profile <name>Profile overlay applied to referenced configs (same semantics as run).
--resolve-secretsResolve secrets-manager directives in referenced configs. Default: offline, directives stay unresolved.
--env-file <path> / --no-env-fileSame .env handling as run / validate.

faucet schema test prints the spec file’s JSON Schema. See the Testing pipelines cookbook page for the spec grammar, matching semantics, and a CI recipe.

dlq

Inspect, replay, and discard the dead-letter-queue envelopes a pipeline’s dlq: sink wrote. A DLQ location is a local .jsonl file, a directory of *.jsonl files, or a glob.

faucet dlq inspect ./dlq/breaches.jsonl                          # breakdown + sample
faucet dlq replay pipeline.yaml --from ./dlq/breaches.jsonl --dry-run
faucet dlq replay pipeline.yaml --from ./dlq/breaches.jsonl      # re-feed through the pipeline
faucet dlq discard ./dlq/breaches.jsonl --reason contract --before 7d

faucet dlq inspect <location> — group envelopes by reason and error kind with a sample.

FlagEffect
--reason <r>Only include envelopes with this reason (partial / dlq_all / quality / schema_drift / contract).
--limit <n>Sample size. Default: 5.
--encryption-key <k>Key for a DLQ sealed at rest by the jsonl sink’s encryption block; repeat for rotated keys. Sealed lines without a matching key are counted as encrypted, never mistaken for malformed. Requires an encryption-feature build.
--jsonEmit a JSON summary.

faucet dlq replay <config> --from <location> — re-feed the quarantined payloads through the config’s transforms → quality → contract → sink. Rows that fail again go to a fresh DLQ, never back to the source.

FlagEffect
--from <location>DLQ location to replay from (required).
--reason <r>Replay only envelopes with this reason.
--encryption-key <k>Key for a sealed DLQ (repeatable). When omitted, the config’s own dlq: jsonl encryption block is used automatically.
--failed-dlq <path>Where re-failed rows go. Default: a replay-failed.jsonl sibling of the source.
--row <id>Which root of the config to replay through. Default: the first root.
--dry-runReport what would be replayed without writing.
--jsonEmit a JSON result.
--env-file <path> / --no-env-file / --profile <name>Same config-load handling as run.

faucet dlq discard <location> — remove processed envelopes.

FlagEffect
--reason <r>Only discard envelopes with this reason.
--before <when>Only discard envelopes older than an RFC 3339 timestamp or a relative age (7d / 24h / 30m).
--deletePermanently delete instead of archiving to a <file>.archived.jsonl sibling.
--encryption-key <k>Key for a sealed DLQ (repeatable). Kept/archived lines stay sealed verbatim; decryption happens only in memory for filtering.
--jsonEmit a JSON result.

See the Dead-letter queues cookbook page for the envelope shape and the inspect → fix → replay → discard workflow.

contract

faucet contract pipeline.yaml                       # validate + human summary
faucet contract pipeline.yaml --export contract     # canonical contract JSON
faucet contract pipeline.yaml --export json-schema  # standalone JSON Schema
faucet contract pipeline.yaml --export openlineage  # OpenLineage schema facet

Validates the config’s pipeline.contract: block (a malformed contract exits non-zero with the compile error) and prints a summary of the promised fields, constraints, and breach policy — or, with --export, a machine-readable artifact for downstream consumers. Offline-safe: secrets are never fetched. Requires the contract Cargo feature (in the default build). See the Data contracts cookbook page.

masking

faucet masking pipeline.yaml     # validate + per-destination rule breakdown
faucet masking                   # auto-discover faucet.yaml in cwd

Validates the config’s pipeline.masking: block (a malformed policy exits non-zero with the compile error) and prints, per destination sink, which rules apply — the fast way to confirm applies_to scoping. Offline-safe: secrets are never fetched. Requires the masking Cargo feature (in the default build). See the masking cookbook page.

catalog

(requires the catalog build feature — included in full)

faucet catalog datasets --config pipeline.yaml                 # list catalogued datasets
faucet catalog datasets --config pipeline.yaml --kind csv --q users --json
faucet catalog show 3f2a9c1e0b7d4a55 --config pipeline.yaml    # detail (id prefix ok)
faucet catalog lineage --config pipeline.yaml --root 3f2a9c1e0b7d4a55 --depth 3

Browses the Data Movement Catalog named by the config’s catalog: block: the dataset list (newest activity first, --kind / --q filters), one dataset’s detail (schema timeline with diffs, recent volume, upstream/downstream edges), and the lineage graph. All subcommands accept --json; --config auto-discovers faucet.yaml in cwd when omitted. Read-only — it never mutates the store.

template

(requires the templates build feature — included in full)

faucet template register  tenant-sync.yaml --store sqlite:./faucet-templates.db
faucet template register  tenant-sync.yaml --id tenant-sync --tag dev --description "per-tenant events"
faucet template register  tenant-sync.yaml --launch            # register AND make live
faucet template list      --store sqlite:./faucet-templates.db
faucet template show      tenant-sync --store sqlite:./faucet-templates.db --version 2
faucet template promote   tenant-sync --tag prod --version dev  # move an environment channel
faucet template launch    tenant-sync --version pre-prod        # move `stable` (the release lever)
faucet template rollback  tenant-sync                           # re-launch `previous`
faucet template deprecate tenant-sync --reason "superseded"      # retire (`--undo` revives)
faucet template run       tenant-sync --store sqlite:./faucet-templates.db \
  --version prod --param tenant_id=acme --param-env API_HOST=eu.example.com
faucet template delete    tenant-sync --store sqlite:./faucet-templates.db --version 1

Register a config declaring params: once, then trigger runs by id — the register-once / trigger-by-id model. See the Parameters & pipeline templates cookbook page.

FlagPurpose
--store <url>Registry location: sqlite:<path>, a postgres://… URL, or memory. Same grammar as catalog.url and faucet serve --history — point serve at the same URL to trigger these templates over HTTP/MCP. Env: FAUCET_TEMPLATE_STORE. SQL stores need serve-history-sqlite / serve-history-postgres.
--id <slug>(register) Registry id (^[a-z0-9][a-z0-9_-]*$). Derived from the config’s name: when omitted.
--description <text>(register) Shown by list / show. Carried forward from the previous version when omitted.
--launch(register) Launch the new version immediately, making it stable. Off by default — registering a build must never move existing callers.
--tag <channel>(register) Point an assignable channel at the new version; repeatable. (promote) The channel to move. One of the closed set: dev, test, staging, pre-prod, canary, prod. The derived channels (stable, previous, newest) cannot be assigned — stable moves only via launch.
--version <n|channel>(show / run / delete / promote / launch) Version selector: an exact number or a channel name. Defaults to stable for show/run/promote and to newest for launch. For delete, omitting it removes every version; giving one removes just that version. For promote, it is the target--tag prod --version dev copies whatever dev names today.
--reason <text>(deprecate) Why the template is being retired; surfaced to anyone who triggers it.
--undo(deprecate) Revive instead of retire.
--param <NAME=VALUE>(run) Supply a declared param. Repeatable.
--param-env <NAME[=VALUE]>(run) Override an environment variable for this materialization only. Repeatable.
--dry-run(run) Materialize and validate without writing to any sink.
--limit <n>(run) Stop after writing this many records.
--jsonMachine-readable output for every subcommand.

Every register appends a new numeric version (auto-incrementing from 1) and does not move existing callers — the 20 most recent per id are kept. Making a version live is the separate launch step, so a template is draft until something is launched, then launched, and deprecated once retired (a deprecated template keeps serving pinned and stable callers, but every trigger warns; delete is the hard stop).

On top of the numbers sits a closed set of channels. Three are derived and never assignable: stable (the launched version — the default when no --version is given), previous (the rollback target), and newest (the build tip). Six are assignable with promote: dev, test, staging, pre-prod, canary, prod. There is deliberately no latest — it means both “newest build” and “current release”, so it is rejected with a message naming stable and newest. An unknown channel name is rejected with the valid list; deleting a version drops any channel and launch-log entry aimed at it.

Promote a version up the channels as it earns trust, then launch it when it should become what unpinned callers get; rollback re-launches previous. list shows each template’s status, live version, and build tip; show prints every version with the channels pointing at it plus the launch history. faucet template run executes through the identical path as faucet run, so observability, lineage, notifications, the catalog, and SLA evaluation all behave the same. The stored body is verbatim — ${env:…} / ${vault:…} resolve at trigger time, never at registration.

notify

(requires the notify build feature)

faucet notify test pipeline.yaml --event run_failure
faucet notify test --event circuit_open        # auto-discover faucet.yaml

Fires one synthetic event through the config’s notifications: rules using the real delivery path (no pipeline runs) — the fast way to confirm a Slack / PagerDuty / webhook channel is wired correctly. --event accepts any event kind (run_failure, run_success, sla_breach, circuit_open, contract_abort, dlq_threshold, scheduler_stuck). See the Notifications cookbook page.

replicate

faucet replicate pipeline.yaml                 # bulk snapshot, then stream CDC; Ctrl-C to stop
faucet replicate                               # auto-discover faucet.yaml in cwd
faucet replicate pipeline.yaml --env-file prod.env
faucet replicate pipeline.yaml --no-env-file
faucet replicate app.yaml --profile prod       # apply a named profile overlay

Bulk-snapshots a database table and then hands off to change-data-capture from a position captured before the snapshot, producing a true mirror (no gap, no duplicate rows) when paired with write_mode: upsert. The config must contain a top-level replication: block (see config reference); faucet run ignores that block, exactly as it ignores schedule:.

It runs two phases in order:

  1. Bulk snapshot — the replication.snapshot.source (a non-CDC query reader) back-fills the destination through the same sink and pipeline-level transforms.
  2. CDC handoff — the pipeline.source CDC connector streams every change committed after the captured position over the snapshot baseline.

When replication.continuous is true (the default) the CDC phase is a long-running foreground process — stop it with Ctrl-C or SIGTERM; the in-flight page flushes at the next page boundary before the process exits. With continuous: false it drains CDC once and exits. A durable state backend (file / redis / postgres, not memory) is required so an interrupted run resumes correctly.

Flags:

FlagPurpose
--profile <name>Select a named overlay from profiles: (also settable via FAUCET_PROFILE; the flag wins). Same semantics as run / validate.
--env-file <path> / --no-env-fileSame .env handling as run / validate.

See the replication cookbook for the correctness model, the resume behaviour, and the per-database retention caveats.

backfill

faucet backfill pipeline.yaml --from 2026-06-01 --to 2026-07-01 --window 1d
faucet backfill pipeline.yaml --from 2026-06-01 --to 2026-07-01 --window 1d --dry-run
faucet backfill pipeline.yaml --from 2026-06-01 --to 2026-07-01 --window 1d --resume
faucet backfill pipeline.yaml --from-bookmark '42' --to-bookmark '99' --bookmark-field seq

Replays a bounded historical window: chunks [from, to) into contiguous half-open window units, runs each through the normal pipeline path with its ${backfill.*} tokens substituted and the ${now.*} clock set to the window start, and records durable, resumable progress in the config’s state: store. Unit state keys are namespaced ({name}::backfill::{unit}) so the forward-sync bookmark is never touched; delivery is forced to at-least-once (pair with write_mode: upsert). Exits non-zero with the failed-unit count.

FlagPurpose
--from / --toWall-clock range: RFC3339 or YYYY-MM-DD (midnight in --timezone). Half-open.
--window <dur>Chunk size (45s, 30m, 6h, 1d, 1w). Default: the config’s backfill.window; omitted = one unit.
--from-bookmark <v>Bookmark mode: seed the scoped state key with this value (JSON or bare string) and run one unit. Requires a state: block.
--to-bookmark <v> / --bookmark-field <f>Upper bookmark bound: drop records whose field orders after the bound.
--concurrency <n>Max window units in flight. Default: backfill.concurrency, else 1.
--timezone <IANA>Date-boundary / ${now.*} timezone. Default: backfill.timezone, else UTC.
--row <id>Which root row to backfill (required when the config has several).
--into <sink>Redirect writes to a named pipeline.sinks template (staging-first).
--dry-runPrint the planned units without executing.
--resume / --restartContinue a prior backfill of the same range / discard its marker and start over.
--jsonMachine-readable plan/report.
--profile / --env-file / --no-env-fileSame semantics as run / validate.

See the backfill cookbook and the backfill: config block.

schedule

faucet schedule pipeline.yaml                  # run on cron schedule, foreground; Ctrl-C to stop
faucet schedule pipeline.yaml --once           # run exactly once now, then exit
faucet schedule pipeline.yaml --env-file prod.env
faucet schedule pipeline.yaml --no-env-file
faucet schedule app.yaml --profile prod        # schedule with a named profile overlay applied

Runs a pipeline on a recurring cron schedule in a long-running foreground process. The config must contain a top-level schedule: block (without one, faucet errors and suggests faucet run). Requires the schedule Cargo feature (included in full).

  • Stop with Ctrl-C or SIGTERM; the in-flight run drains for up to shutdown_grace_secs (default 30) before the process exits.
  • --once ignores cron timing and runs the pipeline exactly once immediately — handy for testing a scheduled config or for one-shot container invocations.
  • Missed ticks are skipped, not backfilled. A run that starts late emits faucet_schedule_run_lateness_seconds for monitoring.

Flags:

FlagPurpose
--onceRun exactly once now, then exit. Ignores cron timing.
--profile <name>Select a named overlay from profiles: (also settable via FAUCET_PROFILE; the flag wins). Same semantics as run / validate.
--env-file <path> / --no-env-fileSame .env handling as run / validate.

See the scheduling cookbook for worked examples, the overlap-policy decision tree, the resilience/supervisor model, and the full metric set to scrape.

serve

FAUCET_SERVE_AUTH_TOKEN=s3cret faucet serve --listen 0.0.0.0:8080
faucet serve --no-auth                             # explicit opt-in; required if no token
faucet serve --history sqlite:/var/lib/faucet/runs.db --default-config defaults.yaml

Runs a long-running HTTP control plane that accepts pipeline configs over REST, executes them under bounded concurrency (reusing the same executor as faucet run), and exposes status / cancel / list / SSE-logs endpoints plus /healthz, /readyz, and /metrics. Requires the serve Cargo feature (included in full).

Unlike the other commands, serve takes no config file — configs arrive per request. Auth is mandatory: pass --auth-token/FAUCET_SERVE_AUTH_TOKEN, or --no-auth to explicitly disable it (absent both, startup fails).

Selected flags (faucet serve --help for the full list):

FlagPurpose
--listen <addr>Bind address (default 127.0.0.1:8080; env FAUCET_SERVE_LISTEN).
--auth-token <t> / --no-authBearer token (prefer the env var) or explicit no-auth opt-in.
--auth-config <path>RBAC principals file ({ name, token, role }; roles viewer/operator/admin) — enables role enforcement + the GET /v1/audit log. Mutually exclusive with --auth-token/--no-auth.
--max-concurrent-runs <n> / --max-queued-runs <n>Concurrency + queue caps (429 past the queue).
--history <url>postgres://… / sqlite:… for durable run history (feature-gated; default in-memory).
--default-config <path>Workspace defaults merged under every submitted run.
--cors-origin <origin>Allow-list a browser origin (repeatable; CORS off by default).
--lease-ttl-secs <n>Run-ownership lease TTL (default 30) for multi-instance orphan fencing on a shared persistent backend — set above worst-case stalls. See the serve cookbook.
--clusterEnable cluster mode: instances pull-balance pending runs from the shared --history DB and provide crash-failover. Requires a persistent --history backend (postgres or sqlite). See Running a cluster.
--cluster-poll-secs <n>Claim-loop poll interval in seconds (default 2). Also the maximum lag before a cross-instance cancel is propagated to the executing instance.
--cluster-max-attempts <n>Maximum total attempts (including crash-failovers) before a run is poisoned and marked failed (default 3).
--body-limit-bytes / --shutdown-grace-secs / --retain-terminal-runs-secs / --idempotency-retention-secsTuning knobs.
--no-uiDisable the embedded web console at runtime even when the binary was built with serve-ui.
--triggers <path>Path to a YAML triggers file that defines event-driven watchers (object-arrival / webhook / queue-depth). Requires the triggers Cargo feature. See Triggers reference.

Optional embedded web console (serve-ui)

When built with the serve-ui Cargo feature, faucet serve also serves a browser-based web console at / (and static assets at /assets/*):

cargo install faucet-cli --features serve-ui
FAUCET_SERVE_AUTH_TOKEN=s3cret faucet serve --listen 127.0.0.1:8080
# Open http://127.0.0.1:8080/ in a browser.

The static shell is public; all /v1 data is bearer-gated as usual. The browser is prompted for the token on first load; it is stored in localStorage and sent on every /v1 call. Pass --no-ui to disable the console at runtime without rebuilding.

serve-ui implies serve and is included in the full aggregate. It ships three additional bearer-gated endpoints:

MethodPathDescription
GET/v1/schemasCatalog of compiled sources, sinks, transforms, and state-store kinds.
GET/v1/schemas/{kind}/{name}JSON Schema for one connector or transform (kindsource/sink/transform). 404 for unknown.
POST/v1/doctorValidate + probe a submitted config without running it. 200 (pass) / 422 (fail). Body: { "config": "…", "config_format": "yaml" }.

These endpoints require serve and are available regardless of --no-ui. See the web console guide for the full walkthrough and the HTTP API reference for the complete endpoint/schema reference.

⚠️ serve executes arbitrary client-supplied configs with the server’s identity (secrets, files, network egress). Run single-tenant, authenticated, behind egress controls. See the serve cookbook for the security model and the HTTP API reference for endpoints.

Environment-only mode

faucet run --from-env assembles a pipeline from a FAUCET_* snapshot (FAUCET_SOURCE_*, FAUCET_SINK_*, FAUCET_STATE_*, FAUCET_TRANSFORM_<N>_*), which is handy for containerized deployments where everything comes from the environment. Nested/tagged-enum fields use a *_JSON suffix.

The complete config grammar (matrix, templates, vars, execution) lives in cli/README.md.

Shell completions

faucet supports tab-completion in two ways.

Static script — generate a completion script for your shell and install it the way that shell expects:

faucet completions bash   > /etc/bash_completion.d/faucet     # bash
faucet completions zsh    > ~/.zfunc/_faucet                  # zsh (dir on $fpath)
faucet completions fish   > ~/.config/fish/completions/faucet.fish
faucet completions powershell >> $PROFILE                     # powershell

This completes subcommand names, flags, fixed-choice values, and file paths.

Dynamic (recommended) — let the binary compute completions at completion time, so it stays in sync with the build and becomes config-aware. Add one line to your shell rc:

echo 'source <(COMPLETE=bash faucet)' >> ~/.bashrc     # bash
echo 'source <(COMPLETE=zsh  faucet)' >> ~/.zshrc      # zsh
echo 'COMPLETE=fish faucet | source'  >> ~/.config/fish/config.fish

With the dynamic hook enabled you get runtime-aware candidates:

  • faucet schema source <TAB> / sink / transform — the connectors and transforms compiled into this binary (a slim build lists only its own).
  • faucet run --select <TAB> / --only / --skip — the matrix row ids from the faucet.yaml in the current directory.
  • faucet run --status <TAB> — the readiness ladder (mandatory active available draft archived).
  • faucet run --tag <TAB> — the tags present in the current config.

The config-aware providers are best-effort and read-only: they parse and expand the local config but never resolve secrets, hit the network, or open a connector, and fall back to no suggestions if no config is present.

migrate

faucet migrate [config] upgrades a config written against an older faucet grammar to the current shape, in place. It is idempotent — running it on an already-current config changes nothing.

faucet migrate                 # migrate the discovered faucet.yaml
faucet migrate old.yaml        # migrate a specific file (rewrites it)
faucet migrate old.yaml --stdout   # print the migrated config, don't write
faucet migrate --check         # exit non-zero if a migration is needed (CI)

Rules applied today:

  • Top-level source: / sink:pipeline: — the pre-pipeline block shape is wrapped into a pipeline: map (moving transforms: / state: too).
  • Legacy auth → { type, config } — an auth: / credentials: block of the old { type, <fields…> } shape has its fields folded into a config: sub-map, matching the current adjacently-tagged form.

Each rule is a pure, unit-tested transform. Comments are not preserved (the config is parsed and re-serialized).

doctor --offline

Beyond its connectivity probes, faucet doctor can run a static, offline config lint — no network, no credentials — ideal for CI:

faucet doctor --offline            # lint the discovered config
faucet doctor --offline --json     # machine-readable findings

It flags: a connector auth: { ref } that points at a provider missing from the auth: catalog (error); an auth: provider nothing references (warning); a vars: entry never interpolated (warning); and a file/append sink (jsonl/csv/stdout) with batch_size: 0, which is a no-op (warning). The command exits non-zero on any lint error (warnings don’t fail). Secret and ${env:…} resolution is validated separately at config load (and by faucet validate).

fmt

faucet fmt [config…] rewrites a config into a canonical form — a stable key order (a curated priority for well-known blocks like version/name/pipeline, then alphabetical), so diffs stay meaningful and reviews stay quiet. Running it twice is always a no-op, so --check is a cheap CI gate.

faucet fmt pipeline.yaml            # rewrite in place
faucet fmt pipeline.yaml --stdout   # print, don't write
faucet fmt pipeline.yaml --check    # exit non-zero if not already canonical (CI)

Comments are not preserved (the file is parsed and re-serialized).

explain

faucet explain [config] narrates, in plain English, what a pipeline does — source → transforms → sink, write mode/key, matrix expansion, delivery guarantee. It is built entirely from the resolved config: fully offline, zero I/O, no source touched, and secrets are never printed (only a curated allowlist of structural fields is surfaced, and the output is scrubbed).

faucet explain pipeline.yaml          # prose
faucet explain pipeline.yaml --json   # structured
faucet explain pipeline.yaml --rows   # narrate every row of a large matrix

history

faucet history [config] prints the recent run history recorded in the config’s catalog: store (the same backend faucet serve history and faucet plan --diff use) — status, duration, throughput — without standing up faucet serve. Read-only; requires the catalog build feature.

faucet history                 # table, newest first (default 20)
faucet history --limit 50      # more rows
faucet history --row us        # only runs with an invocation for row `us`
faucet history --json          # machine-readable

Run records are written by faucet serve; point history at the same store.

run --output

faucet run … --output json (or ndjson) emits a machine-readable end-of-run summary instead of the human line, keeping stdout otherwise clean (logs stay on stderr) so faucet run is composable in CI / cron / Slack:

faucet run pipeline.yaml --output json      # one JSON document: per-row + totals
faucet run pipeline.yaml --output ndjson     # one JSON object per matrix row

Each row reports rows_in / rows_out / duration_ms / dlq_count / status / bookmark; the exit code is unchanged (non-zero on failure). Secret material is scrubbed from the output.