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

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.

FieldDescription
seedInitial members, owned by the YAML
sourceRefresh 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.