Skip to content

Configuration

Rod Christiansen edited this page Oct 8, 2026 · 2 revisions

Configuration

Everything is configured through environment variables. There is no config file. Most are read once at import, so changing one means restarting the container.

Required

Variable Default What it does
DATABASE_URL postgresql://reportmate:password@localhost:5432/reportmate PostgreSQL connection URL. Both postgresql:// and postgres:// schemes are accepted; a query string is ignored. The default is a placeholder, not a usable production value.

At least one credential must also be set, or nothing but the health probes is reachable — see the authentication table below.

Database

Variable Default What it does
DB_SSL true TLS to PostgreSQL. Set to false, 0, no, or disable for a local or in-cluster server with no TLS.
DB_POOL_MIN 2 Connections the pool keeps cached.
DB_POOL_MAX 40 Pool ceiling and maximum connections. Matched to Starlette's threadpool so every worker can hold one; lowering it below the threadpool size makes bursts queue.

Each pooled connection gets statement_timeout = 120s and a 130-second socket timeout. Neither is configurable by environment variable.

Authentication

Variable Default What it does
REPORTMATE_PASSPHRASE unset Shared fleet passphrase accepted in X-Client-Passphrase or X-API-PASSPHRASE. Comma-separated: the first entry is current, later entries are deprecated-but-accepted so you can rotate without an outage.
API_INTERNAL_SECRET unset Value for X-Internal-Secret, the dashboard-BFF-to-API hop. Also required for PUT /api/v1/settings.
DISABLE_AUTH false Bypasses all authentication. Development only — the app refuses to boot with it set in a production-like configuration.
TRUST_EASYAUTH_PRINCIPAL_HEADER false Honour X-MS-CLIENT-PRINCIPAL-ID as proof of identity. Only safe when a platform that strips inbound X-MS-* headers (Azure Easy Auth) fronts the app.
APP_ENV unset (falls back to ENVIRONMENT) Deployment marker. development, dev, local, test, testing allow DISABLE_AUTH; production, prod, staging, stage refuse it.

The startup guard treats a deployment as production-like when APP_ENV names a prod or staging environment or when either auth secret is configured, so you do not have to add a new marker for it to work.

OIDC (federated SSO)

Inert until ENABLE_OIDC_AUTH is true and both issuers and audience are set.

Variable Default What it does
ENABLE_OIDC_AUTH false Master switch.
OIDC_ISSUERS empty Comma-separated trusted issuer URLs. A token whose iss is not listed is refused before any crypto runs.
OIDC_AUDIENCE empty Comma-separated accepted audiences.
OIDC_JWKS_URI unset Pin a JWKS endpoint instead of using each issuer's /.well-known/openid-configuration.
OIDC_ALGORITHMS RS256,RS384,RS512,ES256,ES384 Accepted signature algorithms. none and HS* are excluded on purpose.
OIDC_ROLE_SCOPE_MAP empty JSON object merged over the built-in map, e.g. to grant scopes by group id.
OIDC_DEFAULT_SCOPES empty Comma-separated fallback when a valid token maps to nothing. Without it such a token is rejected.
OIDC_LEEWAY 60 Clock skew allowance in seconds.

The built-in role map recognises ReportMate.Read, ReportMate.Ingest, ReportMate.Admin, the bare names read / ingest / admin, and reader. admin grants all three scopes so an admin token can still perform a plain GET. Roles, groups, and scp / scope claims are all read and unioned.

Rate limiting

Variable Default What it does
RATE_LIMIT_PER_MINUTE 120 Per-device bucket, keyed on the X-Device-Serial header. Clients that do not send it are governed by the address bucket alone.
RATE_LIMIT_IP_PER_MINUTE 3000 Per-address flood shield, keyed on the first hop of X-Forwarded-For when present.

Both buckets are counted on every request. A caller presenting the valid internal secret bypasses them entirely.

Ingest

Variable Default What it does
MAX_DECOMPRESSED_BODY_BYTES 67108864 (64 MiB) Ceiling on what a compressed request body may inflate to. Enforced during the inflate, not after.
INGEST_FAILURES_RETENTION_DAYS 30 Age-out window for the ingest_failures table.
INGEST_FAILURES_MAX_ROWS 50000 Hard row cap so the table stays bounded under a fleet-wide outage.
INGEST_FAILURES_DEDUP_MINUTES 15 Repeats of the same (serial, reason, ip) within this window collapse into the first row.

Both purges run opportunistically on roughly 2% of failure writes.

Usage client floor

Oldest agent build whose application usage figures are trusted. A check-in from an older dated build (YYYY.MM.DD.HHMM) is stored as usual except for applications.dailyUsageHistory, which is dropped and recorded as a usage_client_too_old ingest failure at status 200. A missing or non-dated client version is never judged, so a metadata gap does not drop usage from a current client.

Variable Default What it does
USAGE_MIN_CLIENT_VERSION 2026.08.28.0000 Floor for macOS and any platform that is not Windows.
USAGE_MIN_CLIENT_VERSION_WINDOWS 2026.09.02.0926 Floor for Windows.

Everything else

Variable Default What it does
CORS_ORIGINS empty Comma-separated allowed origins. Empty means no cross-origin browser access.
EVENTS_CONNECTION unset Azure Web PubSub connection string for real-time event broadcast and GET /api/v1/negotiate. Without it both degrade gracefully.
RESPONSE_CACHE_MAX_ENTRIES 256 Ceiling on the in-process response cache. Past it, the oldest entry is evicted first.

Not configurable

Worth knowing because you may expect a knob and there is not one:

  • Event retention is 30 days and idempotency-key retention is 7 days, both hard-coded in routers/events.py and purged on roughly 1% of ingests.
  • Response cache TTLs (15–120s per namespace) are a table in dependencies.py. Only the entry cap is configurable.
  • The stale-agent threshold on /api/v1/installs/filters is 1 day.
  • The certificate search hard limit is 10000.
  • The "on campus" DNS servers and address prefixes used by /api/v1/hardware are constants in routers/fleet.py.
  • The application alias map used by the utilization endpoints is a list in dependencies.py.

Clone this wiki locally