constants, secrets & lists
Three ways to get values into your expressions.
constants
Static values, addressable as ${{ constants.<name> }}:
rflow_version: 1
name: swap-scaler
config:
port: 3940
db_connection: ${DATABASE_URL}
networks:
- name: ethereum
chain_id: 1
rpc: ${ETH_RPC}
contracts:
AnyPool:
abi: ./abis/pool.json
network: ethereum
address: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"
constants:
scale_bps: "2500"
treasury: "0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326"
workflows:
scaled-quote:
trigger:
event: { contract: AnyPool, name: Swap, network: ethereum }
steps:
- id: quote
read:
contract: AnyPool
network: ethereum
function: "quote(uint256)"
args: ["${{ mul_div(trigger.args.amount0, constants.scale_bps, 10000) }}"] secrets
Like constants, but log-redacted: anything under secrets: never appears in
rflow's logs or journal output. Addressable as ${{ secrets.<name> }}.
rflow_version: 1
name: hooked-backend
config:
port: 3940
db_connection: ${DATABASE_URL}
secrets:
HOOK_KEY: ${BACKEND_HMAC_KEY}Two layers: ${BACKEND_HMAC_KEY} is environment substitution at load time;
${{ secrets.HOOK_KEY }} reads it at run time. Typical consumers: an
http_call HMAC and a
webhook trigger's inbound verification:
rflow_version: 1
name: hooked-backend
config:
port: 3940
db_connection: ${DATABASE_URL}
networks:
- name: ethereum
chain_id: 1
rpc: ${ETH_RPC}
contracts:
USDC:
abi: ./abis/erc20.json
network: ethereum
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
secrets:
HOOK_KEY: ${BACKEND_HMAC_KEY}
workflows:
transfer-hook:
trigger:
event: { contract: USDC, name: Transfer, network: ethereum }
steps:
- id: notify-backend
http_call:
url: ${BACKEND_URL}
hmac: "${{ secrets.HOOK_KEY }}"
body: { tx: "${{ trigger.tx_hash }}" } from — external secret providers
A secret's value may be a from: reference that rflow fetches from
AWS Secrets Manager or GCP Secret Manager at boot, so production API
keys never live in .env or the config file:
rflow_version: 1
name: hooked-backend
config:
port: 3940
db_connection: ${DATABASE_URL}
secret_providers:
aws-prod:
aws_secrets_manager:
region: eu-west-1
prefix: /rflow/prod/
secrets:
PRICE_API_KEY:
from:
provider: aws-prod
key: price-api-key
HOOK_KEY: ${BACKEND_HMAC_KEY} # both forms coexist Resolution is fail-closed (a missing/denied secret aborts the boot with the
provider + key named) and a fetched value redacts identically to a declared
one. A project with no secret_providers: behaves exactly as before. Full
provider config, IAM pointers and the rflow secrets ls|doctor commands:
external secret providers.
lists
Runtime-mutable watchlists, backed by Postgres. Seed them in YAML and use them with
the in operator:
rflow_version: 1
name: whale-watch
config:
port: 3940
db_connection: ${DATABASE_URL}
networks:
- name: ethereum
chain_id: 1
rpc: ${ETH_RPC}
contracts:
AnyPool:
abi: ./abis/pool.json
network: ethereum
address: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"
notifications:
channels:
ops:
console: {}
lists:
watched_traders:
seed: ["0xWhale1...", "0xWhale2..."]
workflows:
watched-swaps:
trigger:
event:
contract: AnyPool
name: Swap
network: ethereum
where: "${{ trigger.args.sender in lists.watched_traders }}"
steps:
- id: alert
notify:
channel: ops
message: "watched trader ${{ trigger.args.sender }} swapped (tx ${{ trigger.tx_hash }})"Membership checks against address lists are case-insensitive; see address semantics.
| Field | Description |
|---|---|
seed | Initial members, owned by the YAML |
source | Refresh members from http: / sql: / file: on an interval (see below) |
A list's effective membership is the union of the seed, runtime members
(written by rflow list add and
list_add: / list_remove: steps),
and source-refreshed members. rflow list show breaks members down by
provenance. Mutations are visible to the very next where: evaluation.
source — http / sql / file
rflow_version: 1
name: whale-watch
config:
port: 3940
db_connection: ${DATABASE_URL}
lists:
watched_traders:
seed: ["0xWhale1..."]
source:
http:
url: https://api.example.com/traders
json_path: "$.data[*].address" # default $ — the body is already an array
refresh: 5m # default 5m
vip_vaults:
source:
sql: "SELECT address FROM analytics.vip_vaults" # single read-only SELECT, same Postgres
blocked:
source:
file: ./blocked.txt # newline-separated, # comments skipped #A refresh atomically replaces only the source-tagged rows: seed, manual and workflow members survive every refresh. Values are trimmed, deduplicated and address-shaped entries lowercased.