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 shippableshow 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.yamlAnswer 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 nothingadd 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:
| situation | behavior |
|---|---|
| identical entry already exists | reused silently (reported as =) |
| same contract name, same ABI, template's addresses ⊆ existing | reused with a warning |
| same name, different value | hard error — rename one side |
| workflow name taken | error 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.