CLI
The rflow binary is a pure interface over the core engine. Every command accepts
--path <dir> (short -p) to point at a project directory; the default is the
current directory.
rflow --helpBlazing fast EVM workflow engine built in rust
Usage: rflow <COMMAND>
Commands:
new Create a new rflow project (alias: init)
templates Browse, inspect, check and author templates (builtin + community registry)
add Add a workflow (from a template), network, contract, relayer or notification channel to rflow.yaml
doctor Check local dependencies (Docker, Foundry, Postgres, Node) and project readiness
explain Plain-English breakdown of what a workflow does (static, from rflow.yaml)
abi Fetch, inspect and reverse-lookup contract ABIs
contract Contract helpers - `contract add` fetches the verified ABI when --abi is omitted
import Convert another platform's config into an rflow project
start Validate then boot the engines (lazy), reconcile relayers, recover and run
validate Strict collect-all validation of rflow.yaml
lint Opinionated safety lint over rflow.yaml (policy-driven, static, no DB)
ci One-command CI gate: validate + lint + schema/template/plan/test checks
schema Print the JSON Schema for rflow.yaml (editor autocomplete/validation)
trigger Manually fire a workflow
ls List workflows: state, trigger, cursor, runs today
status Engine health, chains, relayer registry, recent runs
tables List the queryable tables (indexed event tables + the rflow journal)
runs Inspect the run journal
history Explore workflow run history (a richer `runs list`); subcommands: txs, approvals, waiting, export
approve Approve a parked send (by approval id or run id)
reject Reject a parked send (by approval id or run id)
approvals List send approvals
state Durable-state dashboard + repair (inspect | cursors/waits/delays/circuit)
cursors Inspect (ls) or move (set) trigger cursors
waits Inspect (ls) or cancel parked saga waits
delays Inspect (ls) or wake parked durable delays
circuit Inspect (ls) or reset circuit breakers
budgets Inspect durable spend budgets (consumed / reserved / remaining)
spend Native spend history: gas + value by workflow / relayer / network; subcommand: export
token Manage named API bearer tokens for config.server.auth
secrets Inspect declared secrets and verify external-provider resolution (never prints values)
relayers Manage relayer wallet mappings
workflow Pause or resume a workflow
list Inspect and mutate runtime watchlists (`lists:`)
mcp Serve the Model Context Protocol over stdio (AI agent/editor integration)
replay Replay a workflow over historical blocks in an isolated session (dry-run by default), or manage replay/test sessions (`ls` / `prune`)
versions Inspect the deployed config version history (ls | show <id> | diff <a> <b>)
plan Semantic diff of a config change + active runtime conflicts (the deploy gate)
rollback Reconstruct a stored config version back to rflow.yaml (structure; secrets stay placeholders)
retention Plan or run journal retention (history pruning driven by config.retention)
archive Export a redacted JSONL archive of runs + steps + approvals + failures in a date range
command Debug a single `command:` step in isolation (no signer, no db, no run)
test Execute one workflow once in dry-run mode from a fixture or a real transaction
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print versionrflow new
rflow new [--output <dir>] [--name <project-name>] [--template <id>] [--yes]
[--answer key=value ...] [--answers-file answers.yaml]
[--registry github:owner/repo[@branch]]Scaffolds a project from the template registry (rflow templates ls):
rflow.yaml, ABIs, .env + .env.example, docker-compose.yml (postgres on
localhost:5448), a .gitignore and a .rflow/template-lock.yaml recording the
install (template id, version, answers). Interactive on a TTY — a template picker
(id + risk + summary), then one typed prompt per template input; --yes (or a
non-TTY) fills every input from its default. --answer key=value and
--answers-file drive it non-interactively (CI/agents); every answer is
type-checked before rendering (addresses, cron expressions, durations, …).
--template community/<id> installs from the
community registry — fetched SHA-pinned and
hash-verified from the rflow repo, gated by the community doctor profile and
a provenance banner before anything renders (--registry points the same
mechanism at a fork, loudly labeled UNTRUSTED). The interactive picker's
final entry fetches the community index and lists its ids.
Legacy scaffold ids keep working: transfer-alert, cron-report and
treasury-approval resolve to their registry equivalents via aliases;
liquidation-keeper, webhook-relay and blank still scaffold through the
pre-registry renderer until converted. Templates with requires.signer get a
DEV-ONLY generated mnemonic in .env (never in .env.example). Aliased as
rflow init. Refuses to overwrite an existing rflow.yaml, leaves an existing
.env untouched, and ends with rflow validate + next commands.
See Scaffolding for the wizard walkthrough.
rflow templates
rflow templates ls [--category <c>] [--risk <r>] [--json]
rflow templates ls --community [--registry github:owner/repo[@branch]]
rflow templates search <query>
rflow templates show <id>
rflow templates inspect <id> --json
rflow templates doctor <id | --all>
rflow templates index [--dir <repo-root>] [--check [--against <baseline-index.json>]]
rflow templates scaffold <id> [--dir <path>]
rflow templates snapshot <id> [--dir <path>]Browse, inspect, check and author templates. ls prints
id/category/risk/summary (categories: treasury, monitoring, keeper, intents,
bridge-ops, governance, security, relayers, offchain, examples; risk labels:
monitor_only, prepares_tx, money_moving, admin, experimental);
ls --community fetches the community registry
index instead — pinned to the branch head commit, --registry points at a
fork. search matches id/title/summary. show prints the human detail:
generated files, typed inputs with defaults, required
signer/relayers/ABIs/secrets, declared interfaces/endpoints, the safety
profile and the docs page (deprecated templates are labeled). inspect
prints the raw manifest as JSON for agents/tooling. doctor proves a bundled
template is shippable — manifest parses strictly, declared outputs match the
packaged files, interface surfaces verify (recomputed selectors/topic0s),
inputs are well-formed, the sample answers render a project that passes
rflow validate, the committed snapshot matches, and the docs page exists
(skipped outside the repo). Exit 1 on any failure.
The last three are the community-contribution tools: index regenerates
templates/community/index.json (per-file sha256 pins; --check is the CI
gate — byte-exact index + immutable versions, --against enforces the same
immutability vs a baseline index from the base branch). scaffold writes a
compliant community-package skeleton that passes the community doctor
profile as soon as snapshot generates its tests/expected-rflow.yaml.
Browse, inspect, check and author templates (builtin + community registry)
Usage: rflow templates <COMMAND>
Commands:
ls List templates: id, category, risk, summary
search Keyword search over template id/title/summary
show Human-readable detail for one template
inspect Machine-readable manifest for agents/tooling (always JSON)
doctor Validate bundled template(s): manifest, files, inputs, sample render, snapshot, docs page
index Regenerate templates/community/index.json (per-file sha256 pins over every community package)
scaffold Write a compliant community-package skeleton (the submission starting point - see templates/community/SUBMITTING.md)
snapshot Render an on-disk package with its answers.sample.yaml and write tests/expected-rflow.yaml (the community snapshot regenerator)
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print versionSee the templates guide, the community registry and the use-case recipes.
rflow add
rflow add [--path <dir>] workflow <template-id> [--name <wf>] [--answer k=v ...] [--answers-file <f>] [--dry-run] [--yes] [--registry <github:owner/repo[@branch]>]
rflow add [--path <dir>] network [--name <n>] [--chain-id <id>] [--rpc <url|${ENV}>] [--ws <url>] [--confirmations <n>]
rflow add [--path <dir>] contract [--name <n>] [--abi <path>] (--network <net> --address <0x..> | --addresses net=0x,net=0x)
rflow add [--path <dir>] relayer [--name <n>] [--networks a,b] [--speed SLOW|MEDIUM|FAST|SUPER]
rflow add [--path <dir>] notification [--name <n>] (--telegram | --slack | --discord | --pagerduty | --opsgenie | --twilio) [env flags]add workflow composes a template into an existing project: it renders the
template with your answers and merges its contracts, relayers, constants,
secrets, lists, notification channels and workflow into rflow.yaml
(comment-preserving), copying packaged files (ABIs) alongside. Inputs already
satisfied by the project (its name, its single network) are not re-asked; on a
TTY the remaining inputs are prompted. Collisions follow the registry rules —
an identical entry is reused silently, a same-ABI/subset-addresses contract
is reused with a warning, a conflicting entry is a hard error, and a taken
workflow name demands --name <new-name>. --dry-run prints the plan without
writing; a successful add appends to .rflow/template-lock.yaml, refuses any
change that would introduce a validation error, and prints which env vars to
set. A multi-workflow template (e.g. safe-monitor) splices every workflow
it declares, in authored order; --name only applies to single-workflow
templates. community/<id> composes a
community template the same way — fetched
SHA-pinned + hash-verified, gated by the community doctor profile first.
Add a workflow from a template into the existing rflow.yaml (see `rflow templates ls`)
Usage: rflow add workflow [OPTIONS] <TEMPLATE>
Arguments:
<TEMPLATE> The template id, e.g. `large-transfer-alert`
Options:
--name <NAME>
Rename the added workflow (required when the template's workflow name already exists in rflow.yaml)
-p, --path <PATH>
optional - The path of the project, default will be where the command is run
--answer <KEY=VALUE>
Template answer as key=value (repeatable)
--answers-file <FILE>
YAML file of template answers (`key: value` scalars)
--dry-run
Print the merge plan without writing anything
--yes
Non-interactive: no prompts, manifest defaults fill missing inputs
--registry <GITHUB:OWNER/REPO[@BRANCH]>
Fetch `community/<id>` templates from this registry instead of the default rflow repo (`github:owner/repo[@branch]`). Non-default registries are UNTRUSTED and require confirmation
-h, --help
Print help
-V, --version
Print versionThe other four subcommands incrementally grow an existing rflow.yaml — the
counterpart to rflow new.
add appends to the right top-level section and preserves the rest of the file
byte-for-byte (comments, ordering, formatting, trailing newline). It never
parses the whole document to a value and re-serializes it, so your comments on a
money config survive. It expects the 2-space indentation style rflow new
scaffolds; a section in another style (zero-indented lists, 4-space indents) makes
the add refuse with the file untouched.
Interactive on a TTY (a network-preset picker, a channel-kind menu, env-var prompts), and fully flag-driven for CI/agents/non-TTY. A non-TTY run with a required flag missing errors with exactly what is needed.
Before writing, add validates the merged config in memory and refuses
(exit 1, file untouched) any change that would introduce a hard validation error —
a duplicate name, an unknown network reference, a bad address, or a relayer with
no signer. Errors that were already present before the add are surfaced but do not
block. Every successful add then runs rflow validate and prints the exact next
command(s), including a rflow doctor hint.
Env-var flags take the variable name and are rendered as a ${VAR}
placeholder; when only the kind flag is given they default to conventional names
(TG_BOT_TOKEN/TG_CHAT_ID, SLACK_WEBHOOK_URL, DISCORD_WEBHOOK_URL,
PAGERDUTY_ROUTING_KEY, OPSGENIE_API_KEY, TWILIO_ACCOUNT_SID/TWILIO_AUTH_TOKEN).
Add a workflow (from a template), network, contract, relayer or notification channel to rflow.yaml
Usage: rflow add [OPTIONS] <COMMAND>
Commands:
workflow Add a workflow from a template into the existing rflow.yaml (see `rflow templates ls`)
network Add a network to `networks:`
contract Add a contract to `contracts:`
relayer Add a relayer wallet to `relayers:`
notification Add a notification channel to `notifications.channels:`
help Print this message or the help of the given subcommand(s)
Options:
-p, --path <PATH> optional - The path of the project, default will be where the command is run
-h, --help Print help
-V, --version Print version--path is global, so it works either before or after the subcommand
(rflow add --path proj network ... and rflow add network --path proj ... are
equivalent).
rflow doctor
rflow doctor [--path <dir>] [--profile <name>] [--deep] [--json]Two sections, one command:
- Quick readiness — Docker (daemon reachable), Foundry (
anvil+castonPATH, with versions), Postgres (reachable via the project'sconfig.db_connection), Node (optional, for the docs), plus therflow.yamlvalidate summary. Every dependency is markedok/missing/n/awith a one-line "how to install or start" hint. - Deep operational suite — runs automatically whenever a project is present
(
--deeponly makes it explicit; without a project only the tool checks run): yaml validation summary, profile merge correctness (the selected--profilemust apply cleanly and validate — other declared profiles are checked in passing), DB connect +rflowschema/migration presence, per-network RPC (eth_chainIdmust match the declaredchain_id— a mismatch is an error),finalized-tag support (advice, probed only when a workflow usesfinalized), best-effort archive depth (advice, probed only when a workflow backfills history), contract sanity (ABI parses, addresses EIP-55 checksum-valid), relayer mapping presence (rflow.relayers) + native balance fetch with a low-balance warning, signer shape (raw/private-key env vars resolve; cloud providers get a config-shape note — no live call), notification channel delivery probes, webhook auth warnings (no auth on a money workflow), trigger cursor lag vs head, approval-gate posture (on_timeout: proceedwarns), reorg/confirmation guidance for send workflows, and rate-limit/budget/circuit coverage notes for money workflows.
Every check carries a severity: error / warning / advice / info.
Exit code: non-zero iff an error-severity check fails — warnings, advice and
info never block, so rflow doctor is safe to gate CI/deploys on. --json
prints one stable machine-readable report (version, quick, checks,
summary, ok) on stdout for exactly that.
Check local dependencies (Docker, Foundry, Postgres, Node) and project readiness
Usage: rflow doctor [OPTIONS]
Options:
-p, --path <PATH> optional - The path of the project, default will be where the command is run
--profile <PROFILE> Diagnose the merged view after applying a `profiles.<name>` overlay (a broken profile is an error-severity failure)
--deep Run the full operational suite (DB schema, RPC chain ids + capabilities, relayer mappings/balances, channels, webhooks, approvals, coverage). Default ON when a project is present — the flag only forces it explicitly
--json Print one stable machine-readable JSON report on stdout (the CI gating surface). Exit code stays: non-zero iff an error-severity check fails
-h, --help Print help
-V, --version Print versionvalidate --preflight runs the connectivity subset of these same checks (same
functions, no duplicated logic) — but preflight keeps its stricter historical
rule: any failed check fails the command. The full check catalogue, JSON shape
and CI gating pattern live in
Doctor & explain.
rflow explain
rflow explain [<workflow>] [--risk] [--json] [--path <dir>] [--profile <name>]A static plain-English narrative of what a workflow does, straight from
rflow.yaml — no network, no database. With no argument: one summary block per
workflow (trigger sentence, step chain, risk label
monitor_only / prepares_tx / money_moving — the template-registry
vocabulary). With a workflow: the full breakdown — the trigger (kind,
contract/event/schedule, conditions, confirmations), each step in order (what it
does, what it depends on, if: conditions), WHAT SENDS MONEY (each
send_transaction: network, relayer, decoded call target, value/args source),
WHAT BLOCKS MONEY (simulation, assert_sim, recheck, valid_for, approval
gates, gas caps, permissions/budgets, relayer policy, rate limit, circuit
breaker, concurrency), ON FAILURE (retries, on_failure route, finally,
workflow_error watchers), ON REORG, REPLAY / DRY-RUN behaviour (one
honest paragraph: sends stop pre-broadcast; waits settle unless --with-waits)
and EXTERNAL SYSTEMS (HTTP hosts, channels, local commands).
--risk prints only the money sections plus a reviewer checklist
([x]/[ ] per safeguard and a verdict: N of 11 line) — enough context to
approve or reject a production change. --json emits the same data structured.
Output is deterministic, and every render ends with the honesty footer: explain
describes the configuration, not runtime guarantees. The PR-review pattern is
in Doctor & explain.
Plain-English breakdown of what a workflow does (static, from rflow.yaml)
Usage: rflow explain [OPTIONS] [WORKFLOW]
Arguments:
[WORKFLOW] The workflow to explain in full. Omitted = one summary block per workflow
Options:
-p, --path <PATH> optional - The path of the project, default will be where the command is run
--risk Only the money sections (WHAT SENDS / WHAT BLOCKS) plus a reviewer checklist verdict
--json Print the same data as one structured JSON object on stdout
--profile <PROFILE> Explain the merged view after applying a `profiles.<name>` overlay
-h, --help Print help
-V, --version Print versionrflow abi
rflow abi fetch (--network <name> | --chain-id <id>) --address <0x..> [--out <file>] [--source sourcify|etherscan] [--name <base>]
rflow abi inspect <file.json> [--json]
rflow abi find-event (<file.json|abis-dir> <topic0> | --project <topic0>) [--json]
rflow abi find-function (<file.json|abis-dir> <sel-or-sig> | --project <sel-or-sig>) [--json]abi fetch pulls the verified ABI for an address and writes it
pretty-printed under the project's abis/ dir. Source order: Sourcify first
(keyless), then Etherscan v2 (https://api.etherscan.io/v2/api?chainid=…,
needs ETHERSCAN_API_KEY — when the key is absent and sourcify had no match,
the error says exactly that, per source). --network reads the chain id from
rflow.yaml; --chain-id works with no project at all. The default filename is
abis/<--name>.json (or abis/<address>.json); re-fetching an identical file
is a no-op, but a file with different content is never clobbered — delete it
or pass --out.
Reproducibility, by design. The spec'd inline-YAML shortcut (
abi: { fetch: ... }insiderflow.yaml) is intentionally not implemented: a config that fetches ABIs at load time is not reproducible.rflow.yamlonly ever points at committed ABI files; the CLI fetches once and materialises the file. No config surface changed (rflow.schema.jsonis untouched).
abi inspect prints every event (name, full signature, topic0 hash,
indexed params) and every function (name, signature, 4-byte selector,
mutability); --json emits the same rows as JSON.
abi find-event reverse-maps a topic0 hash to the event that emits it —
against one ABI file, a directory of ABI json files, or (with --project)
every ABI file the project's rflow.yaml references. Unparseable json files in
a directory scan are skipped with a warning; no match exits 1 with a
rflow abi fetch hint. abi find-function is the same lookup for 4-byte
selectors, and also accepts a full signature (transfer(address,uint256)),
which it hashes for you.
rflow abi fetch --network ethereum --address 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 --name usdc
rflow abi inspect ./abis/usdc.json
rflow abi find-event ./abis 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
rflow abi find-function --project 0xa9059cbbFetch, inspect and reverse-lookup contract ABIs
Usage: rflow abi [OPTIONS] <COMMAND>
Commands:
fetch Fetch the VERIFIED ABI for an address (sourcify first, then etherscan) and write it under the project's abis/ dir
inspect Show every event (topic0, indexed params) and function (selector, mutability) in an ABI file
find-event Find which event a topic0 hash belongs to (one file, a directory of ABIs, or --project for every ABI rflow.yaml references)
find-function Find which function a 4-byte selector (or full signature) belongs to
help Print this message or the help of the given subcommand(s)
Options:
-p, --path <PATH> optional - The path of the project, default will be where the command is run
-h, --help Print help
-V, --version Print versionRelated: validation errors for unknown event/function names now carry a
close-match suggestion — contract 'USDC' has no event 'Transferr' - did you mean 'Transfer'? — and the importers hint at rflow abi find-event /
rflow abi fetch whenever they cannot name an event topic hash locally.
Full guide: ABIs — fetch, inspect & discover.
rflow contract
rflow contract add <Name> --network <net> --address <0x..> [--abi <path>]Adds a contract by address alone. With --abi it is exactly
rflow add contract (the same comment-preserving, validating code path).
Without it, the verified ABI is fetched first (sourcify → etherscan, see
rflow abi fetch), written to abis/<name>.json, and then wired into
contracts: — the command prints what was fetched, the file written, and the
validate result.
rflow contract add USDC --network ethereum --address 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48Full guide: ABIs — fetch, inspect & discover.
Add a contract to rflow.yaml - with --abi this is exactly `rflow add contract`; without it the verified ABI is fetched (sourcify -> etherscan), written to abis/<name>.json and wired up
Usage: rflow contract add [OPTIONS] --network <NETWORK> --address <ADDRESS> <NAME>
Arguments:
<NAME> Contract registry name, e.g. `USDC`
Options:
--network <NETWORK> The network (must exist in rflow.yaml; its chain_id picks the explorer chain when fetching)
-p, --path <PATH> optional - The path of the project, default will be where the command is run
--address <ADDRESS> The contract address (0x…)
--abi <PATH> Use this ABI file instead of fetching one
-h, --help Print help
-V, --version Print versionrflow import
rflow import defender <serverless.yml> [--output <dir>]
rflow import gelato <tasks.json> [--output <dir>]Converts an OpenZeppelin Defender-as-Code serverless.yml or Gelato Automate task
JSON into a complete rflow project that passes rflow validate as-is, with a
summary table of what mapped and a MIGRATION-NOTES.md. See
Migrate from Defender and
Migrate from Gelato.
rflow start
rflow start [--path <dir>] [--yes] [--standby] [--watch] [--profile <name>]The whole boot, in order: load .env → validate rflow.yaml (warnings printed) →
connect Postgres + apply the rflow schema → take the project advisory lock →
generate runtime engine configs → boot the relayer (only if a signer is declared) →
reconcile relayers → recovery pass → boot the indexer
(only if chain triggers exist) → run. Ctrl-C / SIGTERM shuts down gracefully.
--yesskips confirmation prompts (e.g. creating new relayer wallets).--standbyboots as an HA standby: wait for the project leadership lock instead of exiting when another instance holds it (overridesconfig.ha.standby) — see High availability.--watchhot-reloadsrflow.yamlon valid changes; invalid edits keep the running config (see Observability).--profile <name>applies aprofiles.<name>overlay before booting.
rflow validate
rflow validate [--path <dir>] [--preflight] [--preflight-notify] [--profile <name>]Strict, collect-all: unknown keys, missing refs (networks, contracts, relayers, channels, lists), ABI/function checks, expression analysis, signer shape, and reorg advice — all errors reported at once, advice in yellow, errors in red.
--preflight additionally runs live connectivity checks — every RPC's
eth_chainId compared against the declared chain_id, database connectivity,
and notification channels — as an ok/fail table, exiting 1 on any failure.
Slack/Discord webhooks get a TCP reachability check unless --preflight-notify
is passed (which posts a real test message). It also runs
command checks: each command step's executable
resolves (on PATH or as a project-relative path), any local script argument
exists, and a known interpreter reports its version. See
Observability.
--profile <name> validates the merged view a
profile boots.
rflow lint
rflow lint [--path <dir>] [--strict] [--profile <name>] [--json]The opinionated safety linter — validate asks "is this correct?", lint
asks "is this safe to run in production?": unsimulated sends, missing gas
ceilings, head-fired money, unauthenticated webhooks, missing idempotency,
timeout-proceed approvals, public ports on money projects. Fully static (no
database, no network, and usually no .env needed — lint prefers the raw
${VAR} view).
Findings follow an honest three-tier severity: recommended defaults warn
(exit 0), knobs written into the optional top-level policy: block deny
(exit 1) when violated, and --strict (or policy.strict: true) promotes
every warning to a failure. Suppressions live under policy.suppress and
require a written reason (the file does not parse without one). Every
finding prints its fix and a docs link.
Opinionated safety lint over rflow.yaml (policy-driven, static, no DB)
Usage: rflow lint [OPTIONS]
Options:
-p, --path <PATH> optional - The path of the project, default will be where the command is run
--strict Promote every warning to a failure (same as `policy.strict: true`)
--profile <PROFILE> Lint the merged view after applying a `profiles.<name>` overlay (a profile may carry its own stricter `policy:`)
--json Machine-readable report on pure stdout (errors go to stderr)
-h, --help Print help
-V, --version Print versionSee Safety lint & the CI gate for every rule, the
policy: reference and suppression syntax.
rflow ci
rflow ci [--path <dir>] [--profile <name>] [--strict] [--preflight] [--json]The one-command CI gate: validate → lint → committed-schema check →
template-lock check → config plan against
the latest stored version (when the database is reachable) → every
tests/<workflow>[.case].json fixture as a dry-run — plus the live
connectivity preflight with --preflight. Stages whose prerequisites are
absent are skipped with a reason, never silently passed. Exits non-zero
iff a gating stage fails; --json keeps stdout pure for the versioned
machine report.
One-command CI gate: validate + lint + schema/template/plan/test checks
Usage: rflow ci [OPTIONS]
Options:
-p, --path <PATH> optional - The path of the project, default will be where the command is run
--profile <PROFILE> Run every stage against a `profiles.<name>` overlay
--strict Lint in strict mode (warnings fail)
--preflight Also run the live connectivity preflight (RPCs, database, notification channels) - the only stage that dials out
--json Machine-readable report on pure stdout (errors go to stderr)
-h, --help Print help
-V, --version Print versionSee Safety lint & the CI gate for the stage table and a ready-to-copy GitHub Actions workflow.
rflow schema
rflow schema > rflow.schema.jsonPrints the JSON Schema for rflow.yaml — wire it into your editor for
completion and validation (see
editor setup).
rflow trigger
rflow trigger <workflow> [--input key=value ...] [--path <dir>]Manually fire a workflow. --input pairs are exposed to expressions as inputs.*.
rflow ls
rflow ls [--path <dir>]Workflows with their state (running/paused), trigger summary, cursor (last processed block for event triggers), and runs today.
rflow status
rflow status [--path <dir>]Engine health, chains, relayer registry and recent runs — plus the HA leadership
state (leader running / no leader, via a lock probe). Workflows with
liveness:
get a liveness table (last run, max_silence, OK/BREACHED), and detected
indexer stalls print per network.
rflow tables
rflow tables [--path <dir>]The discoverability command for the query layer:
lists every queryable table in the project's Postgres — the indexed event
tables (rflow_indexer_rflow_<workflow>.* for event triggers,
rflow_indexer_rflow_idx_<contract>.* for
index_events) and
the rflow.* journal — with columns and row counts, plus the naming rule as
a footer. Schemas the config implies but the database does not have yet
(fresh project, indexer not booted) are footnoted as "declared but not
created yet" so nobody mistakes a new project for a broken one.
rflow runs
rflow runs list [--failed] [--limit <n>] # journal: run list, per-step status (default 20, newest first)
rflow runs show <id> # one run in detail: outputs, tx links, attempts, approvals, reorg responses
rflow runs retry <id> # re-fire a dead-lettered run
rflow runs diff <a> <b> [--json] # compare two runs: trigger, step outputs, errors, tx summaries
rflow runs timeline <id> [--json] # chronological step/attempt/approval/tx trace of one runruns diff answers "what changed between a good run and a bad one?" — it lines
up the trigger payloads, per-step outputs, errors and tx summaries of two runs
(both secret-redacted). runs timeline folds one run's steps, attempts,
approval decisions, reorg-response steps and tx lifecycle into a single
time-ordered view — the CLI form of the history explorer's
run detail. Both accept --json for automation.
rflow history
rflow history [--workflow <wf>] [--status <s>] [--trigger <kind>] [--network <net>] \
[--since <24h|7d|RFC3339>] [--session <live|replay|test|any>] \
[--limit <n>] [--cursor <c>] [--json] # a richer `runs list`, keyset-paginated
rflow history txs [--workflow <wf>] [--network <net>] [--status <s>] [--since <7d>] \
[--limit <n>] [--cursor <c>] [--json] # every send attempted (queued/submitted/settled)
rflow history approvals [--workflow <wf>] [--all] [--since <7d>] \
[--limit <n>] [--cursor <c>] [--json] # approvals (pending; --all adds decided/expired)
rflow history waiting [--workflow <wf>] [--limit <n>] [--cursor <c>] [--json]
# runs parked right now (tx / delay / event / approval)
rflow history export [--workflow <wf>] [--since <30d>] [--until <ts>] \
[--format json|csv] [--out <file>] # a redacted audit trail (secrets always stripped)The history explorer surface without the UI —
the same core rflow_core::history
query layer the /api/history/* endpoints use, so the CLI and the UI never
diverge. Every subcommand is keyset-paginated (a --cursor from one page's
next_cursor fetches the next, newest first — never a skip or a duplicate) and
every payload is secret-redacted (declared secrets:, secret-typed config
fields and ${VAR} env values are rendered <redacted>). --json is available
everywhere for automation, and export is redacted by default with no raw-payload
flag.
rflow versions / plan / rollback
rflow versions ls [--json] # deployed config history: id, hash, created, run count
rflow versions show <id> [--json] # a version's summary + per-workflow definitions
rflow versions diff <a> <b> [--json] # semantic delta between two stored versions
rflow plan [--from-version <id>] [--to <path>] [--profile <name>] [--json]
# semantic diff + active runtime conflicts (exits non-zero on any error)
rflow rollback <version-id> [--output <path>] [--yes]
# reconstruct a stored version to rflow.yaml (structure; secrets stay <redacted>)The versioning & config plan surface: every
boot fingerprints the profile-merged, secret-redacted config into
rflow.config_versions and stamps that config_version onto every run
(visible in rflow runs show and rflow ls). rflow plan is the deploy gate
— it flags dangerous changes (money-moving sends, lowered confirmations,
weakened approvals/caps) and the parked work they collide with, and exits
non-zero on any error-severity item for CI.
rflow approve / reject / approvals
rflow approvals ls [--all] # pending approvals (--all includes decided/expired)
rflow approve <id> [--yes] [--token <t>] # approve a parked send (approval id or run id)
rflow reject <id> [--reason "why"] [--token <t>] # reject it (the reason is journaled)
rflow approvals expire <id> [--reason "why"] --yes # expire a pending approval (repair)The human half of approval gates. approve shows the prepared
transaction and asks for confirmation (--yes skips it); an approved send is
re-checked and re-simulated before it broadcasts. On an
N-of-M policy approval your
identity must be PROVEN: pass --token <api-token> (or set
RFLOW_API_TOKEN) — the token's name maps to your
approvers.<member>.cli.token, each member counts once, and the first
rejection vetoes by default. approvals expire is the
repair path for a gate nobody
answered: it prints a plan, needs --yes, and flips a pending approval to
expired so the run takes its on_timeout path (no send is broadcast).
rflow state / cursors / waits / delays / circuit
rflow state inspect [--workflow <wf>] [--json] # durable-state dashboard (read-only)
rflow cursors ls [--workflow <wf>] # trigger cursors
rflow cursors set <wf> <network> --block <n> --yes # re-scan or skip ahead
rflow waits ls [--workflow <wf>] # runs parked on a wait_for:
rflow waits cancel <run-id> --reason "why" --yes # terminalize a stuck saga wait
rflow delays ls [--workflow <wf>] # runs parked on a durable delay
rflow delays wake <run-id> --yes # wake a parked delay early
rflow circuit ls [--workflow <wf>] # circuit-breaker state
rflow circuit reset <workflow> --yes # clear a tripped breakerState inspection & repair — supported,
journal-aware recovery for known stuck states instead of manual SQL. state inspect (and the ls listings) are read-only. Each repair mutation prints a
plan first, requires --yes, refuses on a wrong precondition or a run with
an in-flight send, and writes an rflow.operator_audit row. No repair can create
a duplicate send: repairs terminalize stuck work rather than re-run money-moving
steps, and lowering a cursor re-scans safely (already-claimed events dedupe by
trigger_key).
rflow retention / archive
rflow retention plan # what WOULD be pruned per class (reads only)
rflow retention prune --dry-run # print the plan, delete nothing
rflow retention prune --yes # prune per config.retention (deletion needs --yes)
rflow archive export --since 2026-01-01 [--until 2026-03-01] [--out <dir>] [--format jsonl]
# dump runs+steps+approvals+failures+reorgs+sessions to redacted JSONLOperator-driven history retention, driven
by config.retention. retention plan reports per-class counts + age span
without deleting; retention prune refuses to delete without --yes (it prints
the plan instead) and, with archive_before_prune, exports the archive first
and aborts the whole prune if that export fails. Active runs, pending approvals,
parked waits/delays, in-flight sends and running reorg responses are never
prunable. archive export redacts secrets by default; a relative --out (and
the default ./archives) resolves against the project dir.
rflow budgets ls
rflow budgets ls # per-asset consumed / reserved / remainingShows each spend budget with its cap and, per asset,
consumed (reserved in-flight + spent settled), reserved, and remaining
inside the current rolling window — read straight from rflow.budget_reservations.
rflow spend
rflow spend [--workflow <wf>] [--relayer <r>] [--network <net>] \
[--since <30d|24h|RFC3339>] [--json]
# grouped summary: gas + value by workflow / relayer / network,
# totals + reverted-loss subtotal
rflow spend export [--format csv|json] [--workflow <wf>] [--relayer <r>] \
[--network <net>] [--since <30d>]
# raw ledger entries to stdout (stable CSV columns; exact wei)The operator view over the receipt-derived
native spend ledger (rflow.native_spend):
what rflow actually paid in gas and moved in native value, per settled send.
Human tables format amounts as the native unit; --json and export keep
stdout pure and carry exact wei as decimal strings. Reverted sends appear
with their burned gas as the reverted loss subtotal (a revert moves no
value). The same numbers back GET /api/spend, the explorer's Spend view and
the max_gas_spend
budget circuit breaker.
rflow relayers
rflow relayers ls # every name → wallet mapping (incl. orphans), live balances
rflow relayers balance [--json] # balances per chain with low-balance flags
rflow relayers sync # standalone reconcile: create+clone wallets, print funding table
rflow relayers rename <old> <new> # re-label a mapping WITHOUT creating a new wallet
rflow relayers discover [--json] # list relayers in the shared rrelayer db + signer compatibility
rflow relayers import <name> --id <uuid> --network <net>
# adopt an existing relayer (derived address verified FIRST)
rflow relayers import --interactive # pick from discover, prompt for the local name
rflow relayers verify-imports # ok/mismatch/not-found per import.id — non-zero on failure (CI)
rflow relayers funding-plan [--json] # balance vs estimated need per relayer/network
rflow relayers qr <name> [--network <net>] [--address-only]
# the address + a terminal QR code (fund from a wallet app)
rflow relayers topup-plan [--json] # underfunded relayers and their shortfalls
rflow relayers topup --from <name> --to "*"|<name> --network <net> \
[--amount <wei> | --to-target] --yes
# fund shortfalls from a funding relayer (plan first, --yes to send)
rflow relayers history [--since 30d] [--relayer <r>] [--json]
# recent gas spend per relayer + top-up actionssync lets you create and fund wallets before ever running a workflow. rename is
the answer to the rename honesty check.
The import flow adopts
relayers created by other services sharing the rrelayer database — a signer
mismatch refuses before anything is written. The
funding operations estimate need from a
documented gas-headroom heuristic (workflow count × headroom sends × safety ×
live gas price × typical gas — every factor overridable), and topup broadcasts
normal relayer sends that land in the
spend ledger + operator audit trail. funding-plan,
topup-plan and topup accept --typical-gas, --sends-per-workflow and
--safety-factor to tune the estimate.
rflow token
rflow token create --name <label> # mint a named API bearer token — prints the plaintext ONCE, stores only its hash
rflow token revoke <id> # auth stops accepting the token immediately
rflow token ls # id / name / created / revoked — never the token itselfNamed bearer tokens for config.server.auth.
Auth accepts the config shared token and any non-revoked named token, so you
can hand out per-operator/per-dashboard tokens and revoke a leaked one without a
restart or a yaml edit. Only the hex SHA-256 is stored — the plaintext is shown
once at creation. These commands need only config.db_connection, not a running
server.
rflow secrets
rflow secrets ls # name / source (env | provider:<name>) / remote key / resolved? / version — never values
rflow secrets doctor # verify every declared secret resolves; exit non-zero on any provider failureInspect the secrets: map and verify
external secret provider resolution without ever
printing a value. ls shows where each secret comes from and whether it
resolves (provider secrets are fetched to report the resolved column and the
provider's version id); doctor fetches every provider-backed secret and exits
non-zero when any is missing, denied or unreachable — the same fail-closed
resolution rflow start performs at boot, and the same per-secret checks
rflow doctor runs in its deep suite. Neither command needs the database or a
running server — only rflow.yaml (+ its .env).
rflow workflow
rflow workflow pause <workflow> # kill-switch — persisted in Postgres
rflow workflow resume <workflow>rflow list
rflow list add <list> <value> # persists in rflow.list_members — visible to the next where: evaluation
rflow list remove <list> <value> # removes a runtime member (YAML seeds are config — edit rflow.yaml)
rflow list show [<list>] # members with provenance (seed/manual/workflow/source) + countsLive mutation of lists: watchlists.
Addresses are stored lowercased; membership checks stay case-insensitive.
rflow mcp
rflow mcp [--path <dir>]Serves the Model Context Protocol over stdio so AI agents and editors can operate the project: validate, inspect the journal, simulate calls, trigger/pause/ retry — with no raw send tool, ever.
rflow replay
rflow replay --helpReplay a workflow over historical blocks in an isolated session (dry-run by default), or manage replay/test sessions (`ls` / `prune`)
Usage: rflow replay [OPTIONS] [WORKFLOW]
rflow replay <COMMAND>
Commands:
ls List replay and test sessions (id, workflow, kind, created, run count)
prune Delete replay/test sessions: their rows, indexer schemas and runtime dirs
help Print this message or the help of the given subcommand(s)
Arguments:
[WORKFLOW]
The workflow to replay (event- or block-triggered)
Options:
-p, --path <PATH>
optional - The path of the project, default will be where the command is run
--from-block <FROM_BLOCK>
First block of the bounded range
--to-block <TO_BLOCK>
Last block of the range (default: the network's current head, pinned at start)
--with-waits <WAITS.json>
Deterministic saga wait outcomes: a JSON map of wait-step id -> {"event": …} | {"timeout": true}
--diff <OTHER_PROJECT_DIR>
Diff this range against another project's same-named workflow (prints the delta)
--output <OUTPUT>
Output format for CI: human | json | junit
Possible values:
- human: Human-readable tables (default)
- json: A single structured JSON object on stdout
- junit: A JUnit `<testsuite>` XML on stdout (GitHub Actions / GitLab)
[default: human]
--live
DANGER: boot the relayer engine and send REAL transactions while replaying (default is dry-run: simulate + report, never send)
--yes
Skip the --live confirmation prompt
--timeout <TIMEOUT>
Session budget in seconds (bounded backfill + run drain)
[default: 600]
-h, --help
Print help (see a summary with '-h')
-V, --version
Print versionBacktest an event- or block-triggered workflow over a historical block range
in an isolated session — dry-run by default (simulate + report, never send).
--live sends real transactions and demands you type send at a red prompt
(--yes skips it). --with-waits pins saga wait_for
outcomes; --diff compares two project versions over the same range (non-zero
exit on difference); --output json|junit emits a CI report, and the process
exits non-zero when any replay run failed. Full guide:
Backtesting.
rflow replay ls / prune
rflow replay ls --helpList replay and test sessions (id, workflow, kind, created, run count)
Usage: rflow replay ls [OPTIONS]
Options:
-p, --path <PATH> optional - The path of the project, default will be where the command is run
-h, --help Print help
-V, --version Print versionrflow replay prune --helpDelete replay/test sessions: their rows, indexer schemas and runtime dirs
Usage: rflow replay prune [OPTIONS]
Options:
--older-than <DUR> Prune sessions created longer ago than this duration (e.g. 7d, 12h)
-p, --path <PATH> optional - The path of the project, default will be where the command is run
--session <ID> Prune one session by id
--all Prune every replay/test session
-h, --help Print help
-V, --version Print versionls lists every replay/test session (DB rows ∪ orphan runtime dirs). prune
deletes the selected sessions — namespaced journal rows, per-session rindexer
schemas and runtime dirs — and never touches live workflows. It requires a
selector (--older-than 7d / --session <id> / --all).
rflow test
rflow test --helpExecute one workflow once in dry-run mode from a fixture or a real transaction
Usage: rflow test [OPTIONS] <WORKFLOW>
Arguments:
<WORKFLOW>
The workflow to execute once
Options:
-p, --path <PATH>
optional - The path of the project, default will be where the command is run
--event <FIXTURE.json>
Path to an EVENT fixture json: {"args": {...}, "block_number"?, ...}
--from-tx <TX_HASH>
Build the trigger from a real transaction hash (fetches the receipt and decodes the matching trigger event)
--fixture <FIXTURE.json>
Path to a fixture json for the workflow's OWN trigger kind (cron/webhook/read/query/block/event)
--cron-at <ISO8601>
Cron workflows: the scheduled tick (ISO8601), sugar for a {scheduled_for} fixture
--read-output <OUTPUT.json>
Read/query workflows: a json file with the decoded {output}, sugar for --fixture
--with-waits <WAITS.json>
Deterministic saga wait outcomes: a JSON map of wait-step id -> {"event": …} | {"timeout": true}
--output <OUTPUT>
Output format for CI: human | json | junit
Possible values:
- human: Human-readable tables (default)
- json: A single structured JSON object on stdout
- junit: A JUnit `<testsuite>` XML on stdout (GitHub Actions / GitLab)
[default: human]
--timeout <TIMEOUT>
Run budget in seconds
[default: 120]
-h, --help
Print help (see a summary with '-h')
-V, --version
Print versionExecute one workflow once in dry-run mode from exactly one trigger source
(--event / --from-tx / --fixture / --cron-at / --read-output are
mutually exclusive). --fixture interprets the file against the workflow's
declared trigger kind (event/cron/webhook/read/query/block). --with-waits
pins saga wait outcomes; --output json|junit makes it a CI check — the
process exits non-zero when the run did not succeed. --timeout bounds the
run (default 120s). Full guide:
Backtesting.
rflow command test
rflow command test <workflow> <step-id> [--input <fixture.json>] [--json]Debug a single command step in isolation. The step
renders exactly as a run would — --input supplies the dynamic context roots
(trigger, steps, state, …); the static roots
(constants/secrets/contracts/lists) come from rflow.yaml — then the
command runs for real and prints the rendered invocation, the
secret-redacted stdin, the duration, and the parsed output (or the taxonomy
failure). Nothing is signed, sent, journaled, or persisted, and no database is
needed; the process exits non-zero when the command fails, so a fixture doubles
as a CI check. --json emits a single machine-readable transcript.