Community registry
The template registry has two tiers in one repo, behind one trust gate (PR review into master):
- builtin — the first-party set, compiled into the binary (offline bootstrap; everything on the registry page).
- community — packages under
templates/community/at the repo root. They are not in the binary: the CLI discovers and installs them over the GitHub API, so a template merged today is installable by every rflow binary already in the wild.
rflow templates ls --community # the remote index
rflow new --template community/large-approval-alert
rflow add workflow community/large-approval-alertThe interactive rflow new picker also ends with a "community template
(fetched from the registry)" entry that fetches the index and lists the
community ids.
The trust model
Distribution is pinned, never live — trust never attaches to a branch, only to reviewed, hashed content:
- PR-reviewed. The only way into the registry is a PR merged into master. A community package is reviewable by construction: manifest, typed inputs, YAML templates and (surface-verified) ABI files — no code, no hooks.
- SHA-pinned. Every install resolves the branch to a COMMIT first,
then fetches
index.jsonand every package file at that commit (raw.githubusercontent.com/<owner>/<repo>/<commit>/…). A force-pushed or rewritten history cannot change what anyone installs. - Hash-verified.
index.jsonpins a sha256 for EVERY file of every package (snapshots and READMEs included). Each downloaded byte is re-hashed before it is kept; a mismatch aborts the install and keeps nothing. Verified downloads are cached (~/.rflow/cache, override withRFLOW_CACHE_DIR) and re-hashed on every use — a tampered cache file is re-downloaded, never trusted. SetGITHUB_TOKENto raise the API rate limit. - Immutable versions. CI refuses any change to a published package's
files without a
template_versionbump — and enforces the same rules against the base branch, so regeneratingindex.jsoninside a PR cannot smuggle in a rewrite. Version downgrades fail. Removing an id fails: templates retire viadeprecated: truein the manifest (still installable, labeled inls/show, installs with a warning). - The full local gate re-runs at install. Before anything renders, the installing CLI runs the community doctor profile, verifies every declared interface surface, and prints a provenance banner (registry, commit, risk, author). Merged review is necessary, not sufficient — your own binary re-proves the package.
Install UX
community/<id> is explicit; a plain id that no builtin claims also falls
back to the community index. Builtin names are reserved — a community
package can never shadow one. Every community install prints its
provenance before rendering:
community template 'large-approval-alert' v1.0.0 (risk: monitor_only)
registry: github:joshstevens19/rflow@master
commit: 3f2a9c…
author: rflow maintainersForks and private registries
--registry github:owner/repo[@branch] points the SAME mechanism at any
fork — the org-internal/private sharing story with zero extra machinery:
rflow templates ls --community --registry github:acme/rflow-templates
rflow new --template community/acme-vault-alert --registry github:acme/rflow-templatesNon-default registries are loudly labeled UNTRUSTED — nothing in them
was reviewed by the rflow maintainers — and require an explicit
confirmation (--yes in scripts, a prompt on a TTY).
Lock provenance
Community installs extend
.rflow/template-lock.yaml
with the full provenance — what was installed, from where, pinned to which
commit, hashing to what:
templates:
- id: large-approval-alert
template_version: 1.0.0
rflow_version: 0.1.0
installed_at: "2026-08-08T12:00:00Z"
answers:
network: ethereum
# …
source: community
registry: github:joshstevens19/rflow@master
commit: 3f2a9c1d…
files:
template.yaml: 6797d64b… # sha256 per packaged file
rflow.yaml.tmpl: bc7c730b…Builtin installs keep the exact pre-community entry shape — none of the new keys appear.
Declared interfaces
A template bundling an opaque ABI blob is unreviewable — and a poisoned ABI
encodes different calldata. interfaces: in template.yaml fixes that:
the manifest declares the EXACT function/event signatures the workflows
use, and whatever the ABI source yields must carry those signatures
(checked by recomputed selectors and topic0s) or the template fails
doctor and refuses to install.
interfaces:
token:
abi:
wellknown: erc20 # curated builtin standard - ships NO blob;
functions: [] # the install materializes abis/token.json
events:
- "Approval(address,address,uint256)"
pool:
abi:
fetch: # resolved at install time from TYPED inputs
address_from: pool_address # an input of type address
network_from: network # an input of type network
functions:
- "getUserAccountData(address)"
settlement:
abi:
file: settlement # bundled abis/settlement.json - acceptable
functions: # ONLY because it is machine-verified
- "fill(bytes32,bytes,bytes)"
events:
- "Open(bytes32,(address,uint256))"Exactly one source per interface — wellknown: (curated builtin standards:
erc20 and friends), fetch: (from typed address/network inputs, never
literals), or file: (a bundled blob, verified against the declaration).
fetch:/file: interfaces must declare a non-empty surface — an
unverifiable declaration is refused.
endpoints: declares HTTP egress the same way — a fixed host: or a
host_from: input, plus which requires.secrets entry travels there:
endpoints:
report:
host_from: report_host
secret: REPORT_HMACrflow templates show <id> prints the complete call + egress surface
before anything installs.
Submitting a template
The process is deliberately boring, like an EIP — one directory, one PR, one mechanical review:
# 1. scaffold a compliant skeleton (inside a checkout of the rflow repo)
rflow templates scaffold my-vault-alert
# 2. fill it in: template.yaml (author:, inputs, interfaces), rflow.yaml.tmpl,
# README.md, answers.sample.yaml - then generate the snapshot
rflow templates snapshot my-vault-alert --dir templates/community/my-vault-alert
# 3. regenerate the index (this also runs the community doctor gate over
# every package) and prove it is current
rflow templates index
rflow templates index --dir . --check
# 4. open the PR - CI re-runs the whole gate, one maintainer approval mergesThe scaffolded skeleton passes the full community doctor profile the moment
its snapshot exists, so you start from green. The full checklist lives in
templates/community/SUBMITTING.md.
Note: RFLOW_REGENERATE_TEMPLATE_SNAPSHOTS=1 cargo test -p rflow_core regenerate_template_snapshots only regenerates the EMBEDDED first-party
snapshots — community packages regenerate with rflow templates snapshot.
The community profile
CI (and every install) enforces the first-party doctor checks PLUS the community rules — all mechanical, none advisory:
| rule | why |
|---|---|
author: is mandatory | an EIP-style process puts a name on every proposal |
no command: steps, anywhere in the step tree | a community template may not ship arbitrary execution |
no literal addresses in prepares_tx / money_moving templates | destinations are typed inputs the installer answers (the zero address stays allowed as a placeholder) |
every bundled ABI blob is covered by a file: interface | the blob is machine-verified against the declared signatures — better, ship no blob (wellknown: / fetch:) |
| typed inputs for every installer choice; defaults validate | answers are type-checked before rendering |
outputs.files equals exactly what the package produces | materialized interface ABIs (abis/<name>.json) count as produced |
sample render passes rflow validate and matches the committed snapshot | byte for byte |
a docs page exists for docs.page | checked in-repo by CI (skipped by installing binaries) |
Deprecated templates (deprecated: true) stay in the catalog and stay
installable — the index is immutable — but are labeled in ls/show and
install with a warning.