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
30 changes: 30 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
DATABASE_URL=postgresql://vortex:vortex@localhost:5432/vortex?schema=public

# ─── Server ──────────────────────────────────────────────────────────────────
# development | production | test
NODE_ENV=development
# Port the relay API + WebSocket feed listen on
PORT=4000
# Runtime mode. Production requires SOROBAN_SIGNING_KEY and an explicit
Expand Down Expand Up @@ -77,6 +79,7 @@ SOLVER_CHAINS=stellar,ethereum,base,polygon,arbitrum,optimism,avalanche
# Which repository adapter backs intents and solvers.
# memory (default) — in-process Maps, no database required
# prisma — PostgreSQL via Prisma (staging / production)
# INTENTS_PERSISTENCE is a deprecated alias for INTENTS_STORE (issue #404).
INTENTS_PERSISTENCE=memory
ALLOW_LEGACY_STELLAR_SIGNATURES=false
SOLVERS_PERSISTENCE=memory
Expand Down Expand Up @@ -435,6 +438,33 @@ HEALTH_READY_SUCCESS_THRESHOLD=2
HEALTH_EVENT_LOOP_MAX_LAG_MS=1000
# Soroban RPC endpoints for the quorum check (default: SOROBAN_RPC_URL).
SOROBAN_RPC_HEALTH_URLS=

# ─── Intents store (issue #404) ──────────────────────────────────────────────
# memory | dual | postgres — supersedes INTENTS_PERSISTENCE above.
# memory — in-process only; everything is lost on restart (dev/test)
# dual — writes to both memory and Postgres, reads from memory, and a
# consistency verifier reports mismatches (migration phase)
# postgres — Postgres is the only store (production)
# See docs/runbooks/intents-store-migration.md before changing this in a
# deployed environment. Leave unset to fall back to INTENTS_PERSISTENCE.
INTENTS_STORE=memory
# How often (ms) the dual-write consistency verifier compares the two stores.
INTENTS_VERIFY_INTERVAL_MS=60000

# ─── Source-chain deposit verification (issue #403) ──────────────────────────
# When true, intents from EVM chains stay hidden from solvers (srcVerified=false)
# until the escrow's Deposited log is found at the chain's confirmation depth.
# See docs/runbooks/evm-deposit-verification.md.
EVM_DEPOSIT_VERIFICATION_ENABLED=false
# JSON maps keyed by chain: ethereum | base | polygon | arbitrum | optimism | avalanche
# EVM_RPC_URLS={"ethereum":"https://eth.example","base":"https://base.example"}
EVM_RPC_URLS=
EVM_ESCROW_ADDRESSES=
# Shortfall allowed for fee-on-transfer tokens, in basis points (0 = exact amount).
EVM_TRANSFER_FEE_TOLERANCE_BPS=0
# Blocks searched for the Deposited log when an intent has no srcTxHash.
EVM_LOG_LOOKBACK_BLOCKS=10000

# ── Transactional outbox relay (issue #396) ──────────────────────────────────
OUTBOX_RELAY_ENABLED=true
OUTBOX_RELAY_INTERVAL_MS=2000
Expand Down
15 changes: 15 additions & 0 deletions .env.mainnet.example
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,21 @@ HEALTH_READY_SUCCESS_THRESHOLD=2
HEALTH_EVENT_LOOP_MAX_LAG_MS=1000
# Soroban RPC endpoints for the quorum check (default: SOROBAN_RPC_URL).
SOROBAN_RPC_HEALTH_URLS=

# ─── Persistence ─────────────────────────────────────────────────────────────
# REQUIRED: production must not lose intents on restart. Promote through
# memory → dual → postgres per docs/runbooks/intents-store-migration.md.
INTENTS_STORE=postgres

# ─── Source-chain deposit verification (issue #403) ──────────────────────────
# REQUIRED before accepting EVM-source intents in production: without it,
# solvers must trust that the user's funds exist.
EVM_DEPOSIT_VERIFICATION_ENABLED=true
EVM_RPC_URLS=<CHANGE_ME>
EVM_ESCROW_ADDRESSES=<CHANGE_ME>
EVM_TRANSFER_FEE_TOLERANCE_BPS=0
EVM_LOG_LOOKBACK_BLOCKS=10000

# ── Transactional outbox relay (issue #396) ──────────────────────────────────
OUTBOX_RELAY_ENABLED=true
OUTBOX_RELAY_INTERVAL_MS=2000
Expand Down
9 changes: 9 additions & 0 deletions .env.staging.example
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,15 @@ HEALTH_READY_SUCCESS_THRESHOLD=2
HEALTH_EVENT_LOOP_MAX_LAG_MS=1000
# Soroban RPC endpoints for the quorum check (default: SOROBAN_RPC_URL).
SOROBAN_RPC_HEALTH_URLS=

