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

Using templates

Three commands cover the whole lifecycle: browse the registry, scaffold a new project, compose a template into an existing one.

Browse: rflow templates

rflow templates ls                          # the table: id, category, risk, summary
rflow templates ls --category treasury      # filter by category
rflow templates ls --risk money_moving      # filter by risk label
rflow templates ls --json                   # machine-readable
rflow templates search oracle               # keyword search over id/title/summary
rflow templates show token-deposit-relay    # human detail (files, inputs, requires, safety)
rflow templates inspect token-deposit-relay # the raw manifest as JSON (agents/tooling)
rflow templates doctor token-deposit-relay  # prove the bundled package is shippable

show answers "what happens if I install this" before you install it: the generated files, every typed input with its default, whether it needs a signer/relayers/ABIs/secrets, the safety profile (confirmations, simulation, recheck, approval) and the docs page.

Scaffold: rflow new --template <id>

# interactive: a template picker, then one typed prompt per input
rflow new
 
# direct: prompts only for this template's inputs
rflow new --template treasury-sweep-approval
 
# non-interactive (CI/agents): defaults fill everything not answered
rflow new --template large-transfer-alert --yes --output ./alerts \
  --answer network=base --answer chain_id=8453 --answer min_amount=250000
 
# or drive it from a file of `key: value` scalars
rflow new --template safe-monitor --yes --output ./safe --answers-file answers.yaml

Answer precedence is --answers-file < --answer key=value < interactive prompts; manifest defaults fill whatever remains. Every answer is type-checked before anything is written — addresses are validated (EIP-55 checksums honored on mixed-case), crons parsed, durations/amounts/chain-ids checked, choice inputs constrained to their options, patterns enforced. A bad answer is a clear error naming the key; nothing half-scaffolds.

A successful scaffold writes the files the manifest declares (rflow.yaml, .env.example, .gitignore, docker-compose.yml, ABIs, fixtures, scripts), generates .env from .env.example, runs rflow validate, and prints the next commands. Existing files are never overwritten silently — an existing rflow.yaml refuses the scaffold, an existing .env is left untouched.

Templates that require a signer (money_moving) generate a DEV-ONLY mnemonic into .env (never into .env.example) so the project boots locally — swap in a real signer before production.

Legacy scaffold ids keep working: transfer-alert, cron-report and treasury-approval are manifest aliases; liquidation-keeper resolves to the registry package of the same id; webhook-relay and blank still use the pre-registry renderer.

The lock file

Every install appends to .rflow/template-lock.yaml:

templates:
  - id: large-transfer-alert
    template_version: "1.0.0"
    rflow_version: "0.1.0"
    installed_at: "2026-08-03T12:00:00Z"
    answers:
      network: base
      min_amount: "250000"

It is the audit trail of what was generated from what — and the input a future rflow templates update will diff against. Answers of type secret_name are excluded on purpose.

Compose: rflow add workflow <template-id>

rflow add workflow chainlink-oracle-staleness            # into the current project
rflow add -p ./alerts workflow workflow-error-pager --yes
rflow add workflow large-transfer-alert --name usdt-alert --answer token_address=0x...
rflow add workflow native-spend-report --dry-run         # print the plan, write nothing

add workflow renders the template with your answers, then structurally merges its contracts, relayers, constants, secrets, lists, notification channels and workflow(s) into the existing rflow.yaml — comments, ordering and formatting preserved byte-for-byte outside the appended entries. Packaged files (ABIs, fixtures, scripts) are copied alongside; identical files are skipped, differing ones refuse.

Inputs the project already satisfies are not re-asked: project_name prefills from the config, network/chain_id from the project's single network (a multi-network project gets a picker). networks: and signer: are never merged — an existing project owns its chains and keys; a template that needs a missing network or signer is refused by validation with the exact error.

Collision rules, per entity:

situationbehavior
identical entry already existsreused silently (reported as =)
same contract name, same ABI, template's addresses ⊆ existingreused with a warning
same name, different valuehard error — rename one side
workflow name takenerror demanding --name <new-name>

Multi-workflow templates (safe-monitor splices 4 workflows, timelock-monitor 3, bridge-message-watch 2) add every workflow they declare, in authored order; --name only applies to single-workflow templates.

Before writing, the merged config is validated in memory: any change that would introduce a validation error is refused with the file untouched (errors that a pending file copy resolves — the ABI about to be written — are accounted for). A successful add appends to the lock file, prints which ${VAR} env vars the new entries need, and ends with rflow validate.

Verify: rflow templates doctor

rflow templates doctor <id>     # one template
rflow templates doctor --all    # the whole registry (CI gate)

Checks per template: strict manifest parse, rflow_min_version compatibility, declared outputs exactly match the packaged files, required ABIs packaged, inputs well-formed, 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.