faucet_cli/cli.rs
1//! Argument parser shared by `main.rs` and the integration tests.
2
3use crate::commands::completions;
4use clap::{Args, Parser, Subcommand};
5use clap_complete::engine::ArgValueCandidates;
6use std::path::PathBuf;
7
8/// Runtime matrix-row selection flags, shared by `run`/`validate`/`preview`/
9/// `plan` via `#[command(flatten)]`. Implements the selection model of
10/// #370 (identity), #371 (status), #376 (tags), and #377 (include_parents).
11#[derive(Debug, Args, Default, Clone)]
12pub struct SelectionArgs {
13 /// Run only matrix rows whose id exactly matches. Repeatable and/or
14 /// comma-joined (`--select people --select time_off` or `--select a,b`).
15 /// Force-includes by name, bypassing the `status` gate. (#370)
16 #[arg(long = "select", value_delimiter = ',', env = "FAUCET_SELECT",
17 add = ArgValueCandidates::new(completions::matrix_id_candidates))]
18 pub select: Vec<String>,
19
20 /// Like `--select` but glob-matched against row ids (`--only 'timeoff_*'`).
21 /// Also bypasses the `status` gate. Repeatable / comma-joined. (#370)
22 #[arg(long = "only", value_delimiter = ',',
23 add = ArgValueCandidates::new(completions::matrix_id_candidates))]
24 pub only: Vec<String>,
25
26 /// Remove matching rows (exact id or glob) from the run set, applied last.
27 /// A `mandatory` row is removable only by an exact `--skip <id>`. (#370)
28 #[arg(long = "skip", value_delimiter = ',', env = "FAUCET_SKIP",
29 add = ArgValueCandidates::new(completions::matrix_id_candidates))]
30 pub skip: Vec<String>,
31
32 /// Additively include a readiness tier beyond the default
33 /// `{mandatory, active}` set: `available` / `draft` / `archived`.
34 /// Repeatable / comma-joined. (#371)
35 #[arg(long = "status", value_delimiter = ',', env = "FAUCET_STATUS",
36 add = ArgValueCandidates::new(completions::status_candidates))]
37 pub status: Vec<String>,
38
39 /// Narrow the eligible set to rows carrying any listed tag (union).
40 /// Cannot resurrect a non-eligible row — raise `--status` for that.
41 /// Repeatable / comma-joined. (#376)
42 #[arg(long = "tag", value_delimiter = ',', env = "FAUCET_TAGS",
43 add = ArgValueCandidates::new(completions::tag_candidates))]
44 pub tags: Vec<String>,
45
46 /// How a selected row's `parent:` / `depends_on:` ancestors are resolved
47 /// when not independently selected: `off` (default, strict — error on a
48 /// missing ancestor), `eligible`, or `all`. Overrides
49 /// `selection.include_parents` in the config. (#377)
50 #[arg(long = "include-parents", env = "FAUCET_INCLUDE_PARENTS")]
51 pub include_parents: Option<String>,
52}
53
54/// `faucet` — config-driven runner for faucet-stream pipelines.
55#[derive(Debug, Parser)]
56#[command(name = "faucet", version, about, long_about = None)]
57pub struct Cli {
58 /// Override the global log level (also honors `FAUCET_LOG`).
59 #[arg(long, global = true, env = "FAUCET_LOG", default_value = "info")]
60 pub log_level: String,
61
62 #[command(subcommand)]
63 pub command: Command,
64}
65
66/// Top-level subcommands.
67#[derive(Debug, Subcommand)]
68pub enum Command {
69 /// Execute a pipeline config end-to-end.
70 Run(RunArgs),
71 /// Replay a bounded historical window of a pipeline: chunk --from/--to
72 /// into window units, run them with bounded parallelism, and record
73 /// durable, resumable progress. Exits non-zero if any unit fails.
74 Backfill(BackfillArgs),
75 /// Bulk-snapshot a database table, then stream CDC from a position captured
76 /// before the snapshot (a true mirror with `write_mode: upsert`).
77 /// Long-running when `replication.continuous` is true (Ctrl-C / SIGTERM to stop).
78 Replicate(ReplicateArgs),
79 /// Connect to a config's source, enumerate the datasets behind it
80 /// (tables / collections / indices / prefixes), and emit a ready-to-run
81 /// config with one matrix row per dataset.
82 Discover(DiscoverArgs),
83 /// Parse + validate a pipeline config without running it.
84 Validate(ValidateArgs),
85 /// Print the JSON Schema for a specific connector.
86 Schema(SchemaArgs),
87 /// List every compiled-in source, sink, and transform with a one-line
88 /// description (`--available` lists the whole connector registry instead).
89 List(ListArgs),
90 /// Search the connector registry index for connectors by name / keyword.
91 Search(SearchArgs),
92 /// Score each connector's conformance to the faucet SDK contract and print
93 /// its maturity tier (Stable / Experimental / Beta / Draft) + capabilities.
94 Conformance(ConformanceArgs),
95 /// Show how to install or enable a connector from the registry index
96 /// (prints the recipe; never executes anything).
97 Install(InstallArgs),
98 /// Run only the source side and print records to stdout (uses the stdout sink).
99 Preview(PreviewArgs),
100 /// Read-only preview of what a config would do: resolved pipeline, inferred
101 /// output schema, sink schema delta, lineage, and target sinks — zero writes.
102 Plan(PlanArgs),
103 /// Watch a config and re-run a sample offline on every save, printing a
104 /// live diff of the output. Requires the `cli-dev` build feature.
105 #[cfg(feature = "cli-dev")]
106 Dev(DevArgs),
107 /// Scaffold a starter `pipeline.yaml` to disk.
108 Init(InitArgs),
109 /// Scaffold a new artifact — currently a third-party connector crate.
110 New(NewArgs),
111 /// Probe every connector in a config (auth / network / permissions) and
112 /// print a green/red checklist. Exits non-zero if any probe fails.
113 Doctor(DoctorArgs),
114 /// Run fixture-based offline pipeline tests from one or more spec files.
115 /// No real source or sink is touched. Exits non-zero if any case fails.
116 Test(TestArgs),
117 /// Inspect, replay, or discard dead-letter-queue envelopes written by a
118 /// pipeline's `dlq:` sink.
119 Dlq(DlqArgs),
120 /// Validate a config's `contract:` block and print a summary, or export
121 /// it in a machine-readable format (`--export`).
122 #[cfg(feature = "contract")]
123 Contract(ContractArgs),
124 /// Validate a config's `masking:` block and print which rules apply to
125 /// each destination sink.
126 #[cfg(feature = "masking")]
127 Masking(MaskingArgs),
128 /// Run a pipeline on a cron schedule (long-running; Ctrl-C / SIGTERM to stop).
129 #[cfg(feature = "schedule")]
130 Schedule(ScheduleArgs),
131 /// Run a long-running HTTP control plane (submit / poll / cancel pipeline runs).
132 #[cfg(feature = "serve")]
133 Serve(ServeArgs),
134 /// Run an MCP (Model Context Protocol) server over stdio, exposing faucet's
135 /// introspection surfaces as agent tool calls (for Claude Desktop / Code).
136 #[cfg(feature = "mcp")]
137 Mcp(McpArgs),
138 /// Send a synthetic notification through a config's `notifications:` rules
139 /// to validate channel setup end-to-end (no pipeline runs).
140 #[cfg(feature = "notify")]
141 Notify(NotifyArgs),
142 /// Browse the Data Movement Catalog accumulated by a config's `catalog:`
143 /// store — datasets, schema timelines, volume/freshness, lineage.
144 #[cfg(feature = "catalog")]
145 Catalog(CatalogArgs),
146 /// Register a parameterized config once, then trigger runs by id + params.
147 /// The registry is shared with `faucet serve` — point both at the same
148 /// store URL and templates registered here are triggerable over HTTP.
149 #[cfg(feature = "templates")]
150 Template(TemplateArgs),
151 /// Generate a shell tab-completion script (bash / zsh / fish / powershell /
152 /// elvish). For registry- and config-aware *dynamic* completion, enable the
153 /// `COMPLETE` hook instead, e.g. `source <(COMPLETE=zsh faucet)`.
154 Completions(CompletionsArgs),
155 /// Upgrade a config written against an older `faucet` grammar to the current
156 /// shape (e.g. pre-`pipeline:` top-level source/sink, legacy inline auth).
157 /// Idempotent; rewrites in place unless `--check` / `--stdout`.
158 Migrate(MigrateArgs),
159 /// Canonicalize a config: stable key order, normalized style. Idempotent;
160 /// rewrites in place unless `--check` / `--stdout`. Comments are not
161 /// preserved (the config is parsed and re-serialized).
162 Fmt(FmtArgs),
163 /// Explain, in plain English, what a pipeline config does — source →
164 /// transforms → sink, matrix expansion, replication, delivery guarantee,
165 /// and state store. Read-only and fully offline (no source is touched).
166 Explain(ExplainArgs),
167 /// Show recent run history recorded in a config's `catalog:` store —
168 /// status, duration, throughput, and bookmark. Read-only; requires the
169 /// `catalog` build feature.
170 #[cfg(feature = "catalog")]
171 History(HistoryArgs),
172}
173
174/// `faucet migrate` arguments.
175#[derive(Debug, Args)]
176pub struct MigrateArgs {
177 /// Config file to migrate. Auto-discovered (`faucet.yaml` → `.yml` →
178 /// `.json`) when omitted.
179 #[arg(value_hint = clap::ValueHint::FilePath)]
180 pub config: Option<PathBuf>,
181 /// Report whether a migration is needed without writing (exits non-zero if
182 /// the config is not current). For CI / pre-upgrade checks.
183 #[arg(long)]
184 pub check: bool,
185 /// Write the migrated config to stdout instead of rewriting the file.
186 #[arg(long, conflicts_with = "check")]
187 pub stdout: bool,
188}
189
190/// `faucet fmt` arguments.
191#[derive(Debug, Args)]
192pub struct FmtArgs {
193 /// Config file(s) to format. Auto-discovered (`faucet.yaml` → `.yml` →
194 /// `.json`) when none are given.
195 #[arg(value_hint = clap::ValueHint::FilePath)]
196 pub configs: Vec<PathBuf>,
197 /// Report whether each file is already canonical without writing (exits
198 /// non-zero and prints a unified diff for any file that is not). For CI.
199 #[arg(long)]
200 pub check: bool,
201 /// Write the formatted result to stdout instead of rewriting the file(s).
202 #[arg(long, conflicts_with = "check")]
203 pub stdout: bool,
204}
205
206/// `faucet explain` arguments.
207#[derive(Debug, Args)]
208pub struct ExplainArgs {
209 /// Path to a `.yaml`/`.yml`/`.json` config (auto-discovered if omitted).
210 pub config: Option<PathBuf>,
211 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
212 /// Defaults to `.env` in cwd if present.
213 #[arg(long, conflicts_with = "no_env_file")]
214 pub env_file: Option<PathBuf>,
215 /// Skip auto-loading `.env` from cwd.
216 #[arg(long)]
217 pub no_env_file: bool,
218 /// Select a named overlay from the config's `profiles:` block.
219 #[arg(long, env = "FAUCET_PROFILE")]
220 pub profile: Option<String>,
221 /// Emit the narration as structured JSON instead of prose.
222 #[arg(long)]
223 pub json: bool,
224 /// Narrate every matrix row instead of summarizing a large matrix.
225 #[arg(long)]
226 pub rows: bool,
227}
228
229/// `faucet history` arguments.
230#[cfg(feature = "catalog")]
231#[derive(Debug, Args)]
232pub struct HistoryArgs {
233 /// Path to a config carrying a `catalog:` block (auto-discovered if omitted).
234 pub config: Option<PathBuf>,
235 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
236 #[arg(long, conflicts_with = "no_env_file")]
237 pub env_file: Option<PathBuf>,
238 /// Skip auto-loading `.env` from cwd.
239 #[arg(long)]
240 pub no_env_file: bool,
241 /// Select a named overlay from the config's `profiles:` block.
242 #[arg(long, env = "FAUCET_PROFILE")]
243 pub profile: Option<String>,
244 /// Maximum number of runs to show, newest first.
245 #[arg(long, default_value_t = 20)]
246 pub limit: usize,
247 /// Show only runs that contain an invocation for this matrix row id.
248 #[arg(long)]
249 pub row: Option<String>,
250 /// Emit the history as JSON instead of a table.
251 #[arg(long)]
252 pub json: bool,
253}
254
255/// `faucet completions` arguments.
256#[derive(Debug, Args)]
257pub struct CompletionsArgs {
258 /// Target shell.
259 pub shell: clap_complete::aot::Shell,
260}
261
262/// `faucet catalog` arguments.
263#[cfg(feature = "catalog")]
264#[derive(Debug, Parser)]
265pub struct CatalogArgs {
266 #[command(subcommand)]
267 pub command: CatalogCommand,
268}
269
270/// `faucet catalog` subcommands.
271#[cfg(feature = "catalog")]
272#[derive(Debug, Subcommand)]
273pub enum CatalogCommand {
274 /// List every catalogued dataset (newest activity first).
275 Datasets(CatalogDatasetsArgs),
276 /// Show one dataset's detail: schema timeline, volume points, edges.
277 Show(CatalogShowArgs),
278 /// Print the dataset lineage graph (optionally rooted at a dataset).
279 Lineage(CatalogLineageArgs),
280}
281
282/// Shared config-loading flags for the `faucet catalog` subcommands.
283#[cfg(feature = "catalog")]
284#[derive(Debug, Parser)]
285pub struct CatalogConfigArgs {
286 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
287 /// `catalog:` block naming the store. If omitted, auto-discover
288 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
289 #[arg(long)]
290 pub config: Option<PathBuf>,
291 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
292 /// Defaults to `.env` in cwd if present.
293 #[arg(long, conflicts_with = "no_env_file")]
294 pub env_file: Option<PathBuf>,
295 /// Skip auto-loading `.env` from cwd.
296 #[arg(long)]
297 pub no_env_file: bool,
298 /// Select a named overlay from the config's `profiles:` block.
299 /// Overrides the `FAUCET_PROFILE` env var.
300 #[arg(long, env = "FAUCET_PROFILE")]
301 pub profile: Option<String>,
302 /// Emit machine-readable JSON instead of the human summary.
303 #[arg(long)]
304 pub json: bool,
305}
306
307/// `faucet catalog datasets` arguments.
308#[cfg(feature = "catalog")]
309#[derive(Debug, Parser)]
310pub struct CatalogDatasetsArgs {
311 #[command(flatten)]
312 pub common: CatalogConfigArgs,
313 /// Only datasets of this connector kind (e.g. `postgres`, `csv`).
314 #[arg(long)]
315 pub kind: Option<String>,
316 /// Case-insensitive substring match on the dataset URI.
317 #[arg(long)]
318 pub q: Option<String>,
319 /// Max datasets to list.
320 #[arg(long, default_value_t = 100)]
321 pub limit: usize,
322}
323
324/// `faucet catalog show <id>` arguments.
325#[cfg(feature = "catalog")]
326#[derive(Debug, Parser)]
327pub struct CatalogShowArgs {
328 /// Dataset id (from `faucet catalog datasets`), or a unique prefix of one.
329 pub id: String,
330 #[command(flatten)]
331 pub common: CatalogConfigArgs,
332}
333
334/// `faucet catalog lineage` arguments.
335#[cfg(feature = "catalog")]
336#[derive(Debug, Parser)]
337pub struct CatalogLineageArgs {
338 #[command(flatten)]
339 pub common: CatalogConfigArgs,
340 /// Dataset id to root the graph at (whole graph when omitted).
341 #[arg(long)]
342 pub root: Option<String>,
343 /// BFS hop bound around --root.
344 #[arg(long, default_value_t = 5)]
345 pub depth: u32,
346}
347
348/// `faucet template` arguments (#444).
349#[cfg(feature = "templates")]
350#[derive(Debug, Parser)]
351pub struct TemplateArgs {
352 #[command(subcommand)]
353 pub command: TemplateCommand,
354}
355
356/// `faucet template` subcommands.
357#[cfg(feature = "templates")]
358#[derive(Debug, Subcommand)]
359pub enum TemplateCommand {
360 /// Validate a config and register it as a new template version.
361 Register(TemplateRegisterArgs),
362 /// List registered templates (newest version of each, plus its release state).
363 List(TemplateListArgs),
364 /// Show one template: its params, config body, and versions.
365 Show(TemplateShowArgs),
366 /// Make a version live — what unpinned runs will use. The one action that
367 /// moves existing callers; registering a build never does.
368 Launch(TemplateLaunchArgs),
369 /// Re-launch the previously launched version.
370 Rollback(TemplateRollbackArgs),
371 /// Retire a template (or revive one with `--undo`).
372 Deprecate(TemplateDeprecateArgs),
373 /// Point a named environment channel (`prod`, `staging`, …) at a version.
374 Promote(TemplatePromoteArgs),
375 /// Delete one version, or every version, of a template.
376 Delete(TemplateDeleteArgs),
377 /// Materialize a template with the given params and run it locally.
378 Run(TemplateRunArgs),
379}
380
381/// Where the template registry lives — shared by every `faucet template`
382/// subcommand.
383#[cfg(feature = "templates")]
384#[derive(Debug, Parser)]
385pub struct TemplateStoreArgs {
386 /// Registry store URL: `sqlite:<path>`, a `postgres://…` URL, or `memory`
387 /// (process-lifetime only — useful for a smoke test). Point
388 /// `faucet serve --history` at the same URL to trigger these templates over
389 /// HTTP. SQL backends need the matching `serve-history-sqlite` /
390 /// `serve-history-postgres` build feature.
391 #[arg(long, env = "FAUCET_TEMPLATE_STORE")]
392 pub store: String,
393 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
394 /// Defaults to `.env` in cwd if present.
395 #[arg(long, conflicts_with = "no_env_file")]
396 pub env_file: Option<PathBuf>,
397 /// Skip auto-loading `.env` from cwd.
398 #[arg(long)]
399 pub no_env_file: bool,
400 /// Emit machine-readable JSON instead of the human summary.
401 #[arg(long)]
402 pub json: bool,
403}
404
405/// `faucet template register <config>` arguments.
406#[cfg(feature = "templates")]
407#[derive(Debug, Parser)]
408pub struct TemplateRegisterArgs {
409 /// Path to the `.yaml`, `.yml`, or `.json` config to register. Stored
410 /// verbatim, so `${env:…}` / `${vault:…}` stay unresolved and are resolved
411 /// when a run is triggered.
412 #[arg(value_hint = clap::ValueHint::FilePath)]
413 pub config: PathBuf,
414 /// Registry id. Derived from the config's `name:` when omitted.
415 #[arg(long)]
416 pub id: Option<String>,
417 /// Free-text description shown by `list` / `show`.
418 #[arg(long)]
419 pub description: Option<String>,
420 /// Point a named channel at the newly registered version, e.g.
421 /// `--tag dev --tag test`. The version number itself always auto-increments;
422 /// channels come from a fixed set (`dev`, `test`, `staging`, `pre-prod`,
423 /// `canary`, `stable`, `prod`, `previous`). `latest` is derived and always
424 /// names the newest version, so it cannot be assigned.
425 #[arg(long = "tag", value_name = "CHANNEL")]
426 pub tag: Vec<String>,
427 /// Launch the new version immediately, making it the one unpinned runs use.
428 /// Without this the version is registered but inert — a new build never moves
429 /// existing callers until you launch it.
430 #[arg(long)]
431 pub launch: bool,
432 #[command(flatten)]
433 pub common: TemplateStoreArgs,
434}
435
436/// `faucet template promote <id>` arguments.
437#[cfg(feature = "templates")]
438#[derive(Debug, Parser)]
439pub struct TemplatePromoteArgs {
440 /// Template id.
441 pub id: String,
442 /// Channel to move: `dev`, `test`, `staging`, `pre-prod`, `canary`, or
443 /// `prod`. The derived channels (`stable`, `previous`, `newest`) cannot be
444 /// promoted — `stable` moves with `faucet template launch`.
445 #[arg(long = "tag", value_name = "CHANNEL")]
446 pub tag: String,
447 /// What to point it at: a version number, or another channel whose current
448 /// target should be copied (`--tag prod --version pre-prod`). Defaults to
449 /// `stable`, the currently launched version.
450 #[arg(long, default_value = "stable")]
451 pub version: String,
452 #[command(flatten)]
453 pub common: TemplateStoreArgs,
454}
455
456/// `faucet template launch <id>` arguments.
457#[cfg(feature = "templates")]
458#[derive(Debug, Parser)]
459pub struct TemplateLaunchArgs {
460 /// Template id.
461 pub id: String,
462 /// Which version to make live: a number, or a channel whose current target to
463 /// copy (`--version pre-prod` launches whatever passed pre-prod). Defaults to
464 /// `newest` — launching what you just registered is the common case.
465 #[arg(long, default_value = "newest")]
466 pub version: String,
467 #[command(flatten)]
468 pub common: TemplateStoreArgs,
469}
470
471/// `faucet template rollback <id>` arguments.
472#[cfg(feature = "templates")]
473#[derive(Debug, Parser)]
474pub struct TemplateRollbackArgs {
475 /// Template id.
476 pub id: String,
477 #[command(flatten)]
478 pub common: TemplateStoreArgs,
479}
480
481/// `faucet template deprecate <id>` arguments.
482#[cfg(feature = "templates")]
483#[derive(Debug, Parser)]
484pub struct TemplateDeprecateArgs {
485 /// Template id.
486 pub id: String,
487 /// Why it is being retired — shown to anyone who triggers it.
488 #[arg(long)]
489 pub reason: Option<String>,
490 /// Revive a deprecated template instead of retiring it.
491 #[arg(long)]
492 pub undo: bool,
493 #[command(flatten)]
494 pub common: TemplateStoreArgs,
495}
496
497/// `faucet template list` arguments.
498#[cfg(feature = "templates")]
499#[derive(Debug, Parser)]
500pub struct TemplateListArgs {
501 #[command(flatten)]
502 pub common: TemplateStoreArgs,
503}
504
505/// `faucet template show <id>` arguments.
506#[cfg(feature = "templates")]
507#[derive(Debug, Parser)]
508pub struct TemplateShowArgs {
509 /// Template id.
510 pub id: String,
511 /// Version to show: a number, or a named channel (`stable` — the default,
512 /// i.e. the launched version — `newest`, `previous`, `prod`, `dev`, …).
513 #[arg(long, default_value = "stable")]
514 pub version: String,
515 #[command(flatten)]
516 pub common: TemplateStoreArgs,
517}
518
519/// `faucet template delete <id>` arguments.
520#[cfg(feature = "templates")]
521#[derive(Debug, Parser)]
522pub struct TemplateDeleteArgs {
523 /// Template id.
524 pub id: String,
525 /// Delete only this version — a number, or a named channel (`latest`,
526 /// `prod`, …) resolved to the version it points at. Omitted = delete every
527 /// version of the template.
528 #[arg(long)]
529 pub version: Option<String>,
530 #[command(flatten)]
531 pub common: TemplateStoreArgs,
532}
533
534/// `faucet template run <id>` arguments.
535#[cfg(feature = "templates")]
536#[derive(Debug, Parser)]
537pub struct TemplateRunArgs {
538 /// Template id.
539 pub id: String,
540 /// Version to run: a number, or a named channel. Defaults to `stable` — the
541 /// launched version — so an unpinned run never picks up a build that has not
542 /// been launched. Use `newest` to run the most recent build regardless.
543 #[arg(long, default_value = "stable")]
544 pub version: String,
545 /// Supply a declared param: `--param tenant_id=acme`. Repeatable.
546 #[arg(long = "param", value_name = "NAME=VALUE")]
547 pub param: Vec<String>,
548 /// Override an environment variable for this materialization only:
549 /// `--param-env REGION=eu`, or bare `--param-env TOKEN` to take it from the
550 /// caller's environment. Repeatable.
551 #[arg(long = "param-env", value_name = "NAME[=VALUE]")]
552 pub param_env: Vec<String>,
553 /// Materialize and validate without running (prints the resolved config).
554 #[arg(long)]
555 pub dry_run: bool,
556 /// Stop after writing this many records to the sink.
557 #[arg(long)]
558 pub limit: Option<usize>,
559 #[command(flatten)]
560 pub common: TemplateStoreArgs,
561}
562
563/// `faucet notify test` arguments.
564#[cfg(feature = "notify")]
565#[derive(Debug, Parser)]
566pub struct NotifyArgs {
567 #[command(subcommand)]
568 pub command: NotifyCommand,
569}
570
571/// `faucet notify` subcommands.
572#[cfg(feature = "notify")]
573#[derive(Debug, Subcommand)]
574pub enum NotifyCommand {
575 /// Fire one synthetic event at every matching rule in the config.
576 Test(NotifyTestArgs),
577}
578
579/// `faucet notify test <config>` arguments.
580#[cfg(feature = "notify")]
581#[derive(Debug, Parser)]
582pub struct NotifyTestArgs {
583 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
584 /// `notifications:` block. If omitted, auto-discover in cwd.
585 pub config: Option<PathBuf>,
586 /// Which event to synthesize (defaults to `run_failure`).
587 #[arg(long, default_value = "run_failure")]
588 pub event: String,
589 /// Path to a `.env` file for `${env:VAR}` interpolation.
590 #[arg(long, conflicts_with = "no_env_file")]
591 pub env_file: Option<PathBuf>,
592 /// Disable `.env` auto-discovery.
593 #[arg(long)]
594 pub no_env_file: bool,
595}
596
597/// `faucet test` arguments.
598#[derive(Debug, Parser)]
599pub struct TestArgs {
600 /// One or more test-spec files (`.yaml`, `.yml`, or `.json`), e.g.
601 /// `faucet test tests/*.yaml`.
602 #[arg(required = true)]
603 pub specs: Vec<PathBuf>,
604 /// Run only cases whose name contains this substring.
605 #[arg(long)]
606 pub filter: Option<String>,
607 /// Emit a machine-readable JSON report instead of the human checklist.
608 #[arg(long)]
609 pub json: bool,
610 /// Default `${now.*}` clock for cases without their own `clock:` field
611 /// (RFC3339 like `2026-01-31T00:00:00Z`, or a date `2026-01-31`).
612 /// Defaults to process start (UTC).
613 #[arg(long)]
614 pub clock: Option<String>,
615 /// Path to a `.env` file to load for `${env:VAR}` interpolation in
616 /// referenced pipeline configs. Defaults to `.env` in cwd if present.
617 #[arg(long, conflicts_with = "no_env_file")]
618 pub env_file: Option<PathBuf>,
619 /// Skip auto-loading `.env` from cwd.
620 #[arg(long)]
621 pub no_env_file: bool,
622 /// Select a named overlay from each referenced config's `profiles:` block.
623 /// Overrides the `FAUCET_PROFILE` env var.
624 #[arg(long, env = "FAUCET_PROFILE")]
625 pub profile: Option<String>,
626 /// Resolve `${vault:…}` / `${aws-sm:…}` / … secret directives in
627 /// referenced configs (requires network + credentials). By default tests
628 /// load configs offline and leave secret directives unresolved — safe
629 /// because the real source/sink configs holding them are never used.
630 #[arg(long)]
631 pub resolve_secrets: bool,
632}
633
634/// `faucet dlq` arguments.
635#[derive(Debug, Parser)]
636pub struct DlqArgs {
637 #[command(subcommand)]
638 pub command: DlqCommand,
639}
640
641/// `faucet dlq` subcommands.
642#[derive(Debug, Subcommand)]
643pub enum DlqCommand {
644 /// Read a DLQ location back and print a per-reason / per-error-kind
645 /// breakdown plus a sample of quarantined records.
646 Inspect(DlqInspectArgs),
647 /// Re-feed quarantined records through a pipeline config (transforms →
648 /// quality → contract → sink). Rows that fail again land in a *fresh* DLQ.
649 Replay(DlqReplayArgs),
650 /// Remove processed envelopes from a DLQ location (archive by default,
651 /// or `--delete`), filtered by reason and/or age.
652 Discard(DlqDiscardArgs),
653}
654
655/// `faucet dlq inspect <location>` arguments.
656#[derive(Debug, Parser)]
657pub struct DlqInspectArgs {
658 /// DLQ location: a `.jsonl` file, a directory of `*.jsonl` files, or a glob.
659 pub location: String,
660 /// Only include envelopes with this DLQ reason (`partial` / `dlq_all` /
661 /// `quality` / `schema_drift` / `contract`).
662 #[arg(long)]
663 pub reason: Option<String>,
664 /// Number of sample records to show. Default: 5.
665 #[arg(long, default_value_t = 5)]
666 pub limit: usize,
667 /// Key for a DLQ sealed at rest by the jsonl sink's `encryption` block.
668 /// Repeat the flag to also try older (rotated) keys. Requires a build
669 /// with the `encryption` feature.
670 #[arg(long = "encryption-key")]
671 pub encryption_key: Vec<String>,
672 /// Emit a machine-readable JSON summary instead of the human report.
673 #[arg(long)]
674 pub json: bool,
675}
676
677/// `faucet dlq replay <config> --from <location>` arguments.
678#[derive(Debug, Parser)]
679pub struct DlqReplayArgs {
680 /// Path to the pipeline config whose sink / transforms / quality / contract
681 /// the replayed records flow through. If omitted, auto-discover in cwd.
682 pub config: Option<PathBuf>,
683 /// DLQ location to replay from: a `.jsonl` file, a directory, or a glob.
684 #[arg(long)]
685 pub from: String,
686 /// Only replay envelopes with this DLQ reason.
687 #[arg(long)]
688 pub reason: Option<String>,
689 /// Where replayed rows that fail *again* are quarantined. Defaults to a
690 /// `replay-failed.jsonl` sibling of the source (never the source itself).
691 #[arg(long)]
692 pub failed_dlq: Option<String>,
693 /// Which root row of the config to replay through. Defaults to the first root.
694 #[arg(long)]
695 pub row: Option<String>,
696 /// Report what would be replayed without writing to the sink.
697 #[arg(long)]
698 pub dry_run: bool,
699 /// Key for a DLQ sealed at rest by the jsonl sink's `encryption` block.
700 /// Repeat the flag to also try older (rotated) keys. Requires a build
701 /// with the `encryption` feature.
702 #[arg(long = "encryption-key")]
703 pub encryption_key: Vec<String>,
704 /// (Replay picks up the config's own dlq `encryption` block automatically
705 /// when no key is passed.)
706 /// Emit a machine-readable JSON result instead of the human summary.
707 #[arg(long)]
708 pub json: bool,
709 /// Path to a `.env` file for `${env:VAR}` interpolation in the config.
710 #[arg(long, conflicts_with = "no_env_file")]
711 pub env_file: Option<PathBuf>,
712 /// Skip auto-loading `.env` from cwd.
713 #[arg(long)]
714 pub no_env_file: bool,
715 /// Select a named overlay from the config's `profiles:` block.
716 #[arg(long, env = "FAUCET_PROFILE")]
717 pub profile: Option<String>,
718}
719
720/// `faucet dlq discard <location>` arguments.
721#[derive(Debug, Parser)]
722pub struct DlqDiscardArgs {
723 /// DLQ location: a `.jsonl` file, a directory of `*.jsonl` files, or a glob.
724 pub location: String,
725 /// Only discard envelopes with this DLQ reason.
726 #[arg(long)]
727 pub reason: Option<String>,
728 /// Only discard envelopes older than this: an RFC3339 timestamp
729 /// (`2026-06-01T00:00:00Z`) or a relative age (`7d`, `24h`, `30m`).
730 #[arg(long)]
731 pub before: Option<String>,
732 /// Permanently delete matching envelopes instead of archiving them to a
733 /// `<file>.archived.jsonl` sibling.
734 #[arg(long)]
735 pub delete: bool,
736 /// Key for a DLQ sealed at rest by the jsonl sink's `encryption` block.
737 /// Repeat the flag to also try older (rotated) keys. Requires a build
738 /// with the `encryption` feature.
739 #[arg(long = "encryption-key")]
740 pub encryption_key: Vec<String>,
741 /// Emit a machine-readable JSON result instead of the human summary.
742 #[arg(long)]
743 pub json: bool,
744}
745
746/// `faucet doctor` arguments.
747#[derive(Debug, Parser)]
748pub struct DoctorArgs {
749 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config. If omitted,
750 /// auto-discover `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
751 pub config: Option<PathBuf>,
752 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
753 /// Defaults to `.env` in cwd if present.
754 #[arg(long, conflicts_with = "no_env_file")]
755 pub env_file: Option<PathBuf>,
756 /// Skip auto-loading `.env` from cwd.
757 #[arg(long)]
758 pub no_env_file: bool,
759 /// Per-probe timeout in seconds.
760 #[arg(long, default_value_t = 10)]
761 pub timeout_secs: u64,
762 /// Emit machine-readable JSON instead of the human checklist.
763 #[arg(long)]
764 pub json: bool,
765 /// Run only the offline static config lints (no network probes): dangling /
766 /// unreferenced `auth:` providers, unused `vars:`, and no-op sink
767 /// `batch_size: 0`. Fast and credential-free — ideal for CI. Exits non-zero
768 /// on any lint *error* (warnings don't fail).
769 #[arg(long)]
770 pub offline: bool,
771 /// Select a named overlay from the config's `profiles:` block and deep-merge
772 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
773 #[arg(long, env = "FAUCET_PROFILE")]
774 pub profile: Option<String>,
775}
776
777/// `faucet contract` arguments.
778#[cfg(feature = "contract")]
779#[derive(Debug, Parser)]
780pub struct ContractArgs {
781 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
782 /// `pipeline.contract:` block. If omitted, auto-discover
783 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
784 pub config: Option<PathBuf>,
785 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
786 /// Defaults to `.env` in cwd if present.
787 #[arg(long, conflicts_with = "no_env_file")]
788 pub env_file: Option<PathBuf>,
789 /// Skip auto-loading `.env` from cwd.
790 #[arg(long)]
791 pub no_env_file: bool,
792 /// Select a named overlay from the config's `profiles:` block and deep-merge
793 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
794 #[arg(long, env = "FAUCET_PROFILE")]
795 pub profile: Option<String>,
796 /// Export the contract in a machine-readable format instead of the
797 /// human summary: the canonical contract JSON, a standalone JSON Schema,
798 /// or an OpenLineage schema facet.
799 #[arg(long, value_enum)]
800 pub export: Option<ContractExportFormat>,
801}
802
803/// Arguments for `faucet masking`.
804#[cfg(feature = "masking")]
805#[derive(Debug, Parser)]
806pub struct MaskingArgs {
807 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
808 /// `pipeline.masking:` block. If omitted, auto-discover
809 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
810 pub config: Option<PathBuf>,
811 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
812 /// Defaults to `.env` in cwd if present.
813 #[arg(long, conflicts_with = "no_env_file")]
814 pub env_file: Option<PathBuf>,
815 /// Skip auto-loading `.env` from cwd.
816 #[arg(long)]
817 pub no_env_file: bool,
818 /// Select a named overlay from the config's `profiles:` block and deep-merge
819 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
820 #[arg(long, env = "FAUCET_PROFILE")]
821 pub profile: Option<String>,
822}
823
824/// Export format for `faucet contract --export`.
825#[cfg(feature = "contract")]
826#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
827pub enum ContractExportFormat {
828 /// The canonical contract document as JSON.
829 Contract,
830 /// A standalone JSON Schema (draft 2020-12) for the promised records.
831 JsonSchema,
832 /// An OpenLineage `SchemaDatasetFacet` JSON document.
833 Openlineage,
834}
835
836/// `faucet schedule` arguments.
837#[cfg(feature = "schedule")]
838#[derive(Debug, Parser)]
839pub struct ScheduleArgs {
840 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a `schedule:`
841 /// block. If omitted, auto-discover `faucet.yaml` / `.yml` / `.json` in cwd.
842 pub config: Option<PathBuf>,
843 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
844 /// Defaults to `.env` in cwd if present.
845 #[arg(long, conflicts_with = "no_env_file")]
846 pub env_file: Option<PathBuf>,
847 /// Skip auto-loading `.env` from cwd.
848 #[arg(long)]
849 pub no_env_file: bool,
850 /// Run exactly one pipeline run immediately, then exit (ignores cron timing).
851 /// Useful for platform-driven invocation (k8s CronJob / systemd OnCalendar).
852 #[arg(long)]
853 pub once: bool,
854 /// Select a named overlay from the config's `profiles:` block and deep-merge
855 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
856 #[arg(long, env = "FAUCET_PROFILE")]
857 pub profile: Option<String>,
858}
859
860/// `faucet serve` arguments.
861#[cfg(feature = "serve")]
862#[derive(Debug, Clone, Parser)]
863pub struct ServeArgs {
864 /// Bind address. Defaults to loopback; set 0.0.0.0:PORT to expose externally.
865 #[arg(long, env = "FAUCET_SERVE_LISTEN", default_value = "127.0.0.1:8080")]
866 pub listen: String,
867 /// Bearer token required on /v1/* requests. Prefer the env var (avoids `ps` leakage).
868 #[arg(long, env = "FAUCET_SERVE_AUTH_TOKEN", conflicts_with = "no_auth")]
869 pub auth_token: Option<String>,
870 /// Explicitly disable authentication. Required if no token is set, so an
871 /// unauthenticated server is never accidental.
872 #[arg(long)]
873 pub no_auth: bool,
874 /// Path to an RBAC auth config (YAML/JSON) defining principals — each a
875 /// `{ name, token, role }` where role is `viewer` / `operator` / `admin`.
876 /// Enables role-based access control + an audit log. Mutually exclusive with
877 /// `--auth-token` / `--no-auth`.
878 #[arg(long, conflicts_with_all = ["auth_token", "no_auth"])]
879 pub auth_config: Option<std::path::PathBuf>,
880 /// Max pipeline runs executing at once. Default: min(16, cpu count).
881 #[arg(long)]
882 pub max_concurrent_runs: Option<usize>,
883 /// Max queued (not-yet-running) runs before POST /v1/runs returns 429.
884 /// Default: 8 × max-concurrent-runs.
885 #[arg(long)]
886 pub max_queued_runs: Option<usize>,
887 /// Workspace-default config merged under every submitted run.
888 #[arg(long)]
889 pub default_config: Option<std::path::PathBuf>,
890 /// Run-history backend URL: omitted = in-memory; postgres://… ; sqlite:… .
891 #[arg(long)]
892 pub history: Option<String>,
893 /// CORS allow-list origin (repeatable). Omitted = CORS disabled.
894 #[arg(long)]
895 pub cors_origin: Vec<String>,
896 /// Max POST /v1/runs body size in bytes (413 on exceed).
897 #[arg(long, default_value_t = 1_048_576)]
898 pub body_limit_bytes: usize,
899 /// SIGTERM/SIGINT drain window in seconds.
900 #[arg(long, default_value_t = 60)]
901 pub shutdown_grace_secs: u64,
902 /// Retain terminal run records this long (seconds).
903 #[arg(long, default_value_t = 604_800)]
904 pub retain_terminal_runs_secs: u64,
905 /// Idempotency-key replay window (seconds).
906 #[arg(long, default_value_t = 86_400)]
907 pub idempotency_retention_secs: u64,
908 /// Run-ownership lease TTL in seconds (multi-instance orphan fencing). A run
909 /// is owned by the instance executing it and its lease is heartbeated at
910 /// ~⅓ of this interval; only a run whose lease has expired (owner presumed
911 /// dead) is recovered as failed. Make this comfortably larger than expected
912 /// GC/IO stalls so a healthy-but-slow instance is never falsely reclaimed.
913 /// Only relevant with a persistent (postgres/sqlite) history backend.
914 #[arg(long, default_value_t = 30)]
915 pub lease_ttl_secs: u64,
916 /// Per-probe timeout for `doctor_first` preflight (seconds).
917 #[arg(long, default_value_t = 10)]
918 pub probe_timeout_secs: u64,
919 /// Path to a `.env` file loaded for the server's own startup interpolation.
920 #[arg(long, conflicts_with = "no_env_file")]
921 pub env_file: Option<std::path::PathBuf>,
922 /// Skip auto-loading `.env` from cwd at startup.
923 #[arg(long)]
924 pub no_env_file: bool,
925 /// Disable serving the embedded web console (only meaningful in a build that
926 /// includes the `serve-ui` feature; the API is unaffected).
927 #[arg(long)]
928 pub no_ui: bool,
929 /// Enable clustered execution: run a claim loop that pulls Pending runs from
930 /// the shared history DB so N instances pull-balance and fail over. Requires
931 /// a postgres/sqlite --history backend.
932 #[arg(long)]
933 pub cluster: bool,
934 /// Claim-loop poll interval (seconds) in cluster mode. Also the
935 /// cross-instance cancel-propagation lag. Must be > 0.
936 #[arg(long, default_value_t = 2)]
937 pub cluster_poll_secs: u64,
938 /// Max failover re-runs of an orphaned run before it is marked Failed
939 /// (poison). Must be > 0.
940 #[arg(long, default_value_t = 3)]
941 pub cluster_max_attempts: u32,
942 /// Path to a triggers file (YAML/JSON) defining event-driven pipeline
943 /// triggers (object-arrival / webhook / queue-depth). Requires a build with
944 /// the `triggers` feature. See `faucet schema triggers`.
945 #[arg(long)]
946 pub triggers: Option<std::path::PathBuf>,
947 /// Restrict per-run completion callbacks (`callback` on a submit) to these
948 /// hosts. Repeatable. When unset, any host is permitted **except**
949 /// link-local / cloud-metadata addresses, which are always refused unless
950 /// named here. See the HTTP API reference for the egress posture.
951 #[arg(long = "callback-allow-host")]
952 pub callback_allow_host: Vec<String>,
953 /// Mount the MCP (Model Context Protocol) endpoint at `/mcp`, exposing
954 /// faucet as agent tool calls. Effective only in a build with the `mcp`
955 /// feature; the endpoint inherits serve's bearer-auth + RBAC + audit.
956 #[arg(long)]
957 pub mcp: bool,
958 /// Allow the MCP endpoint's *mutating* tools (`run_pipeline`). Off by
959 /// default: only read-only tools are exposed. A caller still needs the
960 /// `RunWrite` RBAC scope. Only meaningful together with `--mcp`.
961 #[arg(long)]
962 pub mcp_allow_mutations: bool,
963}
964
965/// `faucet mcp` arguments — run an MCP server over stdio (#420).
966#[cfg(feature = "mcp")]
967#[derive(Debug, Clone, Parser)]
968pub struct McpArgs {
969 /// Allow mutating tools (`run_pipeline`). Off by default — only read-only
970 /// tools (list / schema / scaffold / validate / preview) are exposed.
971 /// stdio is local-trust: there is no bearer/RBAC layer, so enable this only
972 /// for a trusted local agent.
973 #[arg(long)]
974 pub allow_mutations: bool,
975 /// Optional `.env` file to load before starting (for `${env:…}` in configs
976 /// passed to `validate`/`preview`/`run_pipeline`).
977 #[arg(long, conflicts_with = "no_env_file")]
978 pub env_file: Option<std::path::PathBuf>,
979 /// Skip auto-loading `.env` from cwd at startup.
980 #[arg(long)]
981 pub no_env_file: bool,
982 /// Pipeline-template registry to expose (#444): `sqlite:<path>`, a
983 /// `postgres://…` URL, or `memory`. Enables the `list_templates` /
984 /// `get_template` tools (plus `register_template` / `run_template` with
985 /// `--allow-mutations`). Omitted = no template tools are advertised.
986 #[cfg(feature = "templates")]
987 #[arg(long, env = "FAUCET_TEMPLATE_STORE")]
988 pub template_store: Option<String>,
989}
990
991/// `faucet run` arguments.
992///
993/// `Default` is derived so callers that execute an already-loaded config through
994/// `commands::run::execute` (notably `faucet template run`) can build a
995/// plain-run argument set without restating every flag.
996#[derive(Debug, Parser, Default)]
997pub struct RunArgs {
998 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config.
999 /// If omitted (and `--from-env` is not set), auto-discover
1000 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in the current directory.
1001 /// Mutually exclusive with `--from-env`.
1002 #[arg(conflicts_with = "from_env")]
1003 pub config: Option<PathBuf>,
1004 /// Build the pipeline entirely from `FAUCET_*` environment variables —
1005 /// no YAML required. See `cli/README.md` for the variable schema.
1006 #[arg(long)]
1007 pub from_env: bool,
1008 /// Path to a `.env` file to load before reading variables. Works in both
1009 /// YAML mode (for `${env:VAR}` interpolation) and `--from-env` mode.
1010 /// When omitted, `.env` in the current directory is auto-loaded if present.
1011 /// Existing process-env values always win over file-supplied ones.
1012 #[arg(long, conflicts_with = "no_env_file")]
1013 pub env_file: Option<PathBuf>,
1014 /// Skip auto-loading `.env` from the current directory.
1015 #[arg(long)]
1016 pub no_env_file: bool,
1017 /// Stop after fetching from the source — write nothing to the sink.
1018 #[arg(long)]
1019 pub dry_run: bool,
1020 /// Stop after writing this many records to the sink. Default: unlimited.
1021 #[arg(long)]
1022 pub limit: Option<usize>,
1023 /// Override the state-store directory (file backend only).
1024 #[arg(long)]
1025 pub state_path: Option<PathBuf>,
1026 /// Override the `${now.*}` interpolation clock (RFC3339 like
1027 /// `2026-01-31T00:00:00Z`, or a date `2026-01-31`). Default: process start (UTC).
1028 /// Use for backfills.
1029 #[arg(long)]
1030 pub clock: Option<String>,
1031 /// Select a named overlay from the config's `profiles:` block and deep-merge
1032 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
1033 /// Not applicable in `--from-env` mode (no config file to compose).
1034 #[arg(long, env = "FAUCET_PROFILE")]
1035 pub profile: Option<String>,
1036 /// Show a live full-screen terminal UI (per-invocation throughput, errors,
1037 /// DLQ counts, bookmark age) while the pipeline runs. Requires a binary
1038 /// built with the `cli-tui` feature and a real terminal on stdout —
1039 /// on a non-TTY (CI, pipes) the run proceeds normally with a notice.
1040 /// Press `q` to cancel cooperatively (in-flight work flushes at the next
1041 /// page boundary).
1042 #[arg(long)]
1043 pub tui: bool,
1044
1045 /// Suppress the inline live progress line (records in/out, rows/s, pages,
1046 /// elapsed) that `faucet run` shows on an interactive terminal. The
1047 /// progress line is already auto-disabled on a non-TTY stdout (CI, pipes)
1048 /// and when `--tui` is used; `--quiet` turns it off explicitly, keeping
1049 /// only the periodic log output.
1050 #[arg(long)]
1051 pub quiet: bool,
1052
1053 /// Format for the end-of-run summary: `text` (default, human — written to
1054 /// **stderr** so stdout stays clean for the sink), `json` (a single
1055 /// machine-readable document on **stdout**), or `ndjson` (one JSON object
1056 /// per matrix row on **stdout**). With `json`/`ndjson`, stdout carries only
1057 /// the summary — logs stay on stderr — so `faucet run` is scriptable.
1058 #[arg(long, value_enum, default_value_t = RunOutput::Text)]
1059 pub output: RunOutput,
1060
1061 /// Supply a value for a `params:` entry declared by the config (#444):
1062 /// `--param tenant_id=acme`. Repeatable. Values are coerced to the declared
1063 /// type, so `--param page=50` satisfies a `type: int` param. A param with a
1064 /// `default` needs no flag; a `required` one errors when unsupplied.
1065 #[arg(long = "param", value_name = "NAME=VALUE")]
1066 pub param: Vec<String>,
1067
1068 /// Override an environment variable for this run's `${env:VAR}` resolution
1069 /// only (#444): `--param-env REGION=eu` sets it, bare `--param-env TOKEN`
1070 /// takes the value from the caller's environment. Repeatable. The process
1071 /// environment is not modified.
1072 #[arg(long = "param-env", value_name = "NAME[=VALUE]")]
1073 pub param_env: Vec<String>,
1074
1075 /// Runtime matrix-row selection (`--select`/`--only`/`--skip`/`--status`/
1076 /// `--tag`/`--include-parents`).
1077 #[command(flatten)]
1078 pub selection: SelectionArgs,
1079}
1080
1081/// Format for `faucet run`'s end-of-run summary.
1082#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, clap::ValueEnum)]
1083pub enum RunOutput {
1084 /// Human-readable one-line summary (default).
1085 #[default]
1086 Text,
1087 /// A single machine-readable JSON document with per-row + total stats.
1088 Json,
1089 /// One JSON object per matrix row (newline-delimited) for streaming consumers.
1090 Ndjson,
1091}
1092
1093/// `faucet backfill` arguments.
1094#[derive(Debug, Parser)]
1095pub struct BackfillArgs {
1096 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config. If omitted,
1097 /// auto-discover `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
1098 pub config: Option<PathBuf>,
1099 /// Window start (inclusive): RFC3339 (`2026-06-01T00:00:00Z`) or a date
1100 /// (`2026-06-01`, midnight in --timezone). Requires --to.
1101 #[arg(long, requires = "to", conflicts_with = "from_bookmark")]
1102 pub from: Option<String>,
1103 /// Window end (exclusive): RFC3339 or a date.
1104 #[arg(long, requires = "from", conflicts_with = "from_bookmark")]
1105 pub to: Option<String>,
1106 /// Chunk the range into windows of this duration (`45s`, `30m`, `6h`,
1107 /// `1d`, `1w`) so each chunk is an independent, resumable unit. Defaults
1108 /// to the config's `backfill.window`; omitted = one unit for the whole
1109 /// range.
1110 #[arg(long)]
1111 pub window: Option<String>,
1112 /// Replay from this explicit bookmark value instead of a wall-clock
1113 /// range (seeded into the backfill's scoped state key; the source's own
1114 /// incremental logic reads forward from it). JSON or a bare string.
1115 #[arg(long)]
1116 pub from_bookmark: Option<String>,
1117 /// Upper bookmark bound: records whose --bookmark-field orders after
1118 /// this value are dropped before the sink.
1119 #[arg(long, requires_all = ["from_bookmark", "bookmark_field"])]
1120 pub to_bookmark: Option<String>,
1121 /// Record field the --to-bookmark bound applies to.
1122 #[arg(long)]
1123 pub bookmark_field: Option<String>,
1124 /// Max concurrently-running window units. Defaults to the config's
1125 /// `backfill.concurrency`, else 1 (sequential).
1126 #[arg(long)]
1127 pub concurrency: Option<usize>,
1128 /// IANA timezone for date boundaries and `${now.*}` rendering. Defaults
1129 /// to the config's `backfill.timezone`, else UTC.
1130 #[arg(long)]
1131 pub timezone: Option<String>,
1132 /// Root row of the config to backfill. Defaults to the only root.
1133 #[arg(long)]
1134 pub row: Option<String>,
1135 /// Redirect writes to this named sink template under `pipeline.sinks`
1136 /// (backfill into a staging table first).
1137 #[arg(long)]
1138 pub into: Option<String>,
1139 /// Print the planned units without running anything.
1140 #[arg(long)]
1141 pub dry_run: bool,
1142 /// Continue a previously-interrupted backfill of the same range: skip
1143 /// units already done, re-run failed and pending ones.
1144 #[arg(long, conflicts_with = "restart")]
1145 pub resume: bool,
1146 /// Discard a previous progress marker for this range and start over.
1147 #[arg(long)]
1148 pub restart: bool,
1149 /// Emit a machine-readable JSON report instead of the human summary.
1150 #[arg(long)]
1151 pub json: bool,
1152 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
1153 /// Defaults to `.env` in cwd if present.
1154 #[arg(long, conflicts_with = "no_env_file")]
1155 pub env_file: Option<PathBuf>,
1156 /// Skip auto-loading `.env` from cwd.
1157 #[arg(long)]
1158 pub no_env_file: bool,
1159 /// Select a named overlay from the config's `profiles:` block.
1160 /// Overrides the `FAUCET_PROFILE` env var.
1161 #[arg(long, env = "FAUCET_PROFILE")]
1162 pub profile: Option<String>,
1163}
1164
1165/// `faucet replicate` arguments.
1166#[derive(Debug, Parser)]
1167pub struct ReplicateArgs {
1168 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config with a
1169 /// `replication:` block. If omitted, auto-discover
1170 /// `faucet.yaml` / `.yml` / `.json` in cwd.
1171 pub config: Option<PathBuf>,
1172 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
1173 /// Defaults to `.env` in cwd if present.
1174 #[arg(long, conflicts_with = "no_env_file")]
1175 pub env_file: Option<PathBuf>,
1176 /// Skip auto-loading `.env` from cwd.
1177 #[arg(long)]
1178 pub no_env_file: bool,
1179 /// Select a named overlay from the config's `profiles:` block and deep-merge
1180 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
1181 #[arg(long, env = "FAUCET_PROFILE")]
1182 pub profile: Option<String>,
1183}
1184
1185/// `faucet discover` arguments.
1186#[derive(Debug, Parser)]
1187pub struct DiscoverArgs {
1188 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config whose source
1189 /// points at the system to introspect. If omitted, auto-discover
1190 /// `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
1191 pub config: Option<PathBuf>,
1192 /// Which source template to introspect (an entry under `pipeline.sources`).
1193 /// Defaults to `default` (the legacy singular `pipeline.source`).
1194 #[arg(long)]
1195 pub source: Option<String>,
1196 /// Only include datasets whose name matches this `*`-wildcard pattern
1197 /// (repeatable; no patterns = include everything).
1198 #[arg(long)]
1199 pub include: Vec<String>,
1200 /// Exclude datasets whose name matches this `*`-wildcard pattern
1201 /// (repeatable; applied after --include).
1202 #[arg(long)]
1203 pub exclude: Vec<String>,
1204 /// Write the generated config to this file instead of stdout.
1205 #[arg(long, short = 'o')]
1206 pub output: Option<PathBuf>,
1207 /// Overwrite the --output file if it already exists.
1208 #[arg(long)]
1209 pub force: bool,
1210 /// Emit the discovered datasets as machine-readable JSON instead of a
1211 /// generated config.
1212 #[arg(long)]
1213 pub json: bool,
1214 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
1215 /// Defaults to `.env` in cwd if present.
1216 #[arg(long, conflicts_with = "no_env_file")]
1217 pub env_file: Option<PathBuf>,
1218 /// Skip auto-loading `.env` from cwd.
1219 #[arg(long)]
1220 pub no_env_file: bool,
1221 /// Select a named overlay from the config's `profiles:` block.
1222 /// Overrides the `FAUCET_PROFILE` env var.
1223 #[arg(long, env = "FAUCET_PROFILE")]
1224 pub profile: Option<String>,
1225}
1226
1227/// `faucet validate` arguments.
1228#[derive(Debug, Parser)]
1229pub struct ValidateArgs {
1230 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config. If omitted,
1231 /// auto-discover `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
1232 pub config: Option<PathBuf>,
1233 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
1234 /// Defaults to `.env` in cwd if present.
1235 #[arg(long, conflicts_with = "no_env_file")]
1236 pub env_file: Option<PathBuf>,
1237 /// Skip auto-loading `.env` from cwd.
1238 #[arg(long)]
1239 pub no_env_file: bool,
1240 /// Validate grammar and structure only — skip fetching from secrets
1241 /// managers (no network / credentials needed).
1242 #[arg(long)]
1243 pub no_secrets: bool,
1244 /// Select a named overlay from the config's `profiles:` block and deep-merge
1245 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
1246 #[arg(long, env = "FAUCET_PROFILE")]
1247 pub profile: Option<String>,
1248 /// Print the fully-composed config (after extends/!include/profile, before
1249 /// `${...}` interpolation) and exit. For debugging composition precedence.
1250 /// `--no-secrets` is redundant here (no interpolation or secret fetch occurs).
1251 #[arg(long)]
1252 pub show_composed: bool,
1253
1254 /// Supply a value for a declared `params:` entry (#444), e.g.
1255 /// `--param tenant_id=acme`. Repeatable. Without any `--param`, a `required`
1256 /// param is validated against a type-shaped placeholder — so a
1257 /// parameterized config validates in CI without inventing real values.
1258 /// Passing at least one `--param` switches to strict binding, checking that
1259 /// every required param is supplied and every value has the declared type.
1260 #[arg(long = "param", value_name = "NAME=VALUE")]
1261 pub param: Vec<String>,
1262
1263 /// Override an environment variable for this validation only:
1264 /// `--param-env REGION=eu`, or bare `--param-env TOKEN` to take it from the
1265 /// caller's environment. Repeatable.
1266 #[arg(long = "param-env", value_name = "NAME[=VALUE]")]
1267 pub param_env: Vec<String>,
1268
1269 /// Runtime matrix-row selection — `validate` reports each row's resolved
1270 /// status/tags and whether the selection would run or skip it.
1271 #[command(flatten)]
1272 pub selection: SelectionArgs,
1273
1274 /// Emit a structured JSON validation summary instead of the prose report,
1275 /// so CI can assert on it programmatically. Suppresses the human lines.
1276 #[arg(long)]
1277 pub json: bool,
1278}
1279
1280/// `faucet schema` arguments.
1281#[derive(Debug, Parser)]
1282pub struct SchemaArgs {
1283 #[command(subcommand)]
1284 pub target: Option<SchemaTarget>,
1285 /// List every valid schema target and exit, instead of printing a schema.
1286 #[arg(long)]
1287 pub list: bool,
1288}
1289
1290/// Schema subcommand target — which connector or system component to describe.
1291#[derive(Debug, Subcommand)]
1292pub enum SchemaTarget {
1293 /// Composed JSON Schema for the **entire** `faucet.yaml` / `faucet.json`
1294 /// config document (top-level grammar + per-connector `type` discrimination).
1295 /// Point an editor at it with a `# yaml-language-server: $schema=…` header.
1296 Config,
1297 /// JSON Schema for a source connector config.
1298 Source {
1299 /// Connector name (e.g. `rest`, `graphql`, `postgres`).
1300 #[arg(add = ArgValueCandidates::new(completions::source_kind_candidates))]
1301 name: String,
1302 },
1303 /// JSON Schema for a sink connector config.
1304 Sink {
1305 /// Connector name (e.g. `jsonl`, `bigquery`, `postgres`).
1306 #[arg(add = ArgValueCandidates::new(completions::sink_kind_candidates))]
1307 name: String,
1308 },
1309 /// JSON Schema for a transform's inline config.
1310 Transform {
1311 /// Transform name (e.g. `flatten`, `keys_case`, `cast`).
1312 /// Run `faucet list` to see what is compiled in.
1313 #[arg(add = ArgValueCandidates::new(completions::transform_candidates))]
1314 name: String,
1315 },
1316 /// JSON Schema for the DLQ (Dead Letter Queue) specification.
1317 Dlq,
1318 /// JSON Schema for the `replication:` (snapshot→CDC) block.
1319 Replication,
1320 /// JSON Schema for the `backfill:` (window replay defaults) block.
1321 Backfill,
1322 /// JSON Schema for the `partition:` (range partitioning) block.
1323 Partition,
1324 /// JSON Schema for the top-level `execution:` block.
1325 Execution,
1326 /// JSON Schema for the top-level `resilience:` block.
1327 Resilience,
1328 /// JSON Schema for the top-level `sla:` (freshness/volume SLA) block.
1329 Sla,
1330 /// JSON Schema for the `quality:` block.
1331 #[cfg(feature = "quality")]
1332 Quality,
1333 /// JSON Schema for the `contract:` block.
1334 #[cfg(feature = "contract")]
1335 Contract,
1336 /// JSON Schema for the `masking:` (PII masking) block.
1337 #[cfg(feature = "masking")]
1338 Masking,
1339 /// JSON Schema for the `faucet test` spec file.
1340 Test,
1341 /// Grammar reference for secrets-manager interpolation directives.
1342 Secrets,
1343 /// JSON Schema for the `schedule:` block.
1344 #[cfg(feature = "schedule")]
1345 Schedule,
1346 /// JSON Schema for the `lineage:` (OpenLineage) block.
1347 #[cfg(feature = "lineage")]
1348 Lineage,
1349 /// JSON Schema for the `--triggers` file (event-driven pipeline triggers).
1350 #[cfg(feature = "triggers")]
1351 Triggers,
1352 /// JSON Schema for the `notifications:` (incident-routing) block.
1353 #[cfg(feature = "notify")]
1354 Notifications,
1355 /// JSON Schema for the `catalog:` (Data Movement Catalog store) block.
1356 #[cfg(feature = "catalog")]
1357 Catalog,
1358 /// JSON Schema for one entry of the `params:` (typed run parameters) block.
1359 /// A config's `params:` maps names to entries of this shape; values are
1360 /// supplied per run via `--param` or a template trigger.
1361 Params,
1362}
1363
1364/// `faucet preview` arguments.
1365#[derive(Debug, Parser)]
1366pub struct PreviewArgs {
1367 /// Path to a `.yaml`, `.yml`, or `.json` pipeline config. If omitted,
1368 /// auto-discover `faucet.yaml` / `faucet.yml` / `faucet.json` in cwd.
1369 pub config: Option<PathBuf>,
1370 /// Stop after this many records. Default: 10.
1371 #[arg(long, default_value_t = 10)]
1372 pub limit: usize,
1373 /// Path to a `.env` file to load for `${env:VAR}` interpolation.
1374 /// Defaults to `.env` in cwd if present.
1375 #[arg(long, conflicts_with = "no_env_file")]
1376 pub env_file: Option<PathBuf>,
1377 /// Skip auto-loading `.env` from cwd.
1378 #[arg(long)]
1379 pub no_env_file: bool,
1380 /// Select a named overlay from the config's `profiles:` block and deep-merge
1381 /// it over the composed base. Overrides the `FAUCET_PROFILE` env var.
1382 #[arg(long, env = "FAUCET_PROFILE")]
1383 pub profile: Option<String>,
1384
1385 /// Runtime matrix-row selection — `preview` previews the first root row of
1386 /// the selected run set.
1387 #[command(flatten)]
1388 pub selection: SelectionArgs,
1389}
1390
1391/// `faucet init` arguments.
1392#[derive(Debug, Parser)]
1393pub struct InitArgs {
1394 /// Name written into the generated file's `name:` field. Defaults to
1395 /// `my-pipeline` when omitted.
1396 pub name: Option<String>,
1397 /// Source connector kind to scaffold (e.g. `rest`, `postgres`, `s3`).
1398 /// Defaults to `rest`. Run `faucet list` to see what is compiled in.
1399 #[arg(long)]
1400 pub source: Option<String>,
1401 /// Sink connector kind to scaffold (e.g. `jsonl`, `bigquery`).
1402 /// Defaults to `jsonl`. Run `faucet list` to see what is compiled in.
1403 #[arg(long)]
1404 pub sink: Option<String>,
1405 /// Output file path. Defaults to `pipeline.yaml`.
1406 #[arg(long, short = 'o', default_value = "pipeline.yaml")]
1407 pub output: PathBuf,
1408 /// Overwrite the output file if it already exists.
1409 #[arg(long)]
1410 pub force: bool,
1411 /// Prompt for the source and sink kinds interactively instead of using
1412 /// `--source` / `--sink`. Requires the `cli-interactive` build feature
1413 /// and a TTY on stdin; falls back to the arg-driven path otherwise.
1414 #[arg(long)]
1415 pub interactive: bool,
1416 /// Name of the template under which to register the scaffolded source
1417 /// and sink. The generated config uses `pipeline.sources.<TEMPLATE>` and
1418 /// `pipeline.sinks.<TEMPLATE>`. Defaults to `default` so a matrix row
1419 /// without a `ref:` field still resolves through the new schema.
1420 #[arg(long, default_value = "default")]
1421 pub template: String,
1422 /// (singer only) Run `<executable> --discover` to fetch the tap's catalog,
1423 /// write it next to the output, and scaffold the config with the discovered
1424 /// streams listed. Requires `--source singer` and `--executable`.
1425 #[arg(long)]
1426 pub discover: bool,
1427 /// (singer only) The Singer tap executable to discover with (used by
1428 /// `--discover`), e.g. `tap-github` or `/opt/taps/tap-csv`.
1429 #[arg(long)]
1430 pub executable: Option<String>,
1431 /// (singer only) The target stream to emit. When given with `--discover`,
1432 /// the written catalog marks this stream — and any inferable parent
1433 /// streams (e.g. a parent-keyed tap's parent) — `selected`, and the
1434 /// scaffolded config's `stream:` is set to it. Most DB / SDK taps sync
1435 /// nothing unless a stream is selected in the catalog.
1436 #[arg(long)]
1437 pub stream: Option<String>,
1438}
1439
1440/// `faucet plan` arguments.
1441#[derive(Debug, Parser)]
1442pub struct PlanArgs {
1443 /// Path to a `.yaml`/`.yml`/`.json` config (auto-discovered if omitted).
1444 pub config: Option<PathBuf>,
1445 /// Which row to plan (default: the first root row).
1446 #[arg(long)]
1447 pub row: Option<String>,
1448 /// Offline sample of input records (`.jsonl` or a `.json` array) to preview
1449 /// the output schema, volume, and sink delta through — no source is touched.
1450 #[arg(long)]
1451 pub sample: Option<PathBuf>,
1452 /// Pull a capped, read-only sample from the real source instead of a
1453 /// fixture (bounded by `--limit`; no bookmark is advanced).
1454 #[arg(long)]
1455 pub live: bool,
1456 /// Cap for `--live` sampling.
1457 #[arg(long, default_value_t = 10)]
1458 pub limit: usize,
1459 /// Emit the plan as JSON.
1460 #[arg(long)]
1461 pub json: bool,
1462 /// Show a `terraform plan`-style diff of the current config against the last
1463 /// recorded run, instead of the resolved-pipeline preview (#374). Requires a
1464 /// `catalog:` block. Resolves secrets so the diff matches what `run` records.
1465 #[arg(long)]
1466 pub diff: bool,
1467 /// Resolve secrets-manager directives (needs network/credentials). Off by
1468 /// default so `plan` works offline like `faucet test`. Implied by `--diff`.
1469 #[arg(long)]
1470 pub resolve_secrets: bool,
1471 /// Select a `profiles:` overlay.
1472 #[arg(long, env = "FAUCET_PROFILE")]
1473 pub profile: Option<String>,
1474}
1475
1476/// `faucet dev` arguments.
1477#[derive(Debug, Parser)]
1478pub struct DevArgs {
1479 /// Path to the `.yaml`/`.yml`/`.json` config to watch.
1480 pub config: PathBuf,
1481 /// Which row to run (default: the first root row).
1482 #[arg(long)]
1483 pub row: Option<String>,
1484 /// Offline sample of input records (`.jsonl` or `.json` array). Required
1485 /// for the offline loop.
1486 #[arg(long)]
1487 pub sample: Option<PathBuf>,
1488 /// (reserved) pull a capped read-only sample from the real source.
1489 #[arg(long)]
1490 pub live: bool,
1491 /// Cap for `--live` sampling.
1492 #[arg(long, default_value_t = 10)]
1493 pub limit: usize,
1494 /// Run once and exit instead of watching (also the non-TTY fallback).
1495 #[arg(long)]
1496 pub once: bool,
1497 /// Debounce window between re-runs, in milliseconds.
1498 #[arg(long, default_value_t = 300)]
1499 pub debounce_ms: u64,
1500 /// Select a `profiles:` overlay.
1501 #[arg(long, env = "FAUCET_PROFILE")]
1502 pub profile: Option<String>,
1503}
1504
1505/// `faucet list` arguments.
1506#[derive(Debug, Parser)]
1507pub struct ListArgs {
1508 /// List every connector in the registry index (not just the compiled-in
1509 /// ones), marking which are already in this binary.
1510 #[arg(long)]
1511 pub available: bool,
1512 /// Read a custom registry index instead of the built-in one.
1513 #[arg(long)]
1514 pub index: Option<PathBuf>,
1515 /// Emit the listing as JSON instead of the human-readable columns.
1516 #[arg(long)]
1517 pub json: bool,
1518}
1519
1520/// `faucet conformance` arguments.
1521#[derive(Debug, Parser)]
1522pub struct ConformanceArgs {
1523 /// Only score the connector with this system name (e.g. `postgres`); prints
1524 /// a detailed scorecard. Omit to score every compiled-in connector.
1525 pub name: Option<String>,
1526 /// Restrict to `source` or `sink`.
1527 #[arg(long)]
1528 pub kind: Option<String>,
1529 /// Score every compiled-in connector (the default when no NAME is given;
1530 /// accepted explicitly for clarity in CI).
1531 #[arg(long)]
1532 pub all: bool,
1533 /// Emit the full scorecards as JSON.
1534 #[arg(long)]
1535 pub json: bool,
1536 /// Fail (exit non-zero) if any scored connector is below this maturity tier
1537 /// — an opt-in CI gate. One of `stable` / `experimental` / `beta` / `draft`.
1538 #[arg(long, value_name = "TIER")]
1539 pub min_tier: Option<String>,
1540 /// Print the connector capability matrix (Markdown) derived from the
1541 /// registry allowlists and exit — the generated source for the docs-site
1542 /// capability matrix. Ignores the scoring flags.
1543 #[arg(long)]
1544 pub matrix: bool,
1545}
1546
1547/// `faucet search` arguments.
1548#[derive(Debug, Parser)]
1549pub struct SearchArgs {
1550 /// Term to match against connector name / description / keywords / crate.
1551 pub term: String,
1552 /// Read a custom registry index instead of the built-in one.
1553 #[arg(long)]
1554 pub index: Option<PathBuf>,
1555 /// Emit matches as JSON.
1556 #[arg(long)]
1557 pub json: bool,
1558}
1559
1560/// `faucet install` arguments.
1561#[derive(Debug, Parser)]
1562pub struct InstallArgs {
1563 /// Connector system name (e.g. `kafka`).
1564 pub name: String,
1565 /// Disambiguate when a name exists as both a source and a sink.
1566 #[arg(long)]
1567 pub kind: Option<String>,
1568 /// Read a custom registry index instead of the built-in one.
1569 #[arg(long)]
1570 pub index: Option<PathBuf>,
1571}
1572
1573/// `faucet new` arguments.
1574#[derive(Debug, Parser)]
1575pub struct NewArgs {
1576 #[command(subcommand)]
1577 pub target: NewTarget,
1578}
1579
1580/// What `faucet new` scaffolds.
1581#[derive(Debug, Subcommand)]
1582pub enum NewTarget {
1583 /// Scaffold a ready-to-build `faucet-source-<name>` / `faucet-sink-<name>`
1584 /// connector crate following every repo convention.
1585 Connector(NewConnectorArgs),
1586}
1587
1588/// `faucet new connector` arguments.
1589#[derive(Debug, Parser)]
1590pub struct NewConnectorArgs {
1591 /// Connector system name (lowercase, e.g. `acme` or `acme-widgets`). Becomes
1592 /// the crate name `faucet-<kind>-<name>` and the YAML `type:` value.
1593 pub name: String,
1594 /// Whether to scaffold a `source` or a `sink`.
1595 #[arg(long)]
1596 pub kind: String,
1597 /// Also scaffold a `faucet-common-<name>` crate for config shared between a
1598 /// source/sink pair.
1599 #[arg(long)]
1600 pub common: bool,
1601 /// Directory to write the new crate(s) into. Defaults to the current dir.
1602 #[arg(long, short = 'o', default_value = ".")]
1603 pub output: PathBuf,
1604 /// Overwrite any existing files.
1605 #[arg(long)]
1606 pub force: bool,
1607}