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

The whole project lives in one rflow.yaml you can read, diff, and review. Three commands scaffold and grow it:

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 (27 recipes, each with an id, risk label and 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 (default template: large-transfer-alert). It writes the template's files (rflow.yaml, ABIs, .env + .env.example, docker-compose.yml with postgres on localhost:5448, .gitignore, .rflow/template-lock.yaml), then runs rflow validate and prints the 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 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. 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  ...

Each subcommand appends one entry to the matching top-level section (networks:, contracts:, relayers:, notifications.channels:).

It preserves your file byte-for-byte

add splices the rendered snippet at the end of the matching section; it never re-serializes the document, so comments, key ordering, spacing, and the trailing newline all survive. The only change to the file is the added lines. An absent section is created; a present-but-empty one is filled by the first entry.

add targets the 2-space indentation style rflow new scaffolds. A section in another style (zero-indented list items, 4-space indents) refuses the add with the file untouched. 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, and the message says why. Errors already present in the file are surfaced but do not block. Every successful add runs rflow validate and prints the next command(s), including a rflow doctor hint.

These checks need the current rflow.yaml to parse, so a ${VAR} placeholder in a strictly-numeric field like chain_id 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. For CI, agents, and non-TTY shells, pass flags; a missing required flag 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; set the real value in .env. Passing only the kind flag uses these defaults:

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).
  • Foundry — anvil 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