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
121 changes: 79 additions & 42 deletions .env.example
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 ('<HOME_DOMAIN> auth')
# SEP-10: home domain used as the manage_data key prefix ("<HOME_DOMAIN> 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)
Expand All @@ -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

Expand All @@ -102,81 +128,92 @@ 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

# How often (minutes) the anomaly detection job runs (default: 15)
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 0 additions & 38 deletions env.example

This file was deleted.

Loading