# Staging runs the dual-write phase so the consistency verifier can soak
# before production moves to postgres (docs/runbooks/intents-store-migration.md).
INTENTS_STORE=dual
INTENTS_VERIFY_INTERVAL_MS=60000
EVM_DEPOSIT_VERIFICATION_ENABLED=true
EVM_RPC_URLS=
EVM_ESCROW_ADDRESSES=

# ── Transactional outbox relay (issue #396) ──────────────────────────────────
OUTBOX_RELAY_ENABLED=true
OUTBOX_RELAY_INTERVAL_MS=2000
Expand Down
279 changes: 0 additions & 279 deletions .env.testnet.example
Original file line number Diff line number Diff line change
@@ -1,279 +0,0 @@
# .env.testnet.example
#
# Environment template for LOCAL DEVELOPMENT against Stellar TESTNET.
# Copy to .env and fill in any values marked with <CHANGE_ME>.
#
# cp .env.testnet.example .env
#
# Testnet is safe to experiment with — tokens have no real value and contract
# deployments are free via Friendbot. Never reuse testnet keys on mainnet.
#
# Closes #136

# ─── Database ────────────────────────────────────────────────────────────────
# Local Docker Compose default. Adjust if you use a remote or managed DB.
DATABASE_URL=postgresql://vortex:vortex@localhost:5432/vortex?schema=public

# ─── Server ──────────────────────────────────────────────────────────────────
PORT=4000
NODE_ENV=development

# ─── Stellar / Soroban ───────────────────────────────────────────────────────
STELLAR_NETWORK=testnet
INTENTS_PERSISTENCE=prisma
ETHEREUM_RPC_URL=
ETHEREUM_ESCROW_ADDRESS=
BASE_RPC_URL=
BASE_ESCROW_ADDRESS=
POLYGON_RPC_URL=
POLYGON_ESCROW_ADDRESS=
ARBITRUM_RPC_URL=
ARBITRUM_ESCROW_ADDRESS=
OPTIMISM_RPC_URL=
OPTIMISM_ESCROW_ADDRESS=
AVALANCHE_RPC_URL=
AVALANCHE_ESCROW_ADDRESS=
EVM_RPC_ALLOWLIST=
ALLOW_LEGACY_STELLAR_SIGNATURES=false
SOROBAN_RPC_URL=https://soroban-testnet.stellar.org

# Testnet contract IDs — leave blank until you have deployed contracts.
# The service boots without them; on-chain write paths are no-ops when empty.
SETTLEMENT_CONTRACT_ID=
SOLVER_REGISTRY_CONTRACT_ID=

# Testnet signing key — generate a throwaway keypair, fund it with Friendbot,
# and paste the secret seed here. Never reuse this key on mainnet.
#
# # Generate a new key:
# npx @stellar/stellar-cli keys generate local-dev --network testnet
# npx @stellar/stellar-cli keys show local-dev
#
# # Or via the SDK:
# node -e "console.log(require('@stellar/stellar-sdk').Keypair.random().secret())"
#
# # Fund it (testnet only):
# curl "https://friendbot.stellar.org/?addr=<YOUR_PUBLIC_KEY>"
#
# Optional in development — leave blank to skip on-chain writes.
SOROBAN_SIGNING_KEY=

# Fee percentile used when estimating Soroban inclusion fees.
# p50 is a safe default for testnet; raise to p90+ for time-sensitive mainnet txs.
SOROBAN_FEE_PERCENTILE=p50

# ─── CORS ────────────────────────────────────────────────────────────────────
# Wildcard is fine for local development — tighten this in staging/production.
CORS_ORIGIN=*

# ─── WebSocket ───────────────────────────────────────────────────────────────
WS_MAX_CONNECTIONS=1000

# ─── Pluggable signer backend (issue #400) ───────────────────────────────────
# SIGNER_BACKEND=local is the default for development.
# In production use SIGNER_BACKEND=vault and supply VAULT_ADDR + VAULT_TOKEN.
SIGNER_BACKEND=local
VAULT_ADDR=
VAULT_TOKEN=
VAULT_TRANSIT_KEY_NAME=vortex-signer
ALLOW_LOCAL_SIGNER_IN_PROD=false
# ─── Resource-exhaustion limits (issue #476) ─────────────────────────────────
# Maximum JSON nesting depth — rejects deeply-nested body attacks (default 10).
JSON_MAX_DEPTH=10
# Maximum chain values in a single WS subscribe message (default 20).
WS_MAX_FILTER_CHAINS=20
# Maximum active subscriptions per WS connection (default 10).
WS_MAX_SUBSCRIPTIONS=10
# Postgres statement_timeout for standard queries in ms (default 5000).
DB_QUERY_TIMEOUT_MS=5000
# Postgres statement_timeout for batch queries in ms (default 10000).
DB_BATCH_QUERY_TIMEOUT_MS=10000
# Postgres statement_timeout for stats queries in ms (default 15000).
DB_STATS_QUERY_TIMEOUT_MS=15000

# Emergency kill-switch (issue #477)
# Postgres-backed so a pause survives a restart and reaches every replica.
KILLSWITCH_OPERATOR_TOKEN=
KILLSWITCH_REDIS_URL=
KILLSWITCH_POLL_MS=2000
KILLSWITCH_PERSISTENCE=prisma

# ─── Observability (optional) ────────────────────────────────────────────────
# Leave blank to disable Sentry error reporting.
SENTRY_DSN=

# Bearer token for GET /metrics (issue #298).
# Leave blank for unauthenticated local Prometheus scraping.
# In production: generate with `openssl rand -hex 32` and set a real value.
METRICS_TOKEN=

# debug | info | warn | error (defaults to "debug" in development)
LOG_LEVEL=debug

# ── Shadow-mode divergence monitor (issue #401) ─────────────────────────
# Off by default in every environment. It runs read-only `simulateTransaction`
# calls against SETTLEMENT_CONTRACT_ID in parallel with the off-chain intent
# path and never signs or submits anything.
#
# SHADOW_SOURCE_ACCOUNT only has to be a valid Stellar public key: it is used to
# populate the source-account field of the simulated envelope and is never
# signed, never charged a fee and never broadcast. It must still be set, or
# every transition reports "contract_unconfigured".
SHADOW_MODE_ENABLED=false
SHADOW_SAMPLE_RATE=1
SHADOW_QUEUE_MAX=256
SHADOW_CONCURRENCY=4
SHADOW_SOURCE_ACCOUNT=
# ─── Governance / Protocol Parameters ────────────────────────────────────────
# On-chain governance parameters contract ID — leave blank to use code defaults.
PARAMS_CONTRACT_ID=

# Poll interval in ms. 30 000 is fine for testnet.
PARAMS_POLL_INTERVAL_MS=30000
# ─── Leader election ─────────────────────────────────────────────────────────
# Enable for multi-replica testnet deployments.
LEADER_ELECTION_ENABLED=false
LEADER_ELECTION_HEARTBEAT_MS=5000

# ─── Background jobs (issue #494) ────────────────────────────────────────────
# api | worker | all — queue workers only run in "worker" or "all".
PROCESS_ROLE=all
# memory (single-process, dev/test) | bullmq (Redis-backed, uses REDIS_URL)
JOBS_DRIVER=memory
# Grace period for in-flight jobs on SIGTERM before they are returned to the queue.
JOBS_SHUTDOWN_TIMEOUT_MS=25000

# ─── Runtime feature flags (issue #495) ──────────────────────────────────────
# Change propagation across instances: memory (single instance) | redis
FLAGS_PUBSUB=memory
# Safety-net cache reload interval (ms)
FLAGS_REFRESH_MS=30000
# Break-glass pins that win over DB state, e.g. onchain-dry-run=true
FLAG_OVERRIDES=

# ─── Admin RBAC ──────────────────────────────────────────────────────────────
# Comma-separated id:role:secret (role = admin | superadmin, secret >= 16 chars).
# Sent as the x-admin-key header (the secret part). Empty disables admin APIs.
ADMIN_API_KEYS=

# ─── Guardian emergency ingestion (issue #507) ───────────────────────────────
# Guardian / security-council contract ID. Leave blank to disable ingestion.
GUARDIAN_CONTRACT_ID=

# ─── Synthetic canary (issue #496) ───────────────────────────────────────────
# Canary user + solver addresses; excluded from public stats and leaderboards.
CANARY_ADDRESSES=

# Public anonymised datasets (docs/rfcs/0001)
# Master switch for the public dataset publication job.
DATASETS_ENABLED=false
# Hash user addresses with the rotating salt before export.
DATASETS_ANONYMIZE=true
# Base anonymisation salt. Required (>= 32 chars) when datasets are enabled
# and anonymisation is on; generate with `openssl rand -hex 32`.
DATASETS_SALT=
# How often the anonymisation salt rotates, in hours.
DATASETS_SALT_ROTATION_HOURS=24
# How many previous salt windows are retained for continuity.
DATASETS_SALT_RETENTION_WINDOWS=2
# Public bucket/prefix the published datasets live under.
DATASETS_PUBLIC_BUCKET=vortex-public-datasets
# Storage backend: local (writes to disk) | memory (tests only).
DATASETS_STORAGE=local
# Root directory for the local storage backend.
DATASETS_LOCAL_DIR=.datasets

# Secrets Manager (issue #465)
# Provider: env | aws-secrets-manager | vault-kv
SECRETS_PROVIDER=env
# Poll interval for secret rotation (ms)
SECRETS_REFRESH_INTERVAL_MS=60000
# Extra secrets: comma-separated "name:envVar:required"
SECRETS_EXTRA=

# AWS Secrets Manager
AWS_SECRETS_MANAGER_PREFIX=
AWS_SECRETS_MANAGER_POLL_INTERVAL_MS=60000

# Vault KV
VAULT_KV_MOUNT=secret
VAULT_KV_PREFIX=vortex/
VAULT_KV_POLL_INTERVAL_MS=60000

# Extra secret env vars referenced by the default SecretConfig
JWT_SIGNING_KEY=
WEBHOOK_SECRET=
CHANNEL_KEY=

# Egress/SSRF Protection
EGRESS_TIMEOUT_MS=10000
EGRESS_MAX_REDIRECTS=3
EGRESS_MAX_BODY_SIZE_BYTES=10485760
SOROBAN_RPC_ALLOWLIST=soroban-testnet.stellar.org,soroban-rpc.stellar.org
WEBHOOK_ALLOWLIST=hooks.example.com,hooks.trusted.com
ORACLE_ALLOWLIST=oracle.trusted.io
# ─── WS gateway hardening (issue #455) ───────────────────────────────────────
# Inbound frames larger than this close the socket (1009).
WS_MAX_PAYLOAD_BYTES=16384
# Concurrent WS connections per client IP (0 = unlimited).
WS_MAX_CONNECTIONS_PER_IP=20
# Trusted reverse-proxy hops for X-Forwarded-For (0 = socket address only).
WS_TRUST_PROXY_HOPS=0
# Inbound token bucket per connection; repeat violators are disconnected.
WS_RATE_LIMIT_PER_SEC=10
WS_RATE_LIMIT_BURST=20
WS_RATE_LIMIT_MAX_VIOLATIONS=5
# Outbound backpressure: messages held per slow consumer, socket buffer
# threshold (bytes), and what to do when the queue is full.
WS_OUTBOUND_QUEUE_MAX=1000
WS_OUTBOUND_BUFFER_BYTES=1048576
WS_SLOW_CONSUMER_POLICY=drop_oldest
# HS256 secret for solver JWTs from the SEP-10 auth flow (#442); >= 32 chars.
# Empty disables JWT auth on the WS gateway.
AUTH_JWT_SECRET=

# ─── API keys & distributed rate limiting (issue #441) ─────────────────────────
RATE_LIMIT_LOCAL_PRUNE_MS=60000
# Redis URL for the shared rate-limit window. Empty = bounded local limiter.
RATE_LIMIT_REDIS_URL=

# ─── Scoped solver credentials (issue #443) ───────────────────────────────────
CREDENTIAL_REVOCATION_PUBSUB=memory

# ─── SSE intent feed (issue #433) ─────────────────────────────────────────────
SSE_HEARTBEAT_MS=15000
SSE_MAX_BUFFER_BYTES=1048576

# ─── Public anonymised datasets ──────────────────────────────────────────────
DATASETS_ENABLED=false
DATASETS_ANONYMIZE=true
DATASETS_SALT=
DATASETS_SALT_ROTATION_HOURS=24
DATASETS_SALT_RETENTION_WINDOWS=2
DATASETS_PUBLIC_BUCKET=
DATASETS_STORAGE_KIND=memory
DATASETS_LOCAL_DIR=

# ─── Health probes (issue #492) ──────────────────────────────────────────────
# Roles served by this process (api, ws, worker); readiness checks follow them.
SERVICE_ROLES=api,ws,worker
HEALTH_CHECK_INTERVAL_MS=5000
# Readiness hysteresis: failures before not-ready, successes before ready again.
HEALTH_READY_FAILURE_THRESHOLD=3
HEALTH_READY_SUCCESS_THRESHOLD=2
# Liveness fails when event-loop delay exceeds this.
HEALTH_EVENT_LOOP_MAX_LAG_MS=1000
# Soroban RPC endpoints for the quorum check (default: SOROBAN_RPC_URL).
SOROBAN_RPC_HEALTH_URLS=
# ── Transactional outbox relay (issue #396) ──────────────────────────────────
OUTBOX_RELAY_ENABLED=true
OUTBOX_RELAY_INTERVAL_MS=2000
OUTBOX_RELAY_BATCH_SIZE=10
OUTBOX_MAX_ATTEMPTS=8
# Must exceed the signed transaction's 30 s time bound.
OUTBOX_LEASE_SECONDS=120

# ── Slashing saga (issue #397) ───────────────────────────────────────────────
SLASH_CHALLENGE_WINDOW_SECONDS=600
SLASH_CLOCK_SKEW_TOLERANCE_SECONDS=30
SLASH_MAX_SUBMIT_ATTEMPTS=5
Loading
Loading