Skip to content

Configuration

Cryptographic parameters live in config.yaml (privacy budgets, data bounds, the library query plan). Deployment concerns come from environment variables, so the same file can be mounted anywhere unchanged.

Variable Default Purpose
CRM_AUTH_MODE dev dev or oidc
CRM_OIDC_ISSUER Identity provider base URL (alias: KEYCLOAK_URL)
CRM_OIDC_REALM Realm, if the provider uses one (alias: KEYCLOAK_REALM)
CRM_OIDC_CLIENT_ID Client ID (alias: KEYCLOAK_CLIENT_ID)
CRM_OIDC_CLIENT_SECRET Client secret (alias: KEYCLOAK_CLIENT_SECRET)
CRM_OIDC_VERIFY_AUDIENCE false Require the audience to match the client ID
CRM_LEDGER_ENDPOINT Distributed ledger receiving audit records (alias: LEDGER_ENDPOINT)
CRM_CM_ENDPOINT Compliance manager; records are posted to <endpoint>/input (alias: CM_ENDPOINT)
CRM_ILM_ENDPOINT Identity manager receiving audit records
CRM_CONFIG_PATH ./config.yaml Configuration file to load
CRM_AUDIT_LOG_PATH ./audit/crm-audit.jsonl Local audit trail
CRM_BUDGET_STORE_PATH ./audit/budgets.json Per-user budget state; unset to keep in memory
CRM_LOG_LEVEL INFO Log verbosity (alias: LOG_LEVEL)
CRM_MAX_REQUEST_BYTES 10485760 Largest accepted request body (10 MiB)

Authentication

Token verification is written against generic OpenID Connect — discovery via .well-known/openid-configuration, then local JWKS signature and expiry validation — rather than any one vendor's API, so it works with any conforming provider. Only asymmetric signature algorithms are accepted, which closes the algorithm-confusion attack where a token is signed with HMAC using the provider's public key as the secret.

The default dev mode accepts any bearer token without verifying it and derives a stable per-caller identity from a digest of the token, so budgets still separate correctly. It exists so the service is runnable without an identity provider. Never use it in a real deployment.

Privacy Budgets

Each user gets privacy_budget_limit epsilon. Every differential privacy request is checked against the remaining budget before it runs and debited only after it succeeds, so a failed query costs nothing. A request costing more than what remains is refused with 400; a user with nothing left gets 429.

Budgets persist to CRM_BUDGET_STORE_PATH, because a budget that resets when the container restarts is not a privacy guarantee. The store is single-node; a multi-replica deployment needs a shared backend, and BudgetStore is the seam to add one behind.

Audit Trail

Every request produces exactly one audit record — including requests that were refused, so a rejection is as traceable as a success. Records are appended as JSON lines locally and, when the corresponding endpoints are configured, forwarded to the ledger, compliance and identity services. Forwarding is best-effort and runs off the request path: an unreachable downstream service is logged and never fails the caller's request.

Sensitive material never reaches a record. Key material, plaintext, ciphertext, tokens, passwords and secret shares are redacted by key name, and bulk data is reduced to a size summary, so a record says how much was processed without saying what it was.