# Database Credentials for all services # These are used by the 'postgres' service to initialize the database # These are used by 'backend' (Go) and 'sabredav' (PHP) POSTGRES_USER=myuser POSTGRES_PASSWORD=mypassword POSTGRES_DB=mydb # and to connect to the 'postgres' service on the Docker network POSTGRES_HOST=postgres POSTGRES_PORT=5432 # JWT_SECRET_KEY — REQUIRED, FAIL-CLOSED. The backend refuses to start with an # empty, weak (<32 chars), or known-placeholder value, because a shared/public # signing key lets anyone forge an admin JWT. Generate a strong random key: # openssl rand -hex 32 JWT_SECRET_KEY= # S3/R2 Configuration (for image storage) # Dev: Uses local Rustfs container (see compose.yml) # Prod: Use Cloudflare R2 credentials S3_ENDPOINT=http://localhost:9000 # Public URL the BROWSER fetches images from. This is HOST-SPECIFIC: it must be # the machine's current LAN IP (DHCP changes it). Run `hostname -I` to check. # A stale IP makes images fail to load even though the objects exist in Rustfs. S3_PUBLIC_URL=http://192.168.0.45:9000 S3_ACCESS_KEY=rustfsadmin S3_SECRET_KEY=rustfsadmin S3_BUCKET=crussell S3_PROFILE_PICS_BUCKET=crussell-profile-pics AWS_REGION=eu-west-2 # Set DAV_SKIP_INIT=1 to skip CardDAV server initialization (e.g., in CI/test environments). DAV_SKIP_INIT=1 # Prod only: Cloudflare R2 (overrides S3_* vars in non-dev builds). # Local dev uses the S3_* vars above (from .env). Not needed for local builds. R2_ENDPOINT= # Required for production object storage — the prod S3 client (backend/internal/s3/s3.go, # via getEnv) reads all four below; not needed for local dev builds. R2_ACCESS_KEY= R2_SECRET_KEY= R2_BUCKET=crussell R2_PUBLIC_URL= # Square Payment Gateway SQUARE_ACCESS_TOKEN= SQUARE_LOCATION_ID= SQUARE_TERMINAL_DEVICE_ID= SQUARE_ENVIRONMENT=mock # SQUARE_ALLOW_REAL_API — dev-build safety valve. In a `//go:build dev` build the # backend HARD-FAILS (refuses to construct the client) when SQUARE_ENVIRONMENT # is 'production', because a leftover/typo'd production env + real key in a dev # shell would create real charges. Set SQUARE_ALLOW_REAL_API=1 ONLY to # deliberately route a dev build to the real production API. Never set in a # deployed production build. SQUARE_ALLOW_REAL_API= # 2FA — merchant-level authorization gate on saved-card online payments (NOT # PSD2 SCA; Square buyer verification is the SCA mechanism and is wired for # new-card charges). Kept as an additional fraud control until Square buyer # verification is wired for saved-card charges. Enforcement is FAIL-CLOSED: # ON unless REQUIRE_2FA explicitly disables it (false/0/off/no, case-insensitive) # OR SQUARE_ENVIRONMENT explicitly equals one of mock/dev/development/test. # Empty or unknown SQUARE_ENVIRONMENT values are treated as production-enforced # (a mistyped env var can never silently disarm the gate; the backend logs a # startup warning in that case). Set REQUIRE_2FA=false only in controlled # environments. # Code delivery: there is NO email/SMS transport yet. The verification code is # delivered via the server log (a [2FA]-prefixed line). In enforced/production # environments an operator must relay the logged code to the user out-of-band; # the API never returns the code while enforcement is ON. REQUIRE_2FA=true # TWO_FACTOR_PEPPER — server-side pepper for HMAC-hashing 2FA codes. REQUIRED # in production builds: code issuance FAILS CLOSED when it is unset (an # unsalted SHA-256 digest in the 1M code space would be offline-brute-forceable # from a log/DB leak), mirroring JWT_SECRET_KEY's fail-fast stance. Optional # only in dev/test builds, where an unset pepper falls back to the legacy # digest with a one-time warning. Generate with: # openssl rand -base64 32 TWO_FACTOR_PEPPER= # TWO_FACTOR_ALLOW_LOG_DELIVERY — defaults false. Production 2FA code issuance # FAILS CLOSED without a delivery channel: there is no email/SMS transport yet, # so the ONLY production channel is the operator's explicit opt-in to the # insecure server-log delivery ([2FA] prefix — anyone with backend log access # can defeat the gate on saved-card charges). MUST be set to true to deliver # 2FA codes via the server log in production until email/SMS lands. Dev/test # builds always deliver via the log and never consult this flag. TWO_FACTOR_ALLOW_LOG_DELIVERY=false # SNAPSHOT_ENC_KEY — base64-encoded 32-byte AES-256 key for encrypting stored # square_request_snapshot rows (buyer PII: email + ccof card tokens) at rest in # non-mock (production/sandbox) deployments. If unset/invalid, snapshots fall # back to PLAINTEXT with a one-time CRITICAL log warning (money-safety first: # the replayable snapshot must not be lost). Generate with: # openssl rand -base64 32 SNAPSHOT_ENC_KEY= # Webhook config MUST exactly match the Square Dashboard webhook subscription # (URL + signature key). If SQUARE_WEBHOOK_NOTIFICATION_URL is left unset it # defaults to http://localhost:8080/webhooks/square, which is fail-closed (503 # without the signing key, 403 on missing/bad signature). Leave both empty if # you do not use webhooks. SQUARE_WEBHOOK_SIGNATURE_KEY= SQUARE_WEBHOOK_NOTIFICATION_URL= # Frontend (public — safe for the browser). Square Web Payments SDK: # VITE_SQUARE_APPLICATION_ID — client-side application ID (sandbox IDs start with "sandbox-") # VITE_SQUARE_LOCATION_ID — Square location ID # VITE_SQUARE_ENVIRONMENT — 'mock' | 'sandbox' | 'production'. Local dev: 'mock' renders the # frontend's built-in mock card form (tokens only; pairs with # SQUARE_ENVIRONMENT=mock above). NEVER set 'mock' in production. VITE_SQUARE_APPLICATION_ID= VITE_SQUARE_LOCATION_ID= VITE_SQUARE_ENVIRONMENT=mock # Test Database (separate from main DB) # Used by testutils/testdb for running tests without corrupting dev data TEST_DB_HOST=localhost TEST_DB_DSN= # Dev/CI mode — set to "true" to enable mock services # Disables zxcvbn password checks, skips artificial Square mock delays, # skips DAV sync, and relaxes production guardrails GO_TESTING= # CardDAV (SabreDAV) — profile photo sync DAV_BASE_URL=http://localhost:8080 # DAV_ADMIN_PASSWORD — REQUIRED, FAIL-CLOSED. sabredav/server.php refuses to # start when unset or set to a known weak/default value ('admin' etc.) — this # server exposes customer PII vCards, so no public default credential is ever # acceptable. Generate a strong random value: # openssl rand -hex 32 DAV_ADMIN_PASSWORD= # Logging # Set to "true" to disable ANSI color escape sequences in log output NO_COLOR= # Frontend VITE_BACKEND_URL=http://localhost:8080 # Backend CORS allowlist — comma-separated list of allowed frontend origins # (read by the backend CORS middleware, see backend/main.go). Falls back to # http://localhost:5173 when unset. FRONTEND_ORIGIN=http://localhost:5173 # TRUST_PROXY_HEADERS — defaults false. Set to true ONLY when a trusted proxy # (nginx and/or the Cloudflare edge) sits between clients and this backend and # overwrites X-Real-IP / CF-Connecting-IP with the real client IP. When true, # the per-IP rate limiter keys requests on those proxy-set headers and main.go # registers chi's ClientIPFromHeader("X-Real-IP") middleware. MUST be true # behind nginx/Cloudflare, or every request keyed by IP collapses onto the # proxy's IP — one client exhausting the limit throttles everyone, and per-IP # limiter protection is effectively bypassed. MUST stay false when the backend # is origin-exposed: a client talking directly to the backend could otherwise # rotate X-Real-IP/CF-Connecting-IP to bypass per-IP rate limiting. TRUST_PROXY_HEADERS=false # Local S3 (Rustfs) — requires GO_TESTING=1 or dev build tag # These are dev-only overrides used by the dev S3 implementation RUSTFS_ENDPOINT=http://rustfs:9000 RUSTFS_ACCESS_KEY=rustfsadmin RUSTFS_SECRET_KEY=rustfsadmin RUSTFS_BUCKET=crussell