This project supports multiple smart contract versions and addresses via a centralized configuration accessor.
Client-side environment variables (those prefixed with NEXT_PUBLIC_*) are validated at startup by src/lib/clientEnv.ts using Zod. This ensures that:
- Misconfigured or missing public env vars fail fast with clear error messages
- Invalid URLs are caught before runtime
- Accidental exposure of non-public secrets as
NEXT_PUBLIC_*is prevented - All client config is type-safe through TypeScript
The following NEXT_PUBLIC_* variables are validated by the client env module:
NEXT_PUBLIC_SOROBAN_RPC_URL- Soroban RPC endpoint (URL format validated)NEXT_PUBLIC_NETWORK_PASSPHRASE- Stellar network passphraseNEXT_PUBLIC_COMMITMENT_NFT_CONTRACT- NFT contract addressNEXT_PUBLIC_COMMITMENT_CORE_CONTRACT- Core contract addressNEXT_PUBLIC_ATTESTATION_ENGINE_CONTRACT- Attestation contract addressNEXT_PUBLIC_CONTRACTS_JSON- JSON blob for contract configurationNEXT_PUBLIC_ACTIVE_CONTRACT_VERSION- Active contract versionNEXT_PUBLIC_USE_MOCKS- Mock mode flagNEXT_PUBLIC_APP_URL- Application URL (for CORS)NEXT_PUBLIC_SITE_URL- Site URL (for CORS)
Import and use the validated client env in client-side code:
import { getValidatedClientEnv } from '@/lib/clientEnv';
const clientEnv = getValidatedClientEnv();
const rpcUrl = clientEnv.NEXT_PUBLIC_SOROBAN_RPC_URL;If validation fails, a ClientEnvValidationError is thrown with a clear message indicating which variables are invalid or missing. In development and production, validation happens eagerly at module load time to catch issues early.
Never include secrets in NEXT_PUBLIC_* variables. These are exposed to the browser and should only contain non-sensitive configuration data.
NEXT_PUBLIC_CONTRACTS_JSON(preferred): JSON string mapping versions to contract entries.- Legacy env vars:
NEXT_PUBLIC_COMMITMENT_NFT_CONTRACT,NEXT_PUBLIC_COMMITMENT_CORE_CONTRACT,NEXT_PUBLIC_ATTESTATION_ENGINE_CONTRACTmapped tov1automatically for backward compatibility.
The JSON should be an object where keys are versions and values map contract keys to entries. Each entry may contain:
address(required)network(optional)abi(optional)
Example:
{
"v1": {
"commitmentNFT": { "address": "0xabc..." },
"commitmentCore": { "address": "0xdef..." }
},
"staging": {
"commitmentCore": { "address": "0x123...", "network": "testnet" }
}
}- Add a new key to the JSON (for example
v2) and include the contract entries and addresses. - Optionally set
NEXT_PUBLIC_ACTIVE_CONTRACT_VERSIONto the new version.
- Add and validate the new version in
NEXT_PUBLIC_CONTRACTS_JSONor set equivalent env vars for that version. - Set
NEXT_PUBLIC_ACTIVE_CONTRACT_VERSIONto the desired version. - Restart the application to pick up the new environment variables.
- If
NEXT_PUBLIC_ACTIVE_CONTRACT_VERSIONis not set, the application defaults tov1. - If
NEXT_PUBLIC_CONTRACTS_JSONis not set, the application falls back to parsing legacy environment variables and treating them asv1contracts. - If a requested contract entry or key is missing in a version, the application throws during contract resolution.
- If
NEXT_PUBLIC_ACTIVE_CONTRACT_VERSIONpoints to a version not defined inNEXT_PUBLIC_CONTRACTS_JSON, startup throwsActive contract version 'X' not found. - Invalid JSON in
NEXT_PUBLIC_CONTRACTS_JSONcauses a parse error at startup. - Incomplete contract entries without an
addressfield throw when that contract is accessed.
NEXT_PUBLIC_ACTIVE_CONTRACT_VERSION=v2
NEXT_PUBLIC_CONTRACTS_JSON={"v1":{"commitmentCore":{"address":"0xv1core"}},"v2":{"commitmentCore":{"address":"0xv2core"}}}Active contract version "X" not found: the configured JSON does not contain that version.Contract entry for key "Y" in version "X" is missing or has no address: the selected version lacks a required contract address.Failed to parse NEXT_PUBLIC_CONTRACTS_JSON: the JSON in the environment variable is invalid.
- The runtime accessor lives at
src/lib/backend/config.tsand providesgetActiveContracts()andgetContractAddress(key). - Legacy single-variable env configuration is still supported and automatically mapped to
v1to avoid breaking changes. - For testnet escrow deployments,
contracts/scripts/deploy-testnet.shupsertsNEXT_PUBLIC_COMMITMENT_CORE_CONTRACT,COMMITMENT_CORE_CONTRACT, andSOROBAN_COMMITMENT_CORE_CONTRACTinto the chosen env file, which defaults to.env.local.
Browser-facing API routes use an explicit CORS policy helper.
COMMITLABS_FIRST_PARTY_ORIGINS: comma-separated allowlist for trusted app origins that can call first-party routes with credentials.COMMITLABS_PUBLIC_API_ORIGINS: comma-separated allowlist for public browser routes, or*. Default:*.
COMMITLABS_FIRST_PARTY_ORIGINSmust never be*.- Development always allows
http://localhost:3000andhttp://127.0.0.1:3000. - If present,
APP_URL,NEXT_PUBLIC_APP_URL,SITE_URL,NEXT_PUBLIC_SITE_URL,VERCEL_PROJECT_PRODUCTION_URL, andVERCEL_URLare folded into the first-party allowlist.
See docs/backend-cors-policy.md for the route classification and allowed methods.