Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Scaffolding & config wizard

rflow is YAML-first — the whole project lives in a rflow.yaml you can read, diff, and review. The scaffold wizard makes the first-run details (RPCs, networks, contracts, relayers, signers, notifications) fast to get right, without ever taking the file out of your hands.

Three commands cover the whole lifecycle:

rflow new / rflow init

rflow new [--output <dir>] [--name <project-name>] [--template <id>] [--yes]
          [--answer key=value ...] [--answers-file answers.yaml]

Interactive on a TTY: a picker lists the template registry — 17 production-shaped recipes, each with an id, a risk label and a summary — then asks one typed prompt per template input (addresses are checksum-validated, crons parsed, amounts checked; a bad answer is re-asked). On a non-TTY, or with --yes, manifest defaults fill every unanswered input (the default template is large-transfer-alert). It writes the template's files (rflow.yaml, ABIs, .env + .env.example, docker-compose.yml with postgres on localhost:5448, .gitignore and a .rflow/template-lock.yaml install record), then runs rflow validate and prints the exact next steps.

--template <id> picks a template directly — rflow templates ls to browse, rflow templates show <id> for its inputs, --answer key=value / --answers-file to drive it non-interactively (CI/agents). Legacy scaffold ids (transfer-alert, cron-report, treasury-approval, liquidation-keeper) resolve to their registry equivalents; webhook-relay and blank still use the pre-registry renderer.

A template that requires a signer (the money_moving ones) scaffolds a DEV-ONLY generated mnemonic into .env — never into .env.example — so the project validates and boots out of the box; swap in a real signer before production. rflow new refuses to overwrite an existing rflow.yaml and leaves an existing .env untouched, so it is safe to re-run in a directory that already has secrets. See Using templates for the full walkthrough, including rflow add workflow composition.

rflow add — grow an existing project

rflow add [--path <dir>] network       ...
rflow add [--path <dir>] contract      ...
rflow add [--path <dir>] relayer       ...
rflow add [--path <dir>] notification  ...

rflow new gets you a project; rflow add grows it. Each subcommand appends one entry to the matching top-level section (networks:, contracts:, relayers:, notifications.channels:).

It preserves your file byte-for-byte

add locates the right section and splices the rendered snippet at the end of that block. It does not parse the whole document to a value and re-serialize it — that would strip your comments and reflow your formatting. On a money config that matters. Comments, key ordering, spacing, and the trailing newline all survive; the only change to the file is the lines that were added. If the section is absent, it is created; if it is present-but-empty, the first entry fills it.

One expectation: add targets the conventional 2-space indentation style rflow new scaffolds. If a section uses another style (zero-indented list items, 4-space indents), the add is refused with the file untouched and an error that says so — re-indent the section or add the entry by hand.

It validates the merge and refuses to break the file

Before writing, add:

  1. rejects a duplicate name (a network/contract/relayer/channel that already exists),
  2. does a light shape check (contract address form, relayer network references), then
  3. validates the merged config in memory and refuses (exit 1, file untouched) any change that would introduce a hard validation error.

So rflow add relayer on a project with no signer: block is refused — a relayer with nowhere to sign does not validate — and the message tells you why. Errors that were already present in the file before your add are surfaced but do not block (you asked to add a thing; a pre-existing problem elsewhere is not yours to fix right now). Every successful add runs rflow validate and prints the next command(s), including a rflow doctor hint.

The duplicate-name and reference checks need the current rflow.yaml to parse, so a ${VAR} placeholder in a strictly-numeric field like chain_id (which never validates) produces a "current config is not valid" message instead of an append — fix the field first, then add.

Interactive, or flag-driven for CI and agents

On a TTY every subcommand prompts (a network-preset picker, a channel-kind menu, env-var prompts). For CI, agents, and non-TTY shells, pass flags. A non-TTY run with a required flag missing errors with exactly which flags are needed.

# a network (preset or custom); --rpc takes a url or a ${ENV} placeholder
rflow add network --name base --chain-id 8453 --rpc '${BASE_RPC}' --confirmations 12
 
# a contract, single-network or multi-network
rflow add contract --name USDC --abi ./abis/erc20.json --network base --address 0x8335...2913
rflow add contract --name USDC --abi ./abis/erc20.json --addresses ethereum=0xA0b8...eB48,base=0x8335...2913
 
# no ABI on disk yet? `rflow contract add` fetches the verified one first
# (sourcify -> etherscan) and then runs the same add - see /config/abis
rflow contract add USDC --network ethereum --address 0xA0b8...eB48
 
# a relayer (needs a signer already configured, else the add is refused)
rflow add relayer --name payout --networks ethereum,base --speed FAST
 
# a notification channel — one kind flag, plus the env-var names to read secrets from
rflow add notification --name ops --slack --webhook-url-env SLACK_WEBHOOK_URL
rflow add notification --name pager --telegram --bot-token-env TG_BOT_TOKEN --chat-id-env TG_CHAT_ID

--path is global — it works before or after the subcommand.

Env-var flags take the variable name, not the secret. --webhook-url-env SLACK_WEBHOOK_URL renders webhook_url: ${SLACK_WEBHOOK_URL} into the YAML, and you set the real value in .env. When you pass only the kind flag, sensible defaults are used:

ChannelFlagsDefault env var(s)
--telegram--bot-token-env, --chat-id-envTG_BOT_TOKEN, TG_CHAT_ID
--slack--webhook-url-envSLACK_WEBHOOK_URL
--discord--webhook-url-envDISCORD_WEBHOOK_URL
--pagerduty--routing-key-envPAGERDUTY_ROUTING_KEY
--opsgenie--api-key-env [--eu]OPSGENIE_API_KEY
--twilio--account-sid-env, --auth-token-env, --from, --toTWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN

rflow doctor — local readiness

rflow doctor [--path <dir>] [--profile <name>] [--deep] [--json]

A lean readiness table for the local dependencies rflow needs:

rflow doctor
 dependency | status | detail
 docker     | ok     | Docker version 28.5.2, build ecc6942
 anvil      | ok     | anvil Version: 1.6.0-v1.7.0
 cast       | ok     | cast Version: 1.6.0-v1.7.0
 node       | ok     | v25.9.0
 postgres   | ok     | reachable
 rflow.yaml | ok     | valid - 1 workflow(s), 0 warning(s)
  • Docker — daemon reachable (docker info).
  • Foundryanvil and cast on PATH, with versions.
  • Postgres — reachable via the project's config.db_connection (when a project is present).
  • Node — optional, for the docs site.
  • rflow.yaml — the project's validate summary (workflows + warnings), when a project is found.

Each row is ok / missing / n/a, and any non-ok row prints a one-line "how to install or start" hint.

When a project is present, the readiness table is followed by the deep operational suite — profile merge, DB schema, RPC chain-id/capability probes, contract sanity, relayer mappings and balances, channels, webhooks, approval posture, and coverage notes. Exit code: non-zero only when an error-severity check fails; warnings, advice and info never gate. The full check catalogue, severities and the CI gating pattern live in Doctor & explain. (For strict collect-all config errors, use rflow validate.)

A full first run

rflow new --name payments --template treasury-sweep-approval --yes   # scaffold a sending project
docker compose up -d                                     # postgres on localhost:5448
rflow add network --name base --chain-id 8453 --rpc '${BASE_RPC}'
rflow add contract --name USDC --abi ./abis/erc20.json --network base --address 0x8335...2913
rflow doctor                                             # readiness check
rflow validate                                           # strict, collect-all
rflow start                                              # boot the engines