Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 217 additions & 0 deletions backend/.env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Backend Server Configuration
# Copy this to .env and customize for your environment

# ── Tenant API Key Authentication ─────────────────────────────────────────────
# Per-tenant API keys are stored hashed in the `apiKey` table with scopes,
# expiry, and last-used tracking. Rotate keys per tenant without downtime.
# API_KEY below is a deprecated global fallback; it only applies when no
# tenant-scoped key matches and logs a deprecation warning on each use.
# API_KEY=

# Server Configuration
PORT=3000
NODE_ENV=development

# Rate Limiting Configuration
# Global rate limiter: 100 requests per 15 minutes (900000 ms)
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100

# Deprecated: single global API key fallback (see Tenant API Key Authentication)
# API_KEY=

# API endpoint rate limiter: 30 requests per minute (60000 ms)
API_RATE_LIMIT_WINDOW_MS=60000
API_RATE_LIMIT_MAX_REQUESTS=30

# ── Payload Size Limits ───────────────────────────────────────────────────────
# Route-tier-specific caps enforced by the payloadLimit middleware.
# Express size notation applies: "4kb", "16kb", "32kb", "1mb" etc.
# Omit a variable (or leave it blank) to use the compiled-in default.
#
# Tier Default Routes
# global 1mb All routes (last-resort baseline)
# auth 4kb /api/v1/auth/* (credentials/tokens only)
# admin 16kb /admin/* (structured config payloads)
# writes 32kb POST /api/v1/vault/deposits|withdrawals|deposits/v2|strategy
#PAYLOAD_LIMIT_GLOBAL=1mb
#PAYLOAD_LIMIT_AUTH=4kb
#PAYLOAD_LIMIT_ADMIN=16kb
#PAYLOAD_LIMIT_WRITES=32kb

# ── Tenant API Key Scopes ─────────────────────────────────────────────────────
# Comma-separated list of scopes required per route tier. Requests must present
# an x-api-key whose tenant record includes the required scope, otherwise the
# middleware returns 401 SCOPE_INSUFFICIENT with a WWW-Authenticate header.
# API_KEY_SCOPES_READ=read
# API_KEY_SCOPES_WRITE=write
# API_KEY_SCOPES_ADMIN=admin
# API_KEY_EXPIRED_ERROR=API_KEY_EXPIRED
# API_KEY_SCOPE_ERROR=SCOPE_INSUFFICIENT

# ── Wallet signed-action nonces ───────────────────────────────────────────────
# WALLET_NONCE_TTL_SECONDS=300
# WALLET_NONCE_MAX_ACTIVE_PER_WALLET=10
# WALLET_NONCE_ENFORCEMENT=strict # strict | off (default: strict in production)
# WALLET_SIGNATURE_MODE=stellar # stellar | hmac (default: stellar in production)
# WALLET_ACTION_HMAC_SECRET= # HMAC secret when WALLET_SIGNATURE_MODE=hmac
# Multi-instance production deployments must set REDIS_URL below so nonce
# allocation/consumption is atomic across every backend replica.

# ── Admin API Key RBAC ────────────────────────────────────────────────────────
# Roles (least β†’ most privileged): viewer, operator, admin, super-admin
# Assign when registering keys via POST /admin/api-keys/register { "role": "..." }
# viewer – read-only admin endpoints
# operator – maintenance, cache, allowlist, webhooks (non-secret), jobs, exports
# admin – full admin except impersonation / global idempotency flush
# super-admin – all privileged operations

# Stellar RPC Configuration
STELLAR_RPC_URL=https://soroban-testnet.stellar.org
STELLAR_NETWORK=testnet
STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015

# Vault Configuration
VAULT_CONTRACT_ID=

# PostgreSQL Database Configuration
# back to the primary when it is omitted or unavailable.
DATABASE_REPLICA_URL=
DATABASE_POOL_SIZE=10
DATABASE_CONNECTION_TIMEOUT_MS=5000
DATABASE_IDLE_TIMEOUT_MS=30000
PRISMA_POOL_MAX=10
PRISMA_POOL_TIMEOUT_MS=10000
PRISMA_QUERY_TIMEOUT_MS=5000

# Admin audit log persistence mode: memory | prisma | hybrid
ADMIN_AUDIT_LOG_STORAGE=hybrid

# Webhook verification
WEBHOOK_VERIFICATION_TIMEOUT_MS=5000
WEBHOOK_CHALLENGE_TTL_SECONDS=900
WEBHOOK_ALLOW_UNVERIFIED=false
WEBHOOK_SIGNATURE_MAX_SKEW_MS=300000

# Prisma runtime connection settings
PRISMA_POOL_SIZE=10
PRISMA_POOL_TIMEOUT_SEC=10
PRISMA_QUERY_TIMEOUT_MS=5000
PRISMA_TX_MAX_WAIT_MS=5000
PRISMA_TX_TIMEOUT_MS=10000

# ── Query performance budgets and slow-query alerts ──────────────────────────
# Per-query defaults; exact model/action overrides use a JSON object.
# QUERY_READ_BUDGET_MS=100
# QUERY_WRITE_BUDGET_MS=200
# QUERY_BUDGETS_JSON={"User.findUnique":50,"Transaction.findMany":150}
# A breach at or above this multiple of its budget is critical.
# SLOW_QUERY_CRITICAL_MULTIPLIER=3
# SLOW_QUERY_ALERT_COOLDOWN_MS=900000
# SLOW_QUERY_ALERT_TIMEOUT_MS=5000
# QUERY_ALERT_TYPE=both # slack | pagerduty | both
# SLOW_QUERY_SLACK_WEBHOOK_URL= # falls back to SLACK_WEBHOOK_URL
# SLOW_QUERY_PAGERDUTY_INTEGRATION_KEY= # falls back to PAGERDUTY_INTEGRATION_KEY
# REDIS_URL below shares alert cooldowns across backend replicas.

