Railguard

Configuration

Complete reference for the railguard.toml configuration file.

Railguard is configured via a TOML file, typically named railguard.toml. The file supports environment variable expansion using ${VAR_NAME} syntax.

Full Example

[server]
port = 8545
max_request_size = 1048576      # 1MB
request_timeout_ms = 30000      # 30 seconds
 
[upstream]
url = "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
 
[firewall]
mode = "strict"                 # or "monitor"
fail_on_decode_error = true     # Block if ABI decoding fails
 
[firewall.global_limits]
max_value = "10000000000000000000"       # 10 ETH
max_gas_price = "100000000000"           # 100 gwei (legacy)
max_fee_per_gas = "100000000000"         # 100 gwei (EIP-1559)
max_priority_fee_per_gas = "10000000000" # 10 gwei
 
[[firewall.rules]]
name = "USDC Safety"
contract = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
allow_methods = [
    "approve(address,uint256)",
    "transfer(address,uint256)"
]
arg_constraints = [
    { index = 1, max = "1000000000" }  # 1000 USDC
]

Server Configuration

[server]
port = 8545                  # Listen port (default: 8545)
max_request_size = 1048576   # Max request body in bytes (default: 1MB)
request_timeout_ms = 30000   # Request timeout in ms (default: 30s)
FieldTypeDefaultDescription
portu168545Port for the local proxy server
max_request_sizeusize1048576Maximum JSON-RPC request size in bytes
request_timeout_msu6430000Timeout for upstream requests

Upstream Configuration

[upstream]
url = "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"
FieldTypeRequiredDescription
urlStringYesUpstream RPC endpoint URL

Environment Variable Expansion

Use ${VAR_NAME} to inject environment variables:

[upstream]
url = "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"

Firewall Configuration

[firewall]
mode = "strict"
fail_on_decode_error = true
FieldTypeDefaultDescription
modeString"strict""strict" blocks violations, "monitor" logs only
fail_on_decode_errorbooltrueBlock if ABI decoding fails (fail closed)

Modes

  • strict — Block transactions that violate policy (default)
  • monitor — Log violations but allow transactions through

Monitor mode is useful for:

  • Testing your policy before enforcing
  • Auditing transaction patterns
  • Gradual rollout

Global Limits

[firewall.global_limits]
max_value = "10000000000000000000"       # 10 ETH in wei
max_gas_price = "100000000000"           # 100 gwei (legacy/EIP-2930)
max_fee_per_gas = "100000000000"         # 100 gwei (EIP-1559/4844)
max_priority_fee_per_gas = "10000000000" # 10 gwei
FieldTypeDescription
max_valueU256Maximum ETH value per transaction (in wei)
max_gas_priceU256Max gas price for legacy transactions
max_fee_per_gasU256Max fee per gas for EIP-1559 transactions
max_priority_fee_per_gasU256Max priority fee for EIP-1559 transactions

All values are strings representing U256 integers. Use quotes: "10000000000000000000".

Contract Rules

[[firewall.rules]]
name = "USDC Safety"
contract = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
allow_methods = [
    "approve(address,uint256)",
    "transfer(address,uint256)"
]
arg_constraints = [
    { index = 1, max = "1000000000" }
]
FieldTypeRequiredDescription
nameStringYesHuman-readable rule name
contractAddressYesContract address (checksummed)
allow_methodsString[]YesAllowed function signatures
arg_constraintsArgConstraint[]NoArgument value limits

Method Signatures

Specify methods in canonical Solidity format:

allow_methods = [
    "transfer(address,uint256)",
    "approve(address,uint256)",
    "transferFrom(address,address,uint256)"
]

Important:

  • No spaces after commas
  • No parameter names
  • Use canonical types (uint256 not uint)

Railguard pre-computes the 4-byte selector at startup for fast matching.

Argument Constraints

Limit numeric argument values:

arg_constraints = [
    { index = 0, max = "1000000000000000000" },  # First arg <= 1 ETH
    { index = 1, max = "100" }                   # Second arg <= 100
]
FieldTypeDescription
indexusizeZero-based argument index
maxU256Maximum allowed value

Arguments are decoded using ABI encoding rules. Only uint types are currently supported for constraints.

Contract Creation

To allow contract creation (transactions with no to address):

[[firewall.rules]]
name = "Allow Deployments"
contract = "0x0000000000000000000000000000000000000000"
allow_methods = []

Setting contract to the zero address matches transactions with no destination.

ETH Transfers

Simple ETH transfers (no calldata) are checked against the destination allowlist. If the to address matches any rule's contract, the transfer is allowed.

[[firewall.rules]]
name = "Treasury"
contract = "0xYourTreasuryAddress"
allow_methods = []  # Empty = allow simple transfers

Multiple Rules

Rules are evaluated in order. A transaction must match at least one rule to be allowed:

[[firewall.rules]]
name = "DEX Router"
contract = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"
allow_methods = [
    "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"
]
 
[[firewall.rules]]
name = "USDC"
contract = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
allow_methods = ["approve(address,uint256)"]

CLI Override

Override config file values via CLI:

# Override port
rg -c railguard.toml --port 8546 run -- forge script Deploy.s.sol
 
# Use different config
rg -c production.toml run -- ./my-script

Next Steps

On this page