# Copy this file to .env. Compose intentionally refuses to start without the # required values. Replace every credential before any public deployment. # Deployment selection is consumed by scripts/deploy-up.sh and # scripts/compose.sh; the application does not try to start an orchestrator. DEPLOYMENT_TARGET=compose DEPLOYMENT_ENV=development COMPOSE_PROJECT_NAME=who_need_help # Every independently deployable checkout must use its own image tags. This # prevents a test build from replacing the image used by production. APP_IMAGE=who-need-help:local SOCKET_PROXY_IMAGE=who-need-help:socket-proxy-local POSTGIS_IMAGE=who-need-help:postgis-local # split runs independently scalable web and worker replicas behind Traefik. # compact runs one combined Phoenix + Oban VM directly on HTTP_PORT. APP_TOPOLOGY=split # container starts the project-owned PostGIS service. external excludes that # service completely and connects every application role through DATABASE_URL. DATABASE_MODE=container # Only used when DEPLOYMENT_TARGET=kubernetes. KUBERNETES_MODE=kind KUBE_CONTEXT= KUBE_NAMESPACE=who-need-help KUBE_RELEASE=who-need-help KUBE_VALUES_FILE= HTTP_PORT=4010 # Bind the public Compose proxy to loopback when a reverse proxy runs on the # same host. The current VPN staging path needs an address reachable by its # verified tunnel topology, so choose this per deployment. HTTP_BIND_ADDRESS=0.0.0.0 # Attach the selected app service to the separately managed public Caddy # network. Keep disabled for ordinary local development. PUBLIC_EDGE_ENABLED=false PUBLIC_EDGE_NETWORK=who_need_help_public_edge PUBLIC_ROUTE_ID=who_need_help PUBLIC_UPSTREAM_NAME=who-need-help-local PUBLIC_UPSTREAM_PORT=4000 PUBLIC_HEALTH_PATH=/healthz/ready PUBLIC_WWW_REDIRECT=false # Legacy shared-edge settings remain while the submitted test deployment is # frozen. New application releases do not build or restart Caddy. After the # judging freeze, migrate these routes to the independent server_edge project. EDGE_COMPOSE_PROJECT_NAME=who_need_help_edge CADDY_IMAGE=who-need-help:caddy-local EDGE_BIND_ADDRESS=0.0.0.0 EDGE_HTTP_PORT=80 EDGE_HTTPS_PORT=443 PRIMARY_DOMAIN=whoneedhelp.com PRIMARY_UPSTREAM=who-need-help-production:4000 TEST_DOMAIN=test.whoneedhelp.com TEST_UPSTREAM=who-need-help-test:4000 MAILPIT_PORT=8027 MAILPIT_BIND_ADDRESS=127.0.0.1 DOCKER_SOCKET_GID=REPLACE_WITH_DOCKER_SOCKET_NUMERIC_GID # Comma-separated proxy IP/CIDR values whose X-Forwarded-* headers Traefik # accepts. Keep loopback locally; set the exact VPN proxy address for staging. TRAEFIK_TRUSTED_IPS=127.0.0.1/32 TRAEFIK_RETRY_ATTEMPTS=3 # Keep false for ordinary deployments. The isolated load profile enables the # unbound port-8080 API only inside its private Compose networks so its rolling # drill can observe when a drained backend leaves service. TRAEFIK_API_INSECURE=false # Docker-provider isolation and names. A second Compose project must use its # own project constraint, router/service name, Docker network, and Host rule. TRAEFIK_PROJECT_CONSTRAINT=who_need_help TRAEFIK_APP_NAME=who-need-help TRAEFIK_DOCKER_NETWORK=who_need_help_ingress TRAEFIK_ROUTER_RULE='PathPrefix(`/`)' PHX_HOST=localhost PHX_SCHEME=http PHX_URL_PORT=4010 # Optional comma-separated additional browser origins for Phoenix sockets. # Keep this empty when the site is reached only through PHX_HOST. For a local # Compose instance also exposed through an HTTPS tunnel, list both exact # origins, for example: https://dev.example.com,http://localhost:4010 PHX_CHECK_ORIGINS= # Android debug builds compile this origin into BuildConfig. The Docker # emulator uses adb reverse to expose the local Compose proxy on loopback. WNH_DEBUG_BASE_URL=http://localhost:4010 # Development/staging/release builds require a public HTTPS origin. Keep the value # environment-specific; scripts/ensure-local-public-origin.sh can derive it # from the three PHX_* values in the ignored .env. WNH_BASE_URL= # These local values preserve the existing five-second client freshness window # and fifteen-second request timeout. They are build inputs, not measured # production capacity recommendations. WNH_TRACKING_MIN_TIME_MS=5000 WNH_TRACKING_HTTP_TIMEOUT_MS=15000 WNH_ANDROID_VERSION_CODE=1 WNH_ANDROID_VERSION_NAME=0.1.0 # Public Firebase Android client configuration. These values are embedded in # the APK and are not service-account credentials. Set all four per environment # to enable native FCM registration, or leave all four empty to disable it. WNH_FIREBASE_APPLICATION_ID= WNH_FIREBASE_API_KEY= WNH_FIREBASE_PROJECT_ID= WNH_FIREBASE_GCM_SENDER_ID= # Verified Android App Links are configured by the web deployment rather than # embedded as secrets in the application. Use # org.whoneedhelp.mobile.development with the development certificate on DEV, # org.whoneedhelp.mobile.staging with the staging certificate on test, and # org.whoneedhelp.mobile with every active Play signing certificate on # production. Keep both empty until the matching signed APK/AAB is available. ANDROID_APP_LINKS_PACKAGE_NAME= ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS= # Production-only evidence from Google Play Console. This must contain the # Play App Signing certificate fingerprint(s), not the local upload key, and # every value must also appear in ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS. # Development and test leave this empty. ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS= # Public identifier of the locally held Google Play upload key. The private # keystore and its randomized password live outside the repository under # ~/.config/who_need_help/android-release/. WNH_ANDROID_SIGNING_KEY_ALIAS=who-need-help-upload # The DEV-domain APK uses a stable signing identity under # ~/.config/who_need_help/android-development/. WNH_ANDROID_DEVELOPMENT_SIGNING_KEY_ALIAS=who-need-help-development # The test-domain staging APK uses a different stable signing identity under # ~/.config/who_need_help/android-staging/. This keeps App Link verification # reproducible without reusing the future production upload key. WNH_ANDROID_STAGING_SIGNING_KEY_ALIAS=who-need-help-staging WNH_ANDROID_TEST_API_MATRIX="24 30 34 37.0" WNH_ANDROID_TEST_DATA_PARTITION_SIZE=1G # Public raster tile template used by MapLibre. Use a provider whose policy and # capacity match the deployment before a public launch. MAP_TILE_URL=https://tile.openstreetmap.org/{z}/{x}/{y}.png # Optional verified GitHub linking. Leave both empty until a GitHub OAuth App # exists. Its callback URL must be: # https://YOUR_PHX_HOST/auth/social/github/callback GITHUB_OAUTH_CLIENT_ID= GITHUB_OAUTH_CLIENT_SECRET= # Optional endpoint and timeout overrides exist for the isolated boundary drill. # Leave them empty for GitHub's official endpoints and Req's library timeouts. GITHUB_OAUTH_BASE_URL= GITHUB_OAUTH_AUTHORIZE_URL= GITHUB_OAUTH_TOKEN_URL= GITHUB_OAUTH_USER_URL= GITHUB_OAUTH_HTTP_CONNECT_TIMEOUT_MS= GITHUB_OAUTH_HTTP_RECEIVE_TIMEOUT_MS= # Optional Google OpenID Connect registration and sign-in. Leave both empty # until a Google OAuth Web client exists. Android Credential Manager obtains # the public client ID from Phoenix at runtime; never copy the secret into the # APK or Gradle configuration. The browser callback URL must be: # https://YOUR_PHX_HOST/auth/google/callback GOOGLE_OAUTH_CLIENT_ID= GOOGLE_OAUTH_CLIENT_SECRET= # Comma-separated Android OAuth client IDs allowed as the verified azp claim # for Credential Manager cross-client ID tokens. Keep environments isolated. GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS= # Leave endpoint and timeout overrides empty for Google's discovery endpoint # and Req defaults. The base URL override exists for isolated protocol tests. GOOGLE_OAUTH_BASE_URL= GOOGLE_OAUTH_HTTP_CONNECT_TIMEOUT_MS= GOOGLE_OAUTH_HTTP_RECEIVE_TIMEOUT_MS= # Optional provider-neutral HTTP push boundary. Leave both endpoint and token # empty to disable product push jobs. When enabled, all four numeric values are # required deployment inputs; the project does not claim universal production # values for them. PUSH_HTTP_ENDPOINT= PUSH_HTTP_BEARER_TOKEN= PUSH_HTTP_MAX_ATTEMPTS= PUSH_HTTP_RECEIVE_TIMEOUT_MS= PUSH_HTTP_CONNECT_TIMEOUT_MS= PUSH_HTTP_RETRY_DELAY_MS= # Direct browser Web Push. Generate one VAPID key pair per environment with # scripts/generate-vapid-env.sh and keep the private key only in that # environment's .env. The subject must be a mailto: or HTTPS contact owned by # the operator. WEB_PUSH_VAPID_PUBLIC_KEY= WEB_PUSH_VAPID_PRIVATE_KEY= WEB_PUSH_VAPID_SUBJECT= # Native Android push through Firebase Cloud Messaging. Either mount the # service-account JSON read-only and set its absolute in-container path, or put # standard Base64 of that JSON in the single environment file. Never set both. # Leave all three values empty to disable FCM. FCM_PROJECT_ID= FCM_SERVICE_ACCOUNT_FILE= FCM_SERVICE_ACCOUNT_JSON_BASE64= POSTGRES_DB=who_need_help POSTGRES_USER=postgres POSTGRES_PASSWORD=replace-with-a-local-or-deployment-secret # Required only by scripts/dev-scale-seed.sh for the local synthetic viewer. # Generate a distinct value; never reuse a real user or deployment password. DEV_SCALE_VIEWER_PASSWORD=replace-with-a-random-local-fixture-password DATABASE_URL=ecto://postgres:replace-with-url-encoded-password@db/who_need_help # Optional absolute host directory containing PostgreSQL Unix sockets. When it # is set in external mode, Compose mounts it read-only and Ecto uses it instead # of the hostname in DATABASE_URL. Leave empty for container or remote TCP DBs. DATABASE_SOCKET_DIR= WEB_POOL_SIZE=4 WORKER_POOL_SIZE=2 MIGRATE_POOL_SIZE=2 COMBINED_POOL_SIZE=4 OBAN_MAINTENANCE_CONCURRENCY=2 OBAN_PUSH_CONCURRENCY=1 OBAN_MAIL_CONCURRENCY=1 WEB_REPLICAS=2 WORKER_REPLICAS=2 # Maximum simultaneously existing Erlang ports (files, sockets and drivers). # Keeping this explicit prevents a host's very large nofile ulimit from making # every BEAM instance preallocate a multi-gigabyte port table. ERLANG_PORT_LIMIT=65536 SECRET_KEY_BASE=generate-with-mix-phx-gen-secret HANDOVER_SECRET=generate-an-independent-random-secret RELEASE_COOKIE=generate-an-independent-beam-cluster-cookie METRICS_TOKEN=generate-an-independent-random-bearer-token # Use Mailpit locally or a transactional SMTP relay in public environments. EMAIL_DELIVERY_PROVIDER=smtp SMTP_RELAY=mailpit SMTP_PORT=1025 SMTP_USERNAME= SMTP_PASSWORD= SMTP_AUTH=never SMTP_TLS=never SMTP_SSL=false EMAIL_FROM_NAME="Who Need Help" EMAIL_FROM_ADDRESS=contact@example.com # Optional monitored inbox used as Reply-To for support and legal correspondence. SUPPORT_INBOX_ADDRESS= # Operator email alerts are disabled by default because the permission-scoped # staff workspace is the canonical queue. Set immediate only when a monitored # mailbox should receive one metadata-only alert for each newly verified case. # Ongoing conversation stays in the staff workspace and in-app inbox. Both # operator alerts and user-facing support updates use the separate Oban mail queue. SUPPORT_OPERATOR_EMAIL_MODE=disabled # Pilot policy: an anonymous support/removal email must be confirmed within one # day. Confirmed case links remain usable for one year. Both values are seconds # and can be changed without rebuilding the release. PUBLIC_CONTACT_VERIFICATION_MAX_AGE_SECONDS=86400 PUBLIC_CASE_ACCESS_MAX_AGE_SECONDS=31536000 CODEX_SESSION_ID=copy-the-main-local-codex-session-id # Shared PostgreSQL-backed pilot policies. This explicit value makes an # operator's effective policy reviewable without inspecting the image. Remove # the value to use the same compiled pilot default; set exactly {} only to # disable every counter in an isolated benchmark/test environment. # Shape: {"action_name":{"limit":POSITIVE_INTEGER,"window_seconds":POSITIVE_INTEGER}} # Authentication delivery uses paired email/IP actions: # registration_email + registration_ip, magic_link_email + magic_link_ip, # password_login_email + password_login_ip, email_change_email + email_change_ip. # Public support intake uses support_request (account/email scope) together with # support_request_ip (trusted client-IP scope). IP ceilings are intentionally # higher than account/email ceilings so shared networks are not treated as one # person. These are initial pilot product limits, not universal recommendations. RATE_LIMIT_POLICIES_JSON={"registration_email":{"limit":4,"window_seconds":3600},"registration_ip":{"limit":120,"window_seconds":3600},"magic_link_email":{"limit":4,"window_seconds":3600},"magic_link_ip":{"limit":120,"window_seconds":3600},"password_login_email":{"limit":10,"window_seconds":900},"password_login_ip":{"limit":300,"window_seconds":900},"email_change_email":{"limit":3,"window_seconds":86400},"email_change_ip":{"limit":60,"window_seconds":3600},"support_request":{"limit":5,"window_seconds":86400},"support_request_ip":{"limit":120,"window_seconds":3600},"content_removal_notice":{"limit":20,"window_seconds":86400},"content_removal_notice_ip":{"limit":120,"window_seconds":3600}}