# ── Cache Configuration ───────────────────────────────────────────────────────
# Redis-backed response cache for price and vault summary endpoints.
# When REDIS_URL is set, GET responses for /api/v1/vault/summary, /api/v1/vault/apy,
# and /api/v1/vault/metrics are cached in Redis so they survive process restarts and
# are shared across multiple backend replicas.
# When REDIS_URL is absent or Redis is temporarily unreachable the backend falls
# back transparently to the in-memory LRU store (CACHE_MAX_ENTRIES entries).

# Redis connection URL (also used for rate-limiting; set once for both)
REDIS_URL=redis://localhost:6379

# Response cache TTL in milliseconds (applies to vault summary, metrics, APY endpoints)
CACHE_TTL_MS=60000

# Alias accepted in addition to CACHE_TTL_MS
# CACHE_VAULT_METRICS_TTL_MS=60000

# Maximum entries kept in the in-memory LRU fallback store
CACHE_MAX_ENTRIES=500

# Redis cache key namespace prefix (default: "cache:")
# REDIS_CACHE_KEY_PREFIX=cache:

# Connection attempt timeout for the Redis cache client (ms)
# REDIS_CACHE_CONNECT_TIMEOUT_MS=2000

# Per-command timeout for Redis cache operations (ms)
# REDIS_CACHE_COMMAND_TIMEOUT_MS=500

# CORS Configuration
# Comma-separated list of allowed origins. Supports strings or regex like /https?:\/\/.*\.yieldvault\.finance/
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://app.yieldvault.finance

# Email Notification Configuration
EMAIL_PROVIDER=resend
EMAIL_API_KEY=
EMAIL_FROM_ADDRESS=notifications@yieldvault.finance

# Latency SLO Monitoring Configuration
# SLO thresholds in milliseconds
SLO_READ_THRESHOLD_MS=200
SLO_WRITE_THRESHOLD_MS=500

# Prometheus /metrics endpoint scrape credential
# When set, GET /metrics requires Authorization: Bearer <token> or ApiKey <token>
# METRICS_AUTH_TOKEN=your-scrape-token
# METRICS_SCRAPE_TOKEN=your-scrape-token
# PROMETHEUS_BEARER_TOKEN=your-scrape-token

# Evaluation window for P95 calculation (5 minutes = 300000 ms)
SLO_EVALUATION_WINDOW_MS=300000

# Alert cooldown period to prevent spam (15 minutes = 900000 ms)
SLO_ALERT_COOLDOWN_MS=900000

# SLO check interval (1 minute = 60000 ms)
SLO_CHECK_INTERVAL_MS=60000

# Alert Integration Configuration
# Set to 'slack', 'pagerduty', or 'both'
ALERT_TYPE=slack

# Slack Webhook URL (required if ALERT_TYPE includes 'slack')
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK

# PagerDuty Integration Key (required if ALERT_TYPE includes 'pagerduty')
PAGERDUTY_INTEGRATION_KEY=

# Event Polling Configuration
# Poll interval for checking new events (10 seconds = 10000 ms)
EVENT_POLL_INTERVAL_MS=10000

# Batch size for event replay (number of ledgers per batch)
EVENT_REPLAY_BATCH_SIZE=100

# ── Outbox Pattern Configuration ───────────────────────────────────────────────
# The outbox pattern ensures reliable, at-least-once event delivery by writing
# events to the EventOutbox table as part of business transactions, with a
# background processor that relays them to webhook consumers.
#
# Poll interval for the outbox processor (2 seconds = 2000 ms)
# OUTBOX_POLL_INTERVAL_MS=2000
#
# Maximum number of entries to process per poll cycle
# OUTBOX_BATCH_SIZE=50
#
# Lock timeout for distributed instance-level locking (60 seconds = 60000 ms)
# OUTBOX_LOCK_TIMEOUT_MS=60000
#
# Maximum delivery attempts before moving to dead-letter
# OUTBOX_MAX_ATTEMPTS=3
#
# Retention period for processed entries (7 days = 604800000 ms)
# OUTBOX_RETENTION_MS=604800000
#
# Unique instance identifier for distributed locking (auto-generated if unset)
# OUTBOX_INSTANCE_ID=
#
# Cleanup interval for removing old processed entries (1 hour = 3600000 ms)
# OUTBOX_CLEANUP_INTERVAL_MS=3600000
DATABASE_URL=file:./prisma/dev.db
4 changes: 4 additions & 0 deletions backend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ module.exports = {
'^@yieldvault/api-schemas$': '<rootDir>/../packages/api-schemas/dist/index.js',
'^@yieldvault/api-schemas/(.*)$': '<rootDir>/../packages/api-schemas/dist/$1',
'^@prisma/instrumentation$': '<rootDir>/src/__tests__/mocks/prismainstrumentation.js',
// pnpm does not hoist dotfile packages (.prisma/client) to the top-level
// node_modules, so Jest cannot resolve it with its default resolver.
// Map it explicitly to the generated location in the pnpm content store.
'^\\.prisma/client$': '<rootDir>/../node_modules/.pnpm/@prisma+client@5.22.0_prisma@5.22.0/node_modules/.prisma/client/index.js',
'^@opentelemetry/(.*)$': '<rootDir>/src/__tests__/mocks/opentelemetry.js',
// Redirect the sorobanClient module that vaultEndpoints imports (./sorobanClient)
// so existing integration tests keep passing without a live Stellar RPC.
Expand Down
Loading
Loading