Configuration
The facilitator is configured via a YAML file (default: config.yaml in the working directory). Pass a custom path with the --config flag.
go run cmd/main.go --config /etc/x402/config.yamlA fully-annotated example is available at config.example.yaml.
server
server:
host: "0.0.0.0" # Bind address
port: ":8080" # Listen port (include the colon)
environment: "production" # development | staging | production | test| Field | Required | Default | Description |
|---|---|---|---|
host | Yes | - | Network interface to bind. Use 0.0.0.0 to accept all interfaces. |
port | Yes | - | Port including the colon prefix, e.g. ":8080". |
environment | Yes | - | Controls log verbosity and Gin's debug/release mode. One of development, staging, production, test. |
database
PostgreSQL connection settings.
database:
host: "localhost"
port: 5432
name: "x402db"
user: "x402user"
password: "secret"
ssl_mode: "disable" # disable | require | verify-ca | verify-full
cert_path: "" # Path to CA cert (required for verify-ca/verify-full)| Field | Required | Description |
|---|---|---|
host | Yes | PostgreSQL hostname or IP. |
port | Yes | PostgreSQL port. Must be 1–65535. |
name | Yes | Database name. |
user | Yes | Database user. |
password | Yes | Database password. |
ssl_mode | Yes | One of disable, require, verify-ca, verify-full. |
cert_path | No | Path to the CA certificate when ssl_mode is verify-ca or verify-full. |
redis
Redis connection settings (used for caching and rate limiting).
redis:
address: "localhost:6379"
password: ""
db: 0| Field | Required | Description |
|---|---|---|
address | Yes | Redis host:port. |
password | No | Redis password (leave empty if not set). |
db | No | Redis database index (0–15). Default 0. |
jwt
JSON Web Token signing secrets and expiry durations.
jwt:
access_token_secret: "long-random-string"
refresh_token_secret: "another-long-random-string"
access_token_expiry: "15m"
refresh_token_expiry: "168h"| Field | Required | Description |
|---|---|---|
access_token_secret | Yes | HMAC secret for signing access tokens. Must be non-empty. |
refresh_token_secret | Yes | HMAC secret for signing refresh tokens. Must be non-empty. |
access_token_expiry | Yes | Go duration string (e.g. "15m", "1h"). Minimum 1 minute. |
refresh_token_expiry | Yes | Go duration string. Minimum 1 minute. Must be ≥ access_token_expiry. |
Best practice: Use at least 32 random bytes (256 bits) for token secrets. Generate with
openssl rand -hex 32.
security
security:
client_secret: "long-random-string"| Field | Required | Description |
|---|---|---|
client_secret | Yes | HMAC key used to sign client API tokens issued via POST /api/v1/clients. |
admin
admin:
enabled: true
token: "your-admin-token"| Field | Required | Description |
|---|---|---|
enabled | No | Enable the /admin route group. Default false. |
token | Yes (if enabled) | Static bearer token for admin endpoints. |
x402
x402 protocol behaviour settings.
x402:
default_timeout: "30s"
retry_count: 3
log_level: "info"
enable_metrics: true
allow_missing_client_auth: false| Field | Default | Description |
|---|---|---|
default_timeout | "30s" | Default request timeout for chain RPC calls. |
retry_count | 3 | Number of retries for failed settlement broadcasts. |
log_level | "info" | Log verbosity: debug, info, warn, error. |
enable_metrics | true | Record per-client x402 metrics to the database. |
allow_missing_client_auth | false | If true, /verify and /settle work without client API keys. Useful for testing; disable in production. |
rate_limit
rate_limit:
enabled: true
requests_per_minute: 100| Field | Default | Description |
|---|---|---|
enabled | false | Enable per-IP rate limiting. |
requests_per_minute | 100 | Maximum requests per IP per minute. |
monitoring
monitoring:
metrics_enabled: true
health_check_interval: "30s"| Field | Description |
|---|---|
metrics_enabled | Expose Prometheus-compatible metrics (future). |
health_check_interval | How often to run internal health probes. |
logging
logging:
level: "info"
format: "json"| Field | Description |
|---|---|
level | Log level: debug, info, warn, error. |
format | Log format: json (structured) or text (human-readable). |
networks
Define which blockchain networks the facilitator supports. Each network must have an enabled wallet (private key + fee payer address) so the facilitator can sign and broadcast settlement transactions.
Structure
networks:
evm:
- network_name: "base-sepolia"
...
cosmos:
- network_name: "osmosis"
...
solana:
- network_name: "solana-devnet"
...Network fields
| Field | Required | Description |
|---|---|---|
network_name | Yes | Unique identifier used in payment requirements (e.g. "base-sepolia"). |
enabled | No | Whether this network is active. Default false. |
chain_id | No | Chain ID (numeric string for EVM, descriptive for Cosmos/Solana). |
rpc | Yes | HTTP RPC endpoint for the chain. |
grpc | No | gRPC endpoint (Cosmos only). |
websocket | No | WebSocket RPC endpoint (optional). |
accepted_denom | Yes | Token denom/symbol used for payment matching. |
decimals | Yes | Token decimal places (e.g. 6 for USDC). |
private_key | Yes | Hex-encoded private key of the facilitator's settlement wallet. |
fee_payer | Yes | Public address of the settlement wallet (used as fee payer / feegrant granter). |
EVM example (Base Sepolia)
networks:
evm:
- network_name: "base-sepolia"
enabled: true
chain_id: "base-sepolia"
rpc: "https://sepolia.base.org"
accepted_denom: "USDC"
decimals: 6
private_key: "0xYourPrivateKey"
fee_payer: "0xYourAddress"Cosmos example (Osmosis)
networks:
cosmos:
- network_name: "osmosis"
enabled: true
chain_id: "osmosis-1"
rpc: "https://rpc.osmosis.zone"
grpc: "grpc.osmosis.zone:443"
accepted_denom: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA84"
decimals: 6
private_key: "YourCosmosPrivateKeyHex"
fee_payer: "osmo1YourAddress"Solana example
networks:
solana:
- network_name: "solana-devnet"
enabled: true
chain_id: "solana-devnet"
rpc: "https://api.devnet.solana.com"
accepted_denom: "USDC"
decimals: 6
private_key: "YourBase58EncodedPrivateKey"
fee_payer: "YourSolanaPublicKey"Full Example
See the full annotated config.example.yaml in the repository.
Environment Variables
The facilitator does not currently support environment-variable overrides - all configuration is file-based. For secrets management in production, consider using Docker secrets or a secrets manager that writes files (e.g. Vault Agent, AWS Secrets Manager with file sink) and mount config.yaml into the container.