# =============================================================================
# IRDB — IP Reputation Database — environment configuration
# =============================================================================
# Copy this file to `.env` and fill in the blanks.
# Generate 32-byte hex secrets with: openssl rand -hex 32
# =============================================================================

# -----------------------------------------------------------------------------
# Docker host ports (consumed by docker-compose.yml only)
# -----------------------------------------------------------------------------
# Host-side ports for the published service mappings. Container-internal
# ports are fixed at 8081 (api) and 8080 (ui) — only the host side here is
# remappable, so port collisions on the host can be resolved without
# rebuilding the images. If you change UI_PORT, also update PUBLIC_URL
# below so the browser-facing URL matches.
API_PORT=8081
UI_PORT=8080

# -----------------------------------------------------------------------------
# Shared (consumed by both api and ui containers)
# -----------------------------------------------------------------------------
# IRDB-format service token. The api uses this to authenticate the ui's
# calls; the ui presents it on every API request together with
# X-Acting-User-Id. Format: irdb_svc_<32 base32 chars>. Generate one with
# (note `--entrypoint php` — the api image's default entrypoint is a
# dispatcher with `api` / `migrate` modes, so ad-hoc PHP bypasses it):
#   docker compose run --rm -T --entrypoint php api -r 'require "/app/vendor/autoload.php";
#       echo (new App\Domain\Auth\TokenIssuer())->issue(App\Domain\Auth\TokenKind::Service), PHP_EOL;'
UI_SERVICE_TOKEN=

# -----------------------------------------------------------------------------
# api container
# -----------------------------------------------------------------------------
APP_ENV=production           # development | production
LOG_LEVEL=info

# Database
DB_DRIVER=sqlite             # sqlite | mysql
DB_SQLITE_PATH=/data/irdb.sqlite
DB_MYSQL_HOST=
DB_MYSQL_PORT=3306
DB_MYSQL_DATABASE=
DB_MYSQL_USERNAME=
DB_MYSQL_PASSWORD=

# OIDC role mapping (defaults applied if no group mapping matches)
OIDC_DEFAULT_ROLE=viewer     # viewer | none

# Reputation engine
SCORE_RECOMPUTE_INTERVAL_SECONDS=300
SCORE_REPORT_HARD_CUTOFF_DAYS=365

# Internal jobs
INTERNAL_JOB_TOKEN=                       # 32-byte hex (api refuses to boot if shorter than 32 hex chars)
# Comma- or whitespace-separated CIDR list of sources allowed to reach
# /internal/*. Empty (the default) means loopback-only (127.0.0.1/32 +
# ::1/128). The bundled `compose.scheduler.yml` shares the api's network
# namespace, so its calls hit loopback and need no extra entries.
# Production topologies that genuinely need extra sources (host cron on
# a private bridge, etc.) list them here AND mirror them into the api
# Caddyfile's @internal matcher.
INTERNAL_CIDR_ALLOWLIST=
# Comma- or whitespace-separated CIDR list of trusted reverse-proxy IPs
# whose `X-Forwarded-For` header Caddy should honour for REMOTE_ADDR
# rewriting. Default (loopback-only) means no XFF rewriting from any
# real client. Set to your reverse proxy's CIDR (e.g. "10.0.0.5/32") if
# the api sits behind one — required for accurate audit-log source IPs.
# SEC_REVIEW F25: never include broad RFC1918 ranges in deployments
# where untrusted neighbours can reach the api on the same docker bridge.
TRUSTED_PROXIES=
# HSTS header value — applied prod-only by both Caddyfiles. Default is
# 1 year + subdomains, NO preload (preload-listing is a one-way
# commitment; browser preload removals take months). Operators who
# want to apply for the HSTS preload list at https://hstspreload.org/
# set:
#     HSTS_HEADER="max-age=31536000; includeSubDomains; preload"
# SEC_REVIEW F60.
HSTS_HEADER=
JOB_RECOMPUTE_MAX_RUNTIME_SECONDS=240
JOB_RECOMPUTE_MAX_ROWS_PER_TICK=5000
JOB_AUDIT_RETENTION_DAYS=180
JOB_GEOIP_REFRESH_INTERVAL_DAYS=7

# Manual blocks / allowlist evaluator
# In-process cache TTL for the CidrEvaluator. Mutations invalidate explicitly,
# so this only matters for cross-replica visibility (per replica is fine).
CIDR_EVALUATOR_TTL_SECONDS=60

# Distribution endpoint
# Per-policy blocklist cache TTL. Mutations to policies / manual_blocks /
# allowlist invalidate explicitly; this is the cross-replica window.
BLOCKLIST_CACHE_TTL_SECONDS=30

# GeoIP / ASN enrichment
# Three pluggable MMDB providers — pick one. The on-disk paths below are
# provider-agnostic; the refresh-geoip job atomic-replaces them with the
# selected provider's files.
#   - dbip    (default, no auth required, CC BY 4.0 — UI shows attribution)
#   - maxmind (opt-in, requires MAXMIND_LICENSE_KEY)
#   - ipinfo  (opt-in, requires IPINFO_TOKEN — UI shows attribution)
GEOIP_ENABLED=true
GEOIP_PROVIDER=dbip
GEOIP_COUNTRY_DB=/data/geoip/country.mmdb
GEOIP_ASN_DB=/data/geoip/asn.mmdb
MAXMIND_LICENSE_KEY=
IPINFO_TOKEN=

# CORS — origin of the ui container (or future SPA frontend)
UI_ORIGIN=http://localhost:8080

# Rate limiting (public API)
API_RATE_LIMIT_PER_SECOND=60

# Public API docs viewer.
# `false` (default): /api/docs and /api/v1/openapi.yaml are not
# registered — Slim returns 404 — so the OpenAPI spec doesn't leak
# the full admin/internal route surface to unauthenticated callers.
# Set `true` for open APIs / dev environments where the menu is
# meant to be public. SEC_REVIEW F68.
API_DOCS_PUBLIC=false

# -----------------------------------------------------------------------------
# ui container
# -----------------------------------------------------------------------------
# (APP_ENV / LOG_LEVEL above are reused; the ui reads its own copies of those.)
PUBLIC_URL=http://localhost:8080

# Where the ui finds the api (internal docker network DNS)
API_BASE_URL=http://api:8081

# OIDC (Entra ID) — lives in ui only
OIDC_ENABLED=true
OIDC_ISSUER=https://login.microsoftonline.com/<tenant>/v2.0
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_REDIRECT_URI=https://reputation.example.com/oidc/callback

# Local admin — lives in ui only
LOCAL_ADMIN_ENABLED=true
LOCAL_ADMIN_USERNAME=admin
# Generate with: php -r "echo password_hash('s3cret', PASSWORD_ARGON2ID);"
LOCAL_ADMIN_PASSWORD_HASH=

# Optional BCP 47 locale fallback for date/time formatting (e.g. "de-CH",
# "en-GB"). The browser's locale wins; this is appended as a fallback so
# JavaScript's Intl.DateTimeFormat picks something sensible if the
# browser's preference isn't supported. Empty = browser-only.
UI_LOCALE=

# How often (seconds) the UI re-validates the cached user role/identity
# against `GET /api/v1/admin/me`. Lower = faster propagation of Entra
# group changes and explicit user-disable actions; higher = fewer api
# calls per active user. Default 300 (5 min).
UI_SESSION_REVALIDATE_SECONDS=300
