Environment Configuration Reference
RitoSwap validates every environment variable as the app boots (see app/config/public.env.ts and app/config/server.env.ts). Incorrect or incomplete settings throw immediately, and some flags change behavior across the entire stack (state worker enablement, JWT signing, Cloudflare worker routing, etc.). Use this page to understand each flag, its default, and the conditional rules enforced by the validators.
Public Environment (NEXT_PUBLIC_*)
These variables ship to the browser, so the schema (lines 32‑99) enforces strict typing before exporting publicEnv and publicConfig. Defaults are safe for local development—even if a variable is omitted, the parser fills in deterministic values.
All public flags run through a custom BoolEnv transformer. Strings such as yes, 1, on, or true become true; everything else (including empty strings) becomes false.
| Variable | Default | Notes / Conditional Rules |
|---|---|---|
NEXT_PUBLIC_ENABLE_STATE_WORKER | false | Master switch for SIWE + rate limiting. When true, the server also requires STATE_WORKER_URL and STATE_WORKER_API_KEY (see server table). |
NEXT_PUBLIC_ACTIVE_CHAIN | sepolia | Determines runtime chain selection (ethereum, sepolia, ritonet). If set to ritonet, the fields below become mandatory. |
NEXT_PUBLIC_DOMAIN | localhost:3000 | Comma‑delimited host allowlist for SIWE + legacy auth. |
NEXT_PUBLIC_LOCAL_CHAIN_ID | Unset | Required when NEXT_PUBLIC_ACTIVE_CHAIN=ritonet. Used to derive CHAIN_IDS.ritonet. |
NEXT_PUBLIC_LOCAL_BLOCKCHAIN_NAME | RitoNet | Display name for local network / chain info provider. |
NEXT_PUBLIC_LOCAL_BLOCKCHAIN_RPC | Unset | Required when running on RitoNet. Must be a valid URL. |
NEXT_PUBLIC_LOCAL_BLOCKCHAIN_WSS | Unset | Required when running on RitoNet. Validated via regex for ws:// or wss://. |
NEXT_PUBLIC_ALCHEMY_API_KEY | Unset | Optional. Enables Alchemy RPCs for Ethereum/Sepolia; otherwise the app uses public RPCs. |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID | Unset | Optional. When missing, the server logs a warning because WalletConnect UI is disabled. |
NEXT_PUBLIC_APP_NAME | App Name | Used by ConnectKit / wallet UI metadata. |
NEXT_PUBLIC_APP_DESCRIPTION | App Description | Displayed alongside the app name in wallet prompts. |
NEXT_PUBLIC_SW | false | Enables the service worker / PWA layer. Surfaced via publicConfig.features.serviceWorker. |
NEXT_PUBLIC_LOG_LEVEL | debug (dev) / warn (prod) | Optional literal (debug, info, warn, error). Falls back to environment‑aware defaults. |
NEXT_PUBLIC_LOCAL_NOTIFICATIONS | true | Controls browser notification prompts inside the dapp. |
Derived Helpers
publicEnv– frozen object containing all validated values (lines 148‑150).publicConfig– friendly helpers exposed to both server and client (chain, log level, feature flags).
Server Environment (serverEnv)
The server validator (app/config/server.env.ts) runs before any server component executes. It throws immediately if a critical variable is missing and emits warnings for optional but recommended values (e.g., R2 config).
| Variable | Default / Requirement | Notes / Conditional Rules |
|---|---|---|
DATABASE_URL | Required | App refuses to boot without it. |
STATE_WORKER_API_KEY, STATE_WORKER_URL | Optional unless NEXT_PUBLIC_ENABLE_STATE_WORKER=true | Both must be present once the public flag is enabled; otherwise validation fails. |
BREVO_API_KEY, SENDER_EMAIL, RECEIVER_EMAIL | Optional | Without them, the code logs warnings and skips production email delivery. |
USE_CLOUDFLARE_WORKER | false | Parser coerces strings/numbers to booleans. If true, CLOUDFLARE_WORKER_URL must be defined. |
CLOUDFLARE_WORKER_URL | Optional | Required only when USE_CLOUDFLARE_WORKER evaluates to true. |
R2_API_* | Optional | Missing values trigger warnings because uploads/signing won’t work. |
BACKDOOR_TOKEN | false | If true, TOKEN_ID must be set. In production, BACKDOOR_ADDRESS must also be defined. |
JWT_ALG | HS256 | Determines which signing material is required (see below). |
JWT_SECRET | Required when JWT_ALG=HS256 | Validator warns if the secret is shorter than 32 characters. |
JWT_PRIVATE_KEY, JWT_PUBLIC_KEY | Required when JWT_ALG is EdDSA or ES256 | Validator checks for PEM markers and throws if either key is missing. |
JWT_ISS | Required (URL) | Issuer claim for minted access tokens. |
JWT_AUD | Required (comma list) | Must contain at least one audience; parsed into serverConfig.jwt.aud. |
JWT_ACCESS_TTL | 900 seconds | Positive integer TTL for gate access tokens. |
JWT_CLOCK_TOLERANCE | 5 seconds | Allowable clock skew passed to downstream JWT verification. |
serverConfig Helpers
The module exports a frozen serverConfig object that downstream code can safely import:
serverConfig.email– indicates whether Brevo credentials exist and whether worker delegation is enabled.serverConfig.stateService– exposesisActive,url, andapiKeyfor the Durable Object client.serverConfig.jwt– shares non-secret metadata such as issuer, audiences, TTL, algorithm, and booleans indicating whether signing material is present (without exposing the secret/private key).
Runtime Validation Workflow
app/config/validate.ts ties everything together. Importing the module triggers no work; you must explicitly call validateEnvironment() during server startup (for example, in app/layout.tsx before rendering the shell).
Key details:
- Server-only – the function throws if invoked in the browser.
- Run-once guard – it stores a promise on
globalThis.__ENV_VALIDATION_PROMISE, preventing duplicate validation/log spam under React Strict Mode. - What it does:
- Dynamically imports
server.env.ts,public.env.ts,chain.ts, andai.server.ts, forcing each file’s zod schema to run. - Builds a “startup configuration” summary (environment, feature flags, chain RPC metadata, AI quota settings, infra health).
- Logs the summary via
@loggerand emits production-only warnings if something isn’t configured (email missing, Alchemy key absent, backdoor enabled, AI quota misconfigured, etc.). - Throws if any import fails, ensuring deployment halts before serving traffic.
- Dynamically imports
Recommended usage:
// app/layout.tsx
import { validateEnvironment } from '@/app/config/validate'
export default async function RootLayout({ children }) {
await validateEnvironment() // Fail fast if env is misconfigured
return (
<html lang="en">
<body>{children}</body>
</html>
)
}If your deployment uses a custom server entry point, call the validator before bootstrapping the Next handler. This guarantees every environment change gets revalidated on cold start.
Deployment Checklist
- Decide on your primary chain. If you stick with the Sepolia default, no extra envs are required. For RitoNet, set every
NEXT_PUBLIC_LOCAL_BLOCKCHAIN_*field plus matching RPC endpoints in other workspaces. - Enable the state worker by setting
NEXT_PUBLIC_ENABLE_STATE_WORKER=trueand providingSTATE_WORKER_URL/STATE_WORKER_API_KEY. - Configure JWT signing material appropriate for your chosen algorithm.
- Choose an email delivery mode:
- Brevo inline: set
BREVO_API_KEY,SENDER_EMAIL,RECEIVER_EMAIL, leaveUSE_CLOUDFLARE_WORKER=false. - Worker delegation: set
USE_CLOUDFLARE_WORKER=trueand provideCLOUDFLARE_WORKER_URL(the worker itself needs the Brevo secrets via Wrangler).
- Brevo inline: set
- (Optional) Wire Cloudflare R2 for gated audio downloads.
Keeping these contracts in sync with their validators ensures your deployment fails fast when configuration drifts, instead of producing subtle runtime bugs.
Security Schemas (shared Zod helpers)
app/config/security.public.ts centralizes the low-level validation used by every API. Refer to these specs when implementing clients or new endpoints.
| Helper | Definition | Usage |
|---|---|---|
NONCE_BYTES, NONCE_ENCODING, NonceSchema | 16 bytes, encoded as lowercase hex (32 chars). Regex enforced via ^[0-9a-fA-F]{32}$. | /api/nonce, /api/gate-access (SIWE flow) – clients must send the exact hex string returned by the nonce endpoint. |
SignatureSchema | 65-byte ECDSA signature (0x + 130 hex chars). Compact 64-byte signatures are disabled unless ALLOW_COMPACT_SIGNATURES is flipped. | Gate access & form-submission requests. Sending a shorter signature triggers a 400 before any cryptographic checks run. |
AddressSchema | Strict 0x[a-fA-F0-9]{40} regex with lowercase normalization. | All API bodies that accept an address (gate access, form submission, token status) reuse this schema to prevent mixed-case drift. |
TokenIdInputSchema / TokenIdStringSchema | Accepts either a number or numeric string (for JSON bodies) and coerces to number. Path params use the string-only variant. | Gate access, form submission, token status routes, and any CLI tooling that needs to accept human-friendly inputs. |
🥡 Takeaway: Instead of duplicating regexes in every route, always import these schemas. That way, updating nonce or signature rules happens in one place and the request DTOs automatically inherit the stricter contract.
Chain Metadata Helpers
Multi-network UI components reuse the pure TypeScript helpers in app/schemas/domain/chains.ts. That file intentionally avoids touching env state so it can be imported by client utilities (Chain Info Provider, ConnectKit config, MCP tools):
SupportedChainSchema– a Zod enum listing every chain the UI can mention (mainnet,sepolia,polygon,arbitrum,avalanche,base,optimism,fantom,ritonet). Use it whenever you accept arbitrary strings from user input or query params.CHAIN_DISPLAY_NAMES/formatChainName()– canonical casing for badges and dropdowns (“RitoNet”, “Sepolia”, etc.). Keeps marketing copy consistent with the environment selectors described earlier.CHAIN_NATIVE_SYMBOLS– lightweight map for showing the correct ticker (ETH,MATIC,AVAX, …) alongside balances.isSupportedChain()– type guard you can run before calling more expensive chain-specific logic.
Because the helpers live in app/schemas/domain, they can be shared between the docs site, MCP integrations, and the dapp without risking accidental bundling of env-sensitive config.