diff --git a/.env.example b/.env.example index 90498e7..a774f5f 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,16 @@ -# ── Stellar / Soroban ──────────────────────────────────────────────────────── -# testnet | mainnet (default: testnet) +# ============================================================================= +# VacciChain Environment Variables +# ============================================================================= +# Copy this file to .env and fill in the required values: +# cp .env.example .env +# +# Required variables are marked with [REQUIRED]. +# Optional variables show their default after the = sign. +# ============================================================================= + +# -- Stellar / Soroban ------------------------------------------------------------ + +# Network to connect to: "testnet" or "mainnet" (default: testnet) STELLAR_NETWORK=testnet # Frontend network — must match STELLAR_NETWORK; Vite requires the VITE_ prefix @@ -9,61 +20,74 @@ VITE_STELLAR_NETWORK=testnet # Horizon REST API for the chosen network HORIZON_URL=https://horizon-testnet.stellar.org -# Soroban RPC endpoint for contract simulation and submission +# [REQUIRED] Soroban RPC endpoint for contract simulation and submission +# Testnet: https://soroban-testnet.stellar.org +# Mainnet: https://soroban-rpc.mainnet.stellar.gateway.fm SOROBAN_RPC_URL=https://soroban-testnet.stellar.org -# Must exactly match the target network passphrase +# [REQUIRED] Network passphrase -- must exactly match the target network +# Testnet: "Test SDF Network ; September 2015" +# Mainnet: "Public Global Stellar Network ; September 2015" STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015 -# ── Contract ───────────────────────────────────────────────────────────────── -# 56-char contract address starting with C — output of `make deploy` +# -- Contract --------------------------------------------------------------------- + +# [REQUIRED] Deployed contract address (56-char string starting with C) +# Obtain from: cd contracts && make deploy VACCINATIONS_CONTRACT_ID= -# ── Backend auth ───────────────────────────────────────────────────────────── -# Secret key for admin contract invocations (starts with S) +# -- Backend auth ----------------------------------------------------------------- + +# [REQUIRED] Secret key for admin contract invocations (starts with S, 56 chars) ADMIN_SECRET_KEY= -# Public key matching ADMIN_SECRET_KEY (starts with G) — grants issuer role on login +# [REQUIRED] Public key matching ADMIN_SECRET_KEY (starts with G, 56 chars) +# Grants the "issuer" role when this wallet authenticates via SEP-10 ADMIN_PUBLIC_KEY= -# Dedicated secret key for signing SEP-10 challenges (starts with S) +# [REQUIRED] Dedicated secret key for signing SEP-10 challenge transactions (starts with S, 56 chars) SEP10_SERVER_KEY= -# SEP-10: home domain — used as the manage_data key prefix (' auth') +# SEP-10: home domain used as the manage_data key prefix (" auth") # Must match the domain in your stellar.toml (e.g. vaccichain.example.com) HOME_DOMAIN=localhost -# SEP-10: domain of this auth server — included in the web_auth_domain manage_data operation +# SEP-10: domain of this auth server -- included in the web_auth_domain manage_data operation # May differ from HOME_DOMAIN when the auth server is on a subdomain WEB_AUTH_DOMAIN=localhost -# Secret key for signing mint/revoke transactions (starts with S) +# [REQUIRED] Secret key for signing mint/revoke transactions (starts with S, 56 chars) ISSUER_SECRET_KEY= -# JWT signing secret — min 32 chars recommended; rotate to invalidate sessions +# [REQUIRED] JWT signing secret -- min 32 characters recommended +# Generate with: openssl rand -hex 32 +# Rotate this value to invalidate all active sessions JWT_SECRET= -# kid (key ID) for the current JWT signing key — increment on each rotation +# kid (Key ID) for the current JWT signing key (default: 1) +# Increment on each secret rotation so tokens carry the correct kid claim JWT_KEY_ID=1 -# Comma-separated list of previous JWT secrets still valid for verification +# Comma-separated list of previous JWT secrets still accepted for verification # during the transition window after a rotation. Remove entries once all # tokens signed with those secrets have expired. # Example: JWT_PREVIOUS_KEYS=oldSecret1,oldSecret2 JWT_PREVIOUS_KEYS= -# ── Backend server ──────────────────────────────────────────────────────────── +# -- Backend server --------------------------------------------------------------- + # TCP port for the Express backend (default: 4000) PORT=4000 # API version header value returned in all responses (default: 1) API_VERSION=1 -# Winston log level for the backend (default: info) +# Winston log level (default: info) # Allowed values: error | warn | info | http | debug LOG_LEVEL=info # Maximum JSON request body size accepted by the API (default: 10kb) +# Format: number + unit, e.g. "10kb", "1mb" BODY_LIMIT=10kb # Comma-separated list of allowed CORS origins (default: http://localhost:3000) @@ -76,23 +100,25 @@ SOROBAN_RPC_MAX_RETRIES=3 # Base transaction fee in stroops (1 XLM = 10,000,000 stroops; default: 100) SOROBAN_FEE=100 -# Fee bump tip in stroops to prioritise transactions (default: 0) +# Fee bump tip in stroops to prioritise transactions during congestion (default: 0) SOROBAN_TIP=0 -# How often (ms) the event indexer polls the backend for new contract events (default: 15000) +# How often (ms) the event indexer polls for new contract events (default: 15000) EVENT_POLL_INTERVAL_MS=15000 # Path to the SQLite database file used by the indexer (default: /data/vaccichain.db) DATABASE_PATH=/data/vaccichain.db -# ── Rate limiting ───────────────────────────────────────────────────────────── +# -- Rate limiting ---------------------------------------------------------------- + # Max SEP-10 challenge requests per IP per minute (default: 10) RATE_LIMIT_SEP10=10 # Max public verify requests per IP per minute (default: 60) RATE_LIMIT_VERIFY=60 -# ── Brute-force protection ──────────────────────────────────────────────────── +# -- Brute-force protection ------------------------------------------------------- + # Max failed /auth/verify attempts before blocking (default: 5) BRUTE_FORCE_MAX_ATTEMPTS=5 @@ -102,25 +128,25 @@ BRUTE_FORCE_WINDOW_MS=600000 # How long a blocked IP/wallet stays blocked in milliseconds (default: 900000 = 15 min) BRUTE_FORCE_BLOCK_MS=900000 -# ── Audit log ───────────────────────────────────────────────────────────────── +# -- Audit log -------------------------------------------------------------------- + # Path to append-only NDJSON audit log (default: ./audit.log) AUDIT_LOG_PATH=./audit.log -# ── Python analytics service ────────────────────────────────────────────────── +# -- Python analytics service ----------------------------------------------------- + # TCP port for the FastAPI analytics service (default: 8001) ANALYTICS_PORT=8001 -# Base URL the analytics service uses to reach the backend (default set by Compose) +# Base URL the analytics service uses to reach the backend (default: http://backend:4000) BACKEND_URL=http://backend:4000 # API key required to access protected analytics endpoints (rates, issuers, anomalies) # Generate with: openssl rand -hex 32 ANALYTICS_API_KEY= -# Python log level: DEBUG | INFO | WARNING | ERROR | CRITICAL (default: INFO) -# Reuses LOG_LEVEL defined in the backend section above; set to INFO for Python stdlib levels. +# -- Anomaly detection ------------------------------------------------------------ -# ── Anomaly detection ───────────────────────────────────────────────────────── # Mint count above which an issuer is flagged as anomalous (default: 50) ANOMALY_THRESHOLD=50 @@ -128,55 +154,66 @@ ANOMALY_THRESHOLD=50 ANOMALY_SCHEDULE_MINUTES=15 # Webhook URL to POST alerts to when anomalies are detected (leave empty to disable) -# Example (Slack): https://hooks.slack.com/services/TXXXXXXXXX/BXXXXXXXXX/XXXXXXXX +# Slack example: https://hooks.slack.com/services/TXXXXXXXXX/BXXXXXXXXX/XXXXXXXX ALERT_WEBHOOK_URL= # Webhook payload format: slack | pagerduty | email (default: slack) ALERT_WEBHOOK_TYPE=slack -# PagerDuty Events API v2 routing key — required when ALERT_WEBHOOK_TYPE=pagerduty +# PagerDuty Events API v2 routing key -- required when ALERT_WEBHOOK_TYPE=pagerduty # Set ALERT_WEBHOOK_URL=https://events.pagerduty.com/v2/enqueue PAGERDUTY_ROUTING_KEY= -# Recipient email address — required when ALERT_WEBHOOK_TYPE=email +# Recipient email address -- required when ALERT_WEBHOOK_TYPE=email +# Format: user@example.com ALERT_EMAIL_TO= -# ── Backup service ──────────────────────────────────────────────────────────── -# S3 Bucket for analytics DB backup +# -- Backup service --------------------------------------------------------------- + +# S3 bucket name for analytics DB backups S3_BUCKET_NAME= -# AWS Credentials for S3 access + +# AWS credentials for S3 access AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= + +# AWS region for S3 and Secrets Manager (default: us-east-1) AWS_REGION=us-east-1 -# Number of days to retain backups + +# Number of days to retain backups (default: 30) RETENTION_DAYS=30 + +# Secret key for the demo issuer account (testnet only, starts with S) DEMO_ISSUER_SECRET= + +# Admin secret for demo/testing purposes (starts with S) ADMIN_SECRET= -# ── Secrets Management (Production) ─────────────────────────────────────────── +# -- Secrets Management (Production) ---------------------------------------------- + # Enable AWS Secrets Manager for production (default: false) +# When true, secrets are fetched from AWS instead of .env USE_AWS_SECRETS=false # AWS Secrets Manager secret name/ARN containing all secrets # Example: arn:aws:secretsmanager:us-east-1:123456789012:secret:vaccichain/prod/stellar AWS_SECRET_NAME= -# AWS region for Secrets Manager (default: us-east-1) -AWS_REGION=us-east-1 +# -- Admin multi-signature -------------------------------------------------------- -# ── Admin multi-signature ───────────────────────────────────────────────────── # Number of approvals required for critical admin operations (default: 2) MULTISIG_THRESHOLD=2 -# Comma-separated list of wallet addresses authorised to approve proposals. -# Leave empty to allow any admin-role JWT holder to approve. +# Comma-separated list of wallet addresses authorised to approve proposals +# Leave empty to allow any admin-role JWT holder to approve # Example: MULTISIG_KEY_HOLDERS=GABC...,GDEF...,GHIJ... MULTISIG_KEY_HOLDERS= # How long a pending proposal stays valid in milliseconds (default: 3600000 = 1 hour) MULTISIG_PROPOSAL_TTL_MS=3600000 +# -- Patient consent -------------------------------------------------------------- -# Set to 'false' to waive consent requirement (e.g. jurisdiction config). +# Set to "false" to waive consent requirement (e.g. jurisdiction config) # Default: true (consent required before minting) REQUIRE_PATIENT_CONSENT=true diff --git a/README.md b/README.md index 7547f73..e1e45f2 100644 --- a/README.md +++ b/README.md @@ -268,9 +268,10 @@ docker compose up --build git clone https://github.com/your-org/vacci-chain.git cd vacci-chain cp .env.example .env -# Fill in your Stellar keys and contract IDs ``` +Open `.env` in your editor and fill in the required values. Every variable in [`.env.example`](.env.example) includes an inline comment explaining its purpose, expected format, and default value. Variables marked `[REQUIRED]` must be set before the backend will start. + ### 2. Setup secret scanning protection ```bash diff --git a/env.example b/env.example deleted file mode 100644 index f1053fb..0000000 --- a/env.example +++ /dev/null @@ -1,38 +0,0 @@ -# Stellar / Soroban -STELLAR_NETWORK=testnet -HORIZON_URL=https://horizon-testnet.stellar.org -SOROBAN_RPC_URL=https://soroban-testnet.stellar.org - -# Contract -VACCINATIONS_CONTRACT_ID= - -# Backend auth -ADMIN_SECRET_KEY= -ADMIN_PUBLIC_KEY= -SEP10_SERVER_KEY= -ISSUER_SECRET_KEY= -JWT_SECRET= - -# Ports -PORT=4000 -ANALYTICS_PORT=8001 - -# Rate Limiting (requests per IP per minute) -# SEP-10 challenge endpoint rate limit (default: 10 requests/minute per IP) -RATE_LIMIT_SEP10=10 -# SEP-10 verification endpoint rate limit (default: 10 requests/minute per IP) -RATE_LIMIT_SEP10_VERIFY=10 -# Public verification endpoint rate limit (default: 60 requests/minute per IP) -RATE_LIMIT_VERIFY=60 -# Verifier API key rate limit (default: 120 requests/minute per API key) -RATE_LIMIT_VERIFIER_KEY=120 - -# Audit log (append-only NDJSON file, defaults to backend/audit.log) -AUDIT_LOG_PATH=./audit.log - -# Backup service -S3_BUCKET_NAME= -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_REGION=us-east-1 -RETENTION_DAYS=30