forked from x402-rs/x402-rs
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy path.env.example
More file actions
370 lines (333 loc) · 18.3 KB
/
Copy path.env.example
File metadata and controls
370 lines (333 loc) · 18.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# Server Configuration
HOST=0.0.0.0
PORT=8080
# Facilitator URL - Used for self-registration in Bazaar discovery registry
# Set to the public URL of this facilitator to enable self-discovery
# Example: https://facilitator.ultravioletadao.xyz
FACILITATOR_URL=
# Bazaar Discovery Persistence (S3)
# When set, discovery registrations are persisted to S3 and survive restarts
# Leave empty for in-memory only (registrations lost on restart)
DISCOVERY_S3_BUCKET=
DISCOVERY_S3_KEY=bazaar/resources.json
# Meta-Bazaar Discovery Aggregation
# When enabled, periodically fetches resources from external facilitators (Coinbase, etc.)
# This populates your Bazaar with services from the broader x402 ecosystem
DISCOVERY_ENABLE_AGGREGATION=true
# How often to aggregate from external facilitators (in seconds, default: 3600 = 1 hour)
DISCOVERY_AGGREGATION_INTERVAL=3600
# Single owner for the periodic discovery work (A4)
# ONE task per cluster aggregates, probes and publishes the catalog; the others
# serve reads and follow the snapshot it writes. Elected with the same lease as
# the EVM writer, in the same DynamoDB table (NONCE_STORE_TABLE_NAME).
# Only a process ECS is running stands in the election: without ECS task
# metadata this is a no-op and every job runs locally, as it always did.
# Set to false to go back to every task running every job. That is the
# break-glass for a prolonged DynamoDB outage: with the lease ON and the table
# unreachable from every task, NOBODY aggregates (deliberately) and the log says
# so with `discovery_owner_unreachable`.
ENABLE_DISCOVERY_LEASE=true
# How often a task that does NOT own the jobs checks whether the catalog moved
# (seconds, default 60). Cheap: a HEAD, and a read only when the ETag changed.
DISCOVERY_REFRESH_INTERVAL=60
# Discovery Crawler (Phase 3)
# When enabled, periodically crawls /.well-known/x402 endpoints from seed URLs
# Discovered resources are registered with source: Crawled
DISCOVERY_ENABLE_CRAWLER=false
# How often to crawl (in seconds, default: 86400 = 24 hours)
DISCOVERY_CRAWL_INTERVAL=86400
# Comma-separated list of URLs to crawl for /.well-known/x402
# Example: https://api.example.com,https://data.service.io
DISCOVERY_CRAWL_URLS=
# Health prober budget. Every probe is a TLS handshake, so this is CPU, not an
# I/O wait: max_rps * tick is the probes issued per tick (2 * 60 = 120). It was
# 20 rps / 40 concurrent, i.e. 1200 handshakes a minute, which on a one-vCPU
# task is what the 60-100 % bursts every minute were.
DISCOVERY_HEALTH_MAX_RPS=2
DISCOVERY_HEALTH_CONCURRENCY=8
# Minimum seconds between uploads of bazaar/health.json. It was written on every
# tick -- 5.0 MB a minute, 7 GB a day -- and nothing reads it but a starting task.
DISCOVERY_HEALTH_PERSIST_SECS=300
# Demand-driven revalidation (P2). Every one of these is defined ONCE, in
# src/discovery_config.rs, and the value a running task resolved is published at
# GET /discovery/config.
#
# The load rule: a revalidation does NOT add a probe. It changes which probe the
# next tick spends its existing allowance on (DISCOVERY_HEALTH_MAX_RPS times
# DISCOVERY_HEALTH_TICK). Nothing below can raise background load.
DISCOVERY_ENABLE_REVALIDATION=true
# Percent of the per-tick allowance reserved for the periodic sweep, so a busy
# resource cannot starve the quiet ones.
DISCOVERY_REVALIDATION_LONG_TAIL_PCT=40
# Coalescing window: repeat requests for one resource inside it are one job.
DISCOVERY_REVALIDATION_WINDOW=300
DISCOVERY_REVALIDATION_QUEUE_CAP=500
DISCOVERY_REVALIDATION_DEMAND_CAP=50
# Probes one host may receive from the demand queue in a single tick.
DISCOVERY_REVALIDATION_PER_HOST=2
# Backoff after an origin refuses. An origin's own Retry-After wins when it
# sends one; these bound what we do when it does not.
DISCOVERY_REVALIDATION_BACKOFF_BASE=60
DISCOVERY_REVALIDATION_BACKOFF_MAX=3600
# The cross-replica hand-off, a string set in the lease table.
DISCOVERY_REVALIDATION_CLAIM_MAX=100
DISCOVERY_REVALIDATION_SHARED_CAP=500
DISCOVERY_REVALIDATION_SHARED_TTL=3600
# Catalog capacity. The in-memory catalog is held whole, cloned whole on every
# import and scanned whole on every listing, so its size is the task's sizing.
# Measured 2026-09-10 on real objects, one scenario per process: ~22 KB of
# process RSS per record. 20000 = 440 MB, 5000 = 119 MB, 2000 = 54 MB, and the
# 752-record baseline this service ran on for months = 25 MB. Raise it when the
# task is raised, not before. 0 disables the bound.
# Eviction is by provenance first: only aggregated copies are dropped (they can
# be re-fetched), oldest write first. A self-registered, settlement or crawled
# record is never evicted.
DISCOVERY_MAX_RESOURCES=2000
# Most items pulled from ONE source in one aggregation cycle. Bounds the peak
# DURING a cycle; DISCOVERY_MAX_RESOURCES bounds what survives it. Was a
# hard-coded 50000, chosen when the biggest source answered with 752 items.
DISCOVERY_MAX_ITEMS_PER_SOURCE=1000
# Observed payment terms overlay
# What the origin's live 402 actually advertises, read by the health prober and
# kept in its OWN S3 object -- never inline on the resource, so an import can
# never overwrite a direct reading. Only used when DISCOVERY_S3_BUCKET is set.
DISCOVERY_TERMS_S3_KEY=bazaar/terms.json
# How long a reading counts as current (seconds, default: 604800 = 7 days).
# Defaults to the healthy re-probe cadence on purpose: "fresh" has to mean
# "observed within the policy we actually run".
DISCOVERY_TERMS_FRESH_SECS=604800
# Minimum seconds between two flushes of the overlay (default: 300)
DISCOVERY_TERMS_PERSIST_SECS=300
# Most readings retained, oldest evicted first. Tracks DISCOVERY_MAX_RESOURCES:
# the overlay is keyed by catalog URL and pruned against the live set, so it can
# never hold more readings than there are resources.
DISCOVERY_TERMS_MAX_RECORDS=2000
# Transaction timeout (seconds) - how long to wait for tx confirmation
# OPTIONAL: Override default network-specific timeouts
# Defaults: Base=60s, All other EVM chains=30s
# TX_RECEIPT_TIMEOUT_SECS=60
# Signer Configuration
SIGNER_TYPE=private-key
# Blockchain Private Keys (NEVER commit actual keys!)
# Production: Leave empty - will be fetched from AWS Secrets Manager
# Development: Set network-specific keys OR use generic keys for backward compatibility
# RECOMMENDED: Separate keys per environment
EVM_PRIVATE_KEY_MAINNET=
EVM_PRIVATE_KEY_TESTNET=
SOLANA_PRIVATE_KEY_MAINNET=
SOLANA_PRIVATE_KEY_TESTNET=
STELLAR_PRIVATE_KEY_MAINNET=
STELLAR_PRIVATE_KEY_TESTNET=
SUI_PRIVATE_KEY_MAINNET=
SUI_PRIVATE_KEY_TESTNET=
# DEPRECATED: Generic keys (backward compatibility only - not recommended)
# If network-specific keys are not set, these will be used for ALL networks
EVM_PRIVATE_KEY=
SOLANA_PRIVATE_KEY=
# RPC URLs - Mainnets
RPC_URL_BASE=https://mainnet.base.org
RPC_URL_AVALANCHE=https://api.avax.network/ext/bc/C/rpc
RPC_URL_CELO=https://celo-rpc.quickapi.com
RPC_URL_HYPEREVM=https://rpc.hyperevm.com
RPC_URL_POLYGON=https://polygon-rpc.com
RPC_URL_OPTIMISM=https://mainnet.optimism.io
RPC_URL_SOLANA=https://api.mainnet-beta.solana.com
RPC_URL_STELLAR=https://soroban-rpc.mainnet.stellar.gateway.fm
RPC_URL_XDC=https://rpc.xinfin.network
RPC_URL_SEI=https://evm-rpc.sei-apis.com
RPC_URL_ETHEREUM=https://ethereum-rpc.publicnode.com
RPC_URL_ARBITRUM=https://public-arb-mainnet.fastnode.io
RPC_URL_UNICHAIN=https://unichain-rpc.publicnode.com
RPC_URL_MONAD=https://rpc.monad.xyz
RPC_URL_XRPL_EVM=https://rpc-evm.xrpl.org
RPC_URL_FOGO=https://rpc.fogo.nightly.app
RPC_URL_SUI=https://sui-rpc.publicnode.com
RPC_URL_SKALE_BASE=https://skale-base.skalenodes.com/v1/base
RPC_URL_SCROLL=https://rpc.scroll.io
RPC_URL_ROBINHOOD=https://rpc.mainnet.chain.robinhood.com
# RPC URLs - Testnets
RPC_URL_BASE_SEPOLIA=https://sepolia.base.org
RPC_URL_AVALANCHE_FUJI=https://api.avax-test.network/ext/bc/C/rpc
RPC_URL_CELO_SEPOLIA=https://forno.celo-sepolia.celo-testnet.org
RPC_URL_HYPEREVM_TESTNET=https://rpc.hyperliquid-testnet.xyz/evm
RPC_URL_POLYGON_AMOY=https://rpc-amoy.polygon.technology
RPC_URL_OPTIMISM_SEPOLIA=https://sepolia.optimism.io
RPC_URL_SOLANA_DEVNET=https://api.devnet.solana.com
RPC_URL_STELLAR_TESTNET=https://soroban-testnet.stellar.org
RPC_URL_SEI_TESTNET=https://evm-rpc-testnet.sei-apis.com
RPC_URL_ETHEREUM_SEPOLIA=https://ethereum-sepolia-rpc.publicnode.com
RPC_URL_ARBITRUM_SEPOLIA=https://arbitrum-sepolia-rpc.publicnode.com
RPC_URL_UNICHAIN_SEPOLIA=https://unichain-sepolia.drpc.org
RPC_URL_FOGO_TESTNET=https://testnet.fogo.io
RPC_URL_SUI_TESTNET=https://sui-testnet-rpc.publicnode.com
RPC_URL_SKALE_BASE_SEPOLIA=https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha
RPC_URL_ROBINHOOD_TESTNET=https://rpc.testnet.chain.robinhood.com
# Arc testnet (Circle). Chain id 5042002; USDC 0x3600...0000 is the native gas
# token AND the ERC-20 being paid, over one balance at two precisions (18 / 6).
#
# COMMENTED OUT ON PURPOSE. This variable is the network's on/off switch: with
# it unset, `EvmProvider::from_env` returns `Ok(None)` and Arc is served by
# nothing on that instance. Uncomment to run Arc locally; production uses the
# independent arc_*_enabled Terraform flags and requires a funded signer first.
# Testnet faucet: https://faucet.circle.com (not mainnet funds).
# Arc mainnet has its own chain ID (5042), domain and mainnet signer.
# Both are opt-in; absent variables mean absent /supported entries.
#RPC_URL_ARC=https://rpc.mainnet.arc.io
#RPC_URL_ARC_TESTNET=https://rpc.testnet.arc.io
# Premium RPC (Optional - for higher rate limits)
QUICKNODE_BASE_RPC=
# Solana Compute Budget Limits (optional, defaults applied if unset)
# These control the maximum compute units and price the facilitator will accept
X402_SOLANA_MAX_COMPUTE_UNIT_LIMIT_SOLANA=400000
X402_SOLANA_MAX_COMPUTE_UNIT_LIMIT_SOLANA_DEVNET=200000
X402_SOLANA_MAX_COMPUTE_UNIT_PRICE_SOLANA=1000000
X402_SOLANA_MAX_COMPUTE_UNIT_PRICE_SOLANA_DEVNET=100000
# OpenTelemetry Configuration
OTEL_SERVICE_NAME=facilitator
# --- Local observability stack (docker-compose.observability.yml) ---
# Uncomment to send telemetry to the local OTel Collector:
#OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
#OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
# --- Honeycomb (cloud alternative) ---
# Uncomment to send telemetry to Honeycomb instead:
#OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io:443
#OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your_api_key,x-honeycomb-dataset=x402-rs
#OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
# ERC-8004 Trustless Agents Integration (Optional)
# Set these addresses when ERC-8004 contracts are deployed on-chain
# Production: Store in AWS Secrets Manager and reference via valueFrom
# If not set, ERC-8004 features are disabled (uses placeholder addresses)
ERC8004_IDENTITY_REGISTRY=
ERC8004_REPUTATION_REGISTRY=
ERC8004_VALIDATION_REGISTRY=
# Is the facilitator still allowed to sign Solana feedback AS the author?
# DEPRECATED path. Account 0 of the program's give_feedback instruction is the
# `client` (feedback author) and POST /feedback puts OUR keypair there, so the chain
# records the facilitator as the author of somebody else's opinion -- 87,2% of the
# feedback on Base is attributed to our wallet for the same reason.
# The replacement is POST /feedback/solana/prepare + /feedback/solana/submit, where
# the rater signs as `client` and we only pay the fee.
# Default true so existing integrations keep working; set false to close the old path.
ERC8004_ALLOW_FACILITATOR_AUTHORSHIP=true
# How long a rater's EIP-7702 relay authorisation stays valid (seconds, default 900).
# Short on purpose: FeedbackDelegate.relayFeedback is permissionless by design (that is
# what lets us sponsor it), so a signed authorisation is live in the wild until its
# deadline. Minutes, not forever. Relayed feedback is only served where a delegate has
# been deployed AND verified on-chain -- the table is delegate_address() in
# src/erc8004/relay.rs (today base, ethereum, polygon, arbitrum, optimism, celo, bsc,
# monad, arc and base-sepolia).
ERC8004_RELAY_DEADLINE_SECS=900
# Proof-of-payment gate for POST /feedback (anti-sybil).
# The registry lets ANY address rate ANY agent, so without a gate the only thing
# rationing reputation is the fact that the facilitator is the one signing.
# Rollout is two-phase and this is phase 1:
# false (default) -> every check runs, the verdict is logged and returned in the
# response `proof` field, and nothing is rejected. This is how
# you MEASURE how much real traffic a hard gate would break.
# true -> a failing proof is rejected with 400 and a bounded reason.
# Two verdicts never block a write in either phase: an unreachable RPC ("no verdict",
# our outage must not erase somebody's reputation) and a Solana feedback (the payment
# half of the gate reads EVM receipts and has no SVM equivalent yet).
ERC8004_REQUIRE_PROOF=false
# How old a payment may be and still buy a rating (seconds, default 604800 = 7 days).
# Also the TTL of the anti-replay record, deliberately: once a proof is too old to be
# accepted it no longer needs a replay record to stop it.
ERC8004_PROOF_MAX_AGE_SECS=604800
# Admin bearer token for POST /feedback/revoke (NO default, fail-closed)
# The registry authorises revokeFeedback by msg.sender -- which is the facilitator --
# so this endpoint can erase any feedback the registry attributes to the facilitator
# wallet, permanently and for third parties. Leave EMPTY and the route answers 404,
# indistinguishable from a route that does not exist.
# Deliberately NOT BAZAAR_ADMIN_TOKEN: different blast radius, different credential.
# Production: leave empty here and inject from AWS Secrets Manager.
ERC8004_ADMIN_TOKEN=
# x402r Escrow/Refund Extension (DepositRelay system)
# Set to true to enable escrow settlement via DepositRelay contracts
# This is the simpler escrow system using proxy contracts
ENABLE_ESCROW=false
# x402r PaymentOperator Extension (Advanced escrow with conditions/fees)
# Set to true to enable PaymentOperator settlement (scheme="escrow")
# This is the advanced escrow system with pluggable conditions and fee system
# Deployed on: Base Mainnet (operator 0xa06958D93135BEd7e43893897C0d9fA931EF051C)
ENABLE_PAYMENT_OPERATOR=false
# Signed lifecycle orders for escrow `release` / `refundInEscrow`.
# off = the order is not checked (default; garbage values fall back here)
# log = verify `payload.lifecycleAuth` when present, log the verdict, never reject
# enforce = refuse release/refundInEscrow without a valid order (403; owner_unverifiable is 502)
# Flip to `log` first and watch for `missing` / `unauthorized_role` before `enforce`.
# GET /settle publishes the effective value.
ESCROW_LIFECYCLE_AUTH=off
# Ceiling on how far ahead an order's deadline may sit (seconds, default 900)
# ESCROW_LIFECYCLE_MAX_DEADLINE_SECS=900
# Upto scheme (Permit2-based variable amount settlement for usage-based pricing)
# Set to true to enable the upto payment scheme
# Uses x402UptoPermit2Proxy contract (0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002)
ENABLE_UPTO=false
# Live traffic stream (GET /events, Server-Sent Events)
# One SSE message per verify/settle so observers can render live traffic without
# scraping logs. Lossy by design: it can never slow down or fail a payment.
# All optional — the defaults below are what the code uses when unset.
#
# WARNING: with SCOPE=all + DETAIL=full the stream is PUBLIC and broadcasts the payer,
# tx hash and amount of EVERY client of this facilitator, not just your own. Narrow it
# with DETAIL=minimal ({ts,kind,network,ok} only) or SCOPE=allowlist + ALLOWLIST.
X402_EVENTS_ENABLED=true
X402_EVENTS_SCOPE=all
X402_EVENTS_ALLOWLIST=
X402_EVENTS_DETAIL=full
X402_EVENTS_BUFFER=256
# Concurrent SSE subscribers admitted; at the cap /events returns 503 + Retry-After.
# It is public and unauthenticated, so this bounds observers starving the task that
# settles payments.
X402_EVENTS_MAX_SUBSCRIBERS=64
# Publish operations that ERRORED (RPC down, bad signature, contract revert).
# Default false. While it is false, an errored operation produces neither an
# event nor a stored record, so a 100% success rate on /stats means "no failures
# were recorded" -- NOT "no failures occurred".
# When enabled, failures carry a BOUNDED CATEGORY (contract_revert,
# invalid_signature, ...) and never the error text: raw errors carry addresses
# and sometimes RPC URLs with the API key inside them.
X402_EVENTS_PUBLISH_FAILURES=false
# Historical transaction index (DynamoDB). Unset = nothing is recorded and
# payments are entirely unaffected -- the store is an index, never a ledger.
TRANSACTIONS_TABLE_NAME=
# Days before a record expires. 0 keeps them forever. Aggregate counters never
# expire, so lifetime totals outlive the rows that produced them.
TRANSACTIONS_TTL_DAYS=90
# MCP server (POST /mcp, Streamable HTTP, stateless)
# Host allowlist. rmcp validates the Host header before anything else and answers
# 403 to anything not on the list -- DNS-rebinding protection aimed at MCP servers
# on a laptop. Its own default is loopback ONLY, which behind an ALB rejects every
# real request while local testing looks perfect, so the production host is in the
# built-in default and this variable only exists to override it.
#
# Unset or blank keeps the default:
# facilitator.ultravioletadao.xyz, localhost, 127.0.0.1, ::1
# Comma-separated. An entry may carry a port (`example.com:8080`); one without a
# port matches every port. The single value `*` turns the check off, with a warning.
MCP_ALLOWED_HOSTS=
# Logging
RUST_LOG=info
RUST_BACKTRACE=1
# Hedera native exact x402 v2 (feature: hedera). Independent activation per network.
# Sponsor accounts must already exist, match their private key and hold HBAR.
HEDERA_ENABLED_TESTNET=false
HEDERA_ENABLED_MAINNET=false
# Pause new payments/discovery while retaining recovery of admitted transactions.
HEDERA_ADMISSIONS_ENABLED_TESTNET=true
HEDERA_ADMISSIONS_ENABLED_MAINNET=true
HEDERA_ACCOUNT_ID_TESTNET=
HEDERA_ACCOUNT_ID_MAINNET=
HEDERA_PRIVATE_KEY_TESTNET=
HEDERA_PRIVATE_KEY_MAINNET=
HEDERA_MIRROR_URL_TESTNET=https://testnet.mirrornode.hedera.com/
HEDERA_MIRROR_URL_MAINNET=https://mainnet-public.mirrornode.hedera.com/
HEDERA_SETTLEMENT_TABLE_NAME=facilitator-hedera-settlements
HEDERA_MAX_TRANSACTION_FEE_TINYBARS=100000000
HEDERA_SETTLEMENT_TIMEOUT_SECS=45
# Explicit UTC-day budgets on MAX fees, atomically reserved across replicas.
HEDERA_DAILY_BUDGET_TINYBARS_TESTNET=1000000000
HEDERA_DAILY_BUDGET_TINYBARS_MAINNET=0
# HBAR and native USDC are included. Extra fungible tokens require token-id:decimals.
HEDERA_ADDITIONAL_TOKENS_TESTNET=
HEDERA_ADDITIONAL_TOKENS_MAINNET=