This guide documents every environment variable used by StellarKraal. Copy .env.example to .env and fill in the values before starting any service.
cp .env.example .envVariables marked Required must be set or the service will refuse to start. Variables marked Optional have safe defaults that work for local development.
Security note: Never commit real secrets to version control. Variables marked with a GitHub Secret name must be stored in GitHub Actions (Settings β Secrets and variables β Actions) for CI/CD.
These variables are consumed by both the frontend build and the backend runtime.
| Required | Yes |
| Format | testnet | mainnet |
| Default | testnet |
| GitHub Secret | NEXT_PUBLIC_NETWORK |
Selects the Stellar network. Use testnet for all non-production environments. Changing this to mainnet without updating RPC_URL and CONTRACT_ID to matching production values will cause all contract calls to fail.
| Required | Yes |
| Format | HTTPS URL |
| Example | https://soroban-testnet.stellar.org |
| GitHub Secret | RPC_URL |
Soroban JSON-RPC endpoint the backend uses to submit transactions and query contract state. The public testnet endpoint is https://soroban-testnet.stellar.org. For mainnet use https://soroban-mainnet.stellar.org or a private RPC provider.
| Required | Yes |
| Format | 56-character Stellar contract/account ID (C...) |
| GitHub Secret | CONTRACT_ID |
The deployed Soroban contract address. Obtain this after running stellar contract deploy. A mismatch between this value and the actual on-chain deployment will cause all loan lifecycle operations to fail silently or with cryptic RPC errors.
These variables are embedded into the Next.js bundle at build time. Any change requires a rebuild (npm run build).
| Required | Yes |
| Format | HTTP(S) URL, no trailing slash |
| Default (local) | http://localhost:3001 |
| GitHub Secret | NEXT_PUBLIC_API_URL |
Base URL the browser uses to reach the backend REST API. In production this must be the public HTTPS URL of the backend service. An incorrect value causes all API calls from the frontend to fail with network errors.
| Required | Yes |
| Format | HTTPS URL |
| Default (local) | https://soroban-testnet.stellar.org |
| GitHub Secret | NEXT_PUBLIC_RPC_URL |
Soroban RPC URL used by the browser-side Stellar SDK (e.g. Freighter wallet integration). Usually the same value as RPC_URL. Exposed to the browser, so do not use a private RPC endpoint that carries credentials in the URL.
These variables are read at runtime by the Express API server (backend/src/config.ts). The server validates all required variables on startup using Zod and exits with a descriptive error if any are missing or malformed.
| Required | No |
| Format | Integer 1β65535 |
| Default | 3001 |
TCP port the Express server listens on. Change this if port 3001 is already in use on your machine. The Docker Compose file maps this port to the host automatically.
| Required | No |
| Format | development | production | test |
| Default | development |
Controls logging verbosity, error detail in API responses, and certain security defaults. Always set to production in deployed environments β this disables stack traces in error responses and enables stricter security headers.
| Required | In production |
| Format | HTTP(S) URL, no trailing slash |
| Default (local) | http://localhost:3000 |
| GitHub Secret | FRONTEND_URL |
Allowed CORS origin. The backend rejects cross-origin requests from any other origin. In production set this to the exact URL of the deployed frontend (e.g. https://app.stellarkraal.example.com).
| Required | In production |
| Format | Arbitrary string, minimum 32 characters recommended |
| GitHub Secret | JWT_SECRET |
Secret used to sign and verify JWT access tokens. A weak or default value allows anyone to forge valid tokens. Generate a strong value with:
openssl rand -hex 32Rotate this secret by updating the value and redeploying β all existing tokens will be immediately invalidated.
| Required | No |
| Format | Positive integer (milliseconds) |
| Default | 900000 (15 minutes) |
Time-to-live for JWT access tokens. Access tokens are short-lived and must be refreshed via /api/v1/auth/refresh before expiry. Set this lower (e.g., 300000 for 5 minutes) for tighter security; set it higher (e.g., 3600000 for 1 hour) for better UX in low-risk environments.
| Required | No |
| Format | Positive integer (milliseconds) |
| Default | 604800000 (7 days) |
Time-to-live for refresh tokens. Refresh tokens are stored as HTTP-only cookies and can be used to obtain new access tokens without re-authenticating. Set this to a shorter duration (e.g., 86400000 for 1 day) if you need tighter session control.
| Required | No |
| Format | Positive integer (Γ10_000) |
| Default | 13000 (1.3) |
Health factor threshold below which a warning-level alert is fired. The health factor is calculated as (collateral_value Γ liquidation_threshold_bps) / (loan_amount Γ 10_000). A health factor of 1.3 means the loan is 30% overcollateralized. When it drops below this threshold, a warning is sent to Slack and the on-call team to give the borrower time to add collateral or repay.
| Required | No |
| Format | Positive integer (Γ10_000) |
| Default | 10000 (1.0) |
Critical health factor threshold below which a loan becomes eligible for liquidation. A health factor of 1.0 means the loan is exactly at the liquidation boundary. When it drops below this value, a critical alert is sent to PagerDuty and the liquidation bot should trigger liquidation.
| Required | No |
| Format | Hex or arbitrary string, minimum 16 characters |
| GitHub Secret | WEBHOOK_SECRET |
HMAC-SHA256 secret used to verify the signature on incoming webhook payloads. If unset, webhook signature verification is skipped (not recommended in production). Generate with:
openssl rand -hex 32| Required | No |
| Format | Arbitrary string, minimum 8 characters |
| GitHub Secret | ADMIN_API_KEY |
Bearer token required to access /api/admin/* endpoints. If unset, admin routes return 403. Rotate immediately if compromised. Generate with:
openssl rand -hex 16All rate limits are expressed as requests per minute per IP address.
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_GLOBAL |
60 |
Applied to every route as a baseline ceiling |
RATE_LIMIT_AUTH |
10 |
Applied to /auth/* routes to slow brute-force attempts |
RATE_LIMIT_READ |
100 |
Applied to read-only GET routes |
RATE_LIMIT_WRITE |
10 |
Applied to state-changing POST/PUT/DELETE routes |
Increase these values if legitimate traffic is being throttled. Decrease them for tighter abuse protection in production.
| Variable | Default | Description |
|---|---|---|
TIMEOUT_GLOBAL_MS |
30000 |
Maximum milliseconds for any request before a 408 is returned |
TIMEOUT_WRITE_MS |
15000 |
Maximum milliseconds for write operations (loan origination, collateral updates) |
TIMEOUT_CONTRACT_MS |
30000 |
Maximum milliseconds for Soroban contract submission routes (loan request, repay, liquidate) |
Values are in milliseconds. TIMEOUT_CONTRACT_MS is deliberately higher than TIMEOUT_WRITE_MS to account for Soroban RPC latency. Increase it if contract submissions are timing out on your network.
| Variable | Default | Description |
|---|---|---|
POOL_MIN |
2 |
Minimum persistent RPC connections to keep open |
POOL_MAX |
10 |
Maximum concurrent RPC connections |
POOL_MIN must be β€ POOL_MAX. Increase POOL_MAX under high write load; decrease it to reduce resource usage on low-traffic deployments.
| Required | No |
| Format | Positive integer (milliseconds) |
| Default | 300000 (5 minutes) |
How long collateral appraisal results are cached in memory before a fresh RPC call is made. Increase this to reduce RPC traffic; decrease it if you need near-real-time price accuracy for liquidation decisions.
| Required | No |
| Format | Positive integer (basis points) |
| Default | 50 (0.5%) |
Origination fee charged on each new loan, expressed in basis points. 50 bps = 0.5%. The contract enforces a hard maximum of 500 bps (5%); setting a higher value will cause the contract to return error #10. Change this with the admin function set_origination_fee(admin, fee_bps) on the contract β the backend reads it from config on startup only for display/validation purposes.
| Required | No |
| Format | Positive integer (milliseconds), minimum 1000 |
| Default | 10000 (10 seconds) |
Maximum time the server waits for in-flight requests to complete after receiving SIGTERM or SIGINT. During this window the server stops accepting new connections but continues serving existing requests. After this timeout, the process exits forcefully. Set this higher (e.g., 30000) if your Soroban contract calls typically take longer than 10 seconds under load.
| Required | No |
| Format | Absolute or relative filesystem path |
| Default | β (audit logging disabled when unset) |
Directory where structured audit log files are written. Each audit event (admin actions, loan state changes, authentication events) is appended as a JSON line. Ensure the backend process has write access to this directory. In production, use an absolute path (e.g., /var/log/stellarkraal/audit).
| Required | No (production: recommended) |
| Format | postgresql://user:password@host:5432/dbname or sqlite:/path/to/db |
| GitHub Secret | DATABASE_URL |
PostgreSQL connection URL for staging and production. When unset, the backend falls back to SQLite (local development only). In production, always set this to a PostgreSQL URL. The value must start with postgres://, postgresql://, or sqlite:.
These variables configure the backend alert dispatcher. All are optional β the corresponding alert channel is silently disabled if the variable is unset.
| Required | No |
| Format | https://hooks.slack.com/services/T.../B.../... |
| GitHub Secret | SLACK_WEBHOOK_URL |
Slack incoming webhook URL for deployment notifications and operational alerts. Create one at https://api.slack.com/messaging/webhooks. The same webhook is used by the staging deployment workflow.
| Required | No |
| Format | 32-character alphanumeric string |
| GitHub Secret | PAGERDUTY_ROUTING_KEY |
PagerDuty Events API v2 routing (integration) key. Only critical-severity alerts (e.g. liquidation failures, RPC outages) are sent to PagerDuty. Find this key in PagerDuty under Services β Integrations β Events API v2.
| Required | No |
| Format | HTTPS URL, no trailing slash |
| Default | https://github.com/teslims2/StellarKraal-/blob/main/docs/runbooks |
Base URL prepended to runbook paths in alert messages. Override this if you host runbooks elsewhere (e.g. Confluence, Notion).
| Variable | Service | Required | Default |
|---|---|---|---|
NEXT_PUBLIC_NETWORK |
Shared | Yes | testnet |
RPC_URL |
Shared | Yes | β |
CONTRACT_ID |
Shared | Yes | β |
NEXT_PUBLIC_API_URL |
Frontend | Yes | http://localhost:3001 |
NEXT_PUBLIC_RPC_URL |
Frontend | Yes | https://soroban-testnet.stellar.org |
PORT |
Backend | No | 3001 |
NODE_ENV |
Backend | No | development |
FRONTEND_URL |
Backend | Prod only | http://localhost:3000 |
ALLOWED_ORIGINS |
Backend | No | β |
JWT_SECRET |
Backend | Prod only | β |
ACCESS_TTL_MS |
Backend | No | 900000 |
REFRESH_TTL_MS |
Backend | No | 604800000 |
WEBHOOK_SECRET |
Backend | No | β |
ADMIN_API_KEY |
Backend | No | β |
RATE_LIMIT_GLOBAL |
Backend | No | 60 |
RATE_LIMIT_AUTH |
Backend | No | 10 |
RATE_LIMIT_READ |
Backend | No | 100 |
RATE_LIMIT_WRITE |
Backend | No | 10 |
TIMEOUT_GLOBAL_MS |
Backend | No | 30000 |
TIMEOUT_WRITE_MS |
Backend | No | 15000 |
TIMEOUT_CONTRACT_MS |
Backend | No | 30000 |
ORIG_FEE_BPS |
Backend | No | 50 |
POOL_MIN |
Backend | No | 2 |
POOL_MAX |
Backend | No | 10 |
APPRAISAL_CACHE_TTL_MS |
Backend | No | 300000 |
HEALTH_FACTOR_WARN |
Backend | No | 13000 |
HEALTH_FACTOR_CRIT |
Backend | No | 10000 |
SHUTDOWN_TIMEOUT_MS |
Backend | No | 10000 |
AUDIT_LOG_DIR |
Backend | No | β |
DATABASE_URL |
Backend | Prod only | β (SQLite) |
SLACK_WEBHOOK_URL |
Alerting | No | β |
PAGERDUTY_ROUTING_KEY |
Alerting | No | β |
RUNBOOK_BASE_URL |
Alerting | No | (GitHub URL) |