Operators
Configuration
Every environment variable, its default, and what happens when it is wrong.
Every environment variable the facilitator reads, its default, and what happens when it is wrong.
By the end of this page you will have a complete reference for every knob the facilitator exposes, know which variables are required versus optional, and know which misconfigurations fail the boot rather than degrading silently.
Prerequisites
- A checkout of Vellar-Wallet/vellar-facilitator
- Node 20 or later
- A funded Stellar sponsor account (fund any keypair at
friendbot.stellar.orgon testnet)
If you only want to get a local instance running, start with Run a facilitator and come back here for the details.
1. Required variables
Two variables have no default and no fallback. The facilitator refuses to boot without either of them.
| Variable | Description |
|---|---|
SPONSOR_SECRET_KEY | A funded Stellar classic (S...) secret. Settles payments and pays sponsored fees. Never include it in CHANNEL_ACCOUNT_SECRET_KEYS. |
CHANNEL_ACCOUNT_SECRET_KEYS | Exactly 50 comma-separated Stellar classic (S...) secrets, one per channel account. No default, no fallback. |
⚠️ Exactly 50 keys, not 49 and not 51. A smaller pool loses the collision-free guarantee for 50 concurrent settlements. The count is enforced at boot, not at first collision, so a wrong count is a startup failure rather than a mysterious production incident later.
2. Network
| Variable | Default | Notes |
|---|---|---|
STELLAR_NETWORK | testnet | Only testnet or pubnet. |
STELLAR_RPC_URL | per-network | Overrides the Soroban RPC endpoint. |
PORT | 4100 | HTTP listen port. |
HOST | 0.0.0.0 | HTTP bind address. |
STELLAR_NETWORK is strict. mainnet, PUBNET and stellar:pubnet all fail
the boot. Unrecognised values fail closed rather than silently defaulting to
testnet, so a typo cannot quietly point a production deployment at the wrong
network.
3. Fee ceiling
| Variable | Default | Notes |
|---|---|---|
MAX_TX_FEE_STROOPS | 500,000 | Ceiling on the fee bid before submission. |
MAX_TX_FEE_STROOPS is compared against the bid (minResourceFee plus
BASE_FEE), not the charge. A policy-governed payment bids about 130,000
stroops. The reference x402.org facilitator defaults to 50,000 and rejects
those payments with fee_exceeds_maximum.
⚠️ Do not lower this below 200,000 if you serve smart-account buyers. Policy-governed payments bid well above the reference default, and a ceiling refusal happens before submission, so the buyer sees a rejection rather than a payment. See Fees and sponsorship for the measured numbers behind this.
4. Spend controls
Spend controls are enforced on pubnet and log-only on testnet. On testnet a would-be refusal is logged and the settlement proceeds, so you cannot test your handling of enforcement behaviour there.
| Variable | Default | Notes |
|---|---|---|
SPEND_CEILING_STROOPS | 50,000,000 (5 XLM) | Global rolling ceiling per window across all merchants. |
SPEND_WINDOW_MS | 60,000 | Window length in milliseconds. |
SETTLE_RATE_WINDOW_MS | 60,000 | Shared window for per-entity budgets. |
SETTLE_PER_URL_MAX | 10 | Settlements per window per bound resource URL. |
SETTLE_PER_PAYTO_MAX | 50 | Settlements per window per payTo, half of global capacity. |
SETTLE_UNBOUND_POOL_MAX | 10 | Shared settlements per window for all unbound URLs. |
The four spend-control refusal reasons are rate_limited_payto,
rate_limited_url, spend_ceiling and unbound_pool_exhausted.
Note: The spend ceiling is accounted at the estimate (500,000 stroops per settlement), not the actual charge (about 23,000 stroops for a keypair payment). So the ceiling trips after roughly 100 settlements per window while actually spending about 0.23 XLM of the 5 XLM it names. This is a known open item for pubnet tuning. It fails safe: the error is conservatism, not overspending.
5. Sponsor balance guards
| Variable | Default | Notes |
|---|---|---|
SPONSOR_SOFT_FLOOR_STROOPS | 250,000,000 (25 XLM) | Warns below this. |
SPONSOR_HARD_FLOOR_STROOPS | 100,000,000 (10 XLM) | Refuses /settle below this with settlement_refused: sponsor_balance_low. |
SPONSOR_BALANCE_INTERVAL_MS | 60,000 | How often the sponsor balance is checked. |
⚠️ The hard floor must exceed
SPEND_CEILING_STROOPS. If it does not, the facilitator fails to start on pubnet. A hard floor at or below the ceiling would let a single window's spending carry the sponsor past the floor it is meant to defend.
6. Channel pool
| Variable | Default | Notes |
|---|---|---|
CHANNEL_ACCOUNT_MIN_STROOPS | 5,000,000 (5 XLM) | Reserve floor per channel account. |
Below this floor the account is pulled from the pool, and returned when it is
re-funded. channelPool.available in /health reports how many are currently
usable: 50 means the pool is fully healthy.
7. Catalog persistence
| Variable | Default | Notes |
|---|---|---|
CATALOG_DB_URL | unset (in-memory) | A libSQL URL: file:./data/catalog.db locally, libsql://... for Turso in production. |
CATALOG_DB_AUTH_TOKEN | unset | Required for remote Turso. |
VERIFICATION_API_URL | unset | Base URL of the Vellar verification API. |
Unset CATALOG_DB_URL means nothing survives a restart. Unset
VERIFICATION_API_URL means all verification verdicts are "unknown".
⚠️ libSQL will not create the data directory. Without it you get
ConnectionFailed("./data/catalog.db: 14"), which isSQLITE_CANTOPEN. Runmkdir -p databefore starting.
mkdir -p data
CATALOG_DB_URL=file:./data/catalog.db npm start8. Optional scheme contracts
| Variable | Default | Notes |
|---|---|---|
UPTO_CONTRACT_ID | unset | Set to enable the upto scheme. Must be a valid C... address or the boot fails. |
BOND_ESCROW_CONTRACT_ID | unset | Set to enable bond escrow. Must be paired with BOND_ESCROW_ADMIN_SECRET_KEY. |
BOND_ESCROW_ADMIN_SECRET_KEY | unset | Must be paired with BOND_ESCROW_CONTRACT_ID. |
Setting one half of the bond escrow pair without the other fails the boot rather than starting with escrow half-configured.
Note: Bond escrow is an optional feature that registers each settled payment with an on-chain escrow contract, giving the payer standing to dispute. It is not required for normal operation. Leave both
BOND_ESCROW_*variables unset unless you are specifically building on the dispute and bond feature.
9. Retired variables
These are logged with a loud warning and ignored if set. They do nothing.
| Variable | Why it was retired |
|---|---|
SETTLE_RATE_MAX | Was a second per-payTo budget that shadowed SETTLE_PER_PAYTO_MAX. Set SETTLE_PER_PAYTO_MAX instead. |
CATALOG_OWNERSHIP_BOOTSTRAP | Durable storage removed the ambiguity it resolved. Remove it from your environment. |
CATALOG_FILE | The catalog is now in libSQL. Set CATALOG_DB_URL instead. |
When it fails
Every one of these stops the process at boot. None of them degrade silently.
| Error | Cause | Fix |
|---|---|---|
SPONSOR_SECRET_KEY is required | The variable is not set | Set it to a funded Stellar classic (S...) secret |
CHANNEL_ACCOUNT_SECRET_KEYS is required | The variable is not set | Set it to exactly 50 comma-separated S... secrets |
must contain exactly 50 keys, got N | Wrong number of channel keys | Provide exactly 50, not 49 and not 51 |
contains SPONSOR_SECRET_KEY | The sponsor key appears in the channel list | Remove it: the sponsor is never a channel account |
STELLAR_NETWORK must be "pubnet" or "testnet" | An invalid value such as mainnet, PUBNET or stellar:pubnet | Use exactly testnet or pubnet |
sponsor hard floor does not exceed spend ceiling | SPONSOR_HARD_FLOOR_STROOPS is less than or equal to SPEND_CEILING_STROOPS | Raise the hard floor or lower the ceiling. Fatal on pubnet, a warning on testnet |
UPTO_CONTRACT_ID is not a valid contract address | A malformed C... address | Correct the contract id, or unset it to leave the upto scheme disabled |
BOND_ESCROW_CONTRACT_ID and BOND_ESCROW_ADMIN_SECRET_KEY must be set together | One is set without the other | Set both, or unset both |
ExplorerGitHubOpen Vellar Wallet