Configuration
Every environment variable the app reads, with its scope, default, validation and effect.
Public variables (NEXT_PUBLIC_*) are inlined into the client bundle at build time, so a
change needs a rebuild. Server-only variables are read at runtime.
For local development, copy .env.example to .env.local. The Docker image takes the public
variables as build arguments and the server variables from its environment; see
Self-hosting.
Public
| Variable | Default | Effect |
|---|---|---|
NEXT_PUBLIC_STELLAR_NETWORK | testnet | The starting network: mainnet or testnet. The header selector switches between them at runtime |
NEXT_PUBLIC_DEPLOYMENT_MODE | reference | reference runs the mediator. self-hosted does not: a close that needs it (a registry exchange or a SEP-29 memo-required destination) is refused, leaving direct closes |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID | unset | A Reown project id. When set, WalletConnect joins the wallet picker. When unset it is left out and the relay is never contacted |
NEXT_PUBLIC_SITE_URL | https://demolisher.app | The canonical origin for page metadata, the sitemap and robots.txt. A trailing slash is trimmed |
NEXT_PUBLIC_SLIPPAGE_BPS | 100 (1%) | Slippage tolerance in basis points for every market conversion in a close: the classic path-payment and pool-withdraw floors and the Soroban exit and swap floors. A whole number from 10 to 500 |
NEXT_PUBLIC_MIN_MARKET_OUT_STROOPS | 0 | A floor in stroops for a conversion's XLM output. A balance whose slippage-adjusted output is at or below it is not sold, and goes to its issuer or to the destination instead. A whole number, 0 or more |
NEXT_PUBLIC_LUMEUS_TESTNET_KEY | unset | A Lumeus publishable key (lum_pub_...) for testnet. When set, the allowance, held-token and Aquarius scans also read the account's whole history from Lumeus, from the browser. When unset they run on the RPC's seven-day window |
NEXT_PUBLIC_LUMEUS_MAINNET_KEY | unset | The same for mainnet |
Validation
A blank value counts as unset for every public variable, because an unset Docker build argument arrives as an empty string.
NEXT_PUBLIC_STELLAR_NETWORK and NEXT_PUBLIC_DEPLOYMENT_MODE only accept the values listed. Any
other value throws when the app reads its configuration:
Invalid public environment configuration: <detail>NEXT_PUBLIC_SLIPPAGE_BPS and NEXT_PUBLIC_MIN_MARKET_OUT_STROOPS are checked when next.config.ts
loads, so a malformed or out-of-range value fails next build and next dev with a
SlippageConfigError naming the variable. Only unset or blank uses the default.
NEXT_PUBLIC_LUMEUS_TESTNET_KEY and NEXT_PUBLIC_LUMEUS_MAINNET_KEY accept only a publishable
key, matching lum_pub_ followed by letters, digits and underscores. The key ships in the client
bundle, so a full or read key would hand every visitor the workspace's access. next.config.ts
checks both when it loads, and any other value fails next build and next dev with:
NEXT_PUBLIC_LUMEUS_TESTNET_KEY must be a Lumeus publishable key (lum_pub_...): it is inlined into the pageThe error never prints the value. See Self-hosting for creating a key.
Server-only
Never expose these to the client.
| Variable | Default | Effect |
|---|---|---|
MEDIATOR_SECRET | unset | A Stellar seed (S...) used only as the HMAC master that derives a fresh mediator keypair per exchange close and signs flow tokens. It is never an account: do not fund it. Needed only in reference mode |
MEDIATOR_ALLOWED_ORIGIN | unset | Comma-separated origins allowed to call the mediator routes cross-origin. Unset means same-origin only |
TRUSTED_PROXY_HOPS | 1 | How many reverse proxies in front of the app append to X-Forwarded-For. The rate limiter keys on the entry that many places from the right. A whole number, at least 1 |
METRICS_ADMIN_TOKEN | unset | The bearer token for GET /api/metrics. Unset, or shorter than 16 characters, makes that read answer 503. Recording metrics does not need it |
METRICS_DB_DIR | data/ under the working directory | Where the metrics store keeps closes.jsonl and counters.json, and the mediator its co-sign log, cosign.jsonl. Must be writable. The Docker image sets /app/data |
Validation
The server configuration module imports server-only, so importing it from a client bundle is a
build error. An empty string counts as unset.
TRUSTED_PROXY_HOPSis checked when the server starts (src/instrumentation.ts). A value of0or anything that is not a whole number logsrefusing to start: <reason>and exits. The app is supported only behind a reverse proxy.MEDIATOR_SECRETmust start withSand be 56 characters, or be unset. The full ed25519 seed check runs at first use. A bad value disables only the mediator routes, which answerMEDIATOR_NOT_CONFIGUREDand log the reason with any seed redacted.METRICS_ADMIN_TOKENshorter than 16 characters disables only the metrics read, which answers 503.
A bad value for one of these never disables another feature.
Build and runtime switches
| Variable | Effect |
|---|---|
NODE_ENV | Set by Next. Selects the development or production CSP, turns HSTS on in production, and in development lets localhost:3000, :3001 and :3002 call the mediator routes cross-origin |
The only supported target is the server build (next build, then next start). The API routes
and the per-request CSP need a running server, so there is no static export.
Endpoints are not environment variables
Horizon and Soroban RPC endpoints are pinned per network in src/lib/config/networks.ts. Editing
that file is how you point the app at other providers, and the CSP follows it. See
Networks and endpoints.
Outbound connections from the server
The server itself contacts only Horizon, from /api/plan (the account's signers and sequence),
/api/mediator/reissue (whether the mediator exists) and /api/metrics (close verification). It
calls no other third-party API.
Related
Self-hosting walks through install, run, Docker and the reverse proxy.
Audit and review status
The state of the external audit, the internal pre-audit review and its remediation by severity and status, the FxDAO removal, and the dependency posture.
Networks and endpoints
Horizon, Soroban RPC, passphrase and friendbot per network, how RPC failover works, and how the app picks a network.