# Environment Variables EDRI behavior is configured primarily through environment variables. This page documents all variables the framework reads, their defaults, and what they control. --- ## Runtime mode ### `ENVIRONMENT` | Attribute | Value | |---------------|------------------------------------------| | Values | `development` / `production` | | Default | `production` when unset or empty | The value is case-insensitive. Only an explicit `development` selects development. An unset or empty variable is `production`, and any other value (for example `prod` or `staging`) is also treated as `production` and logs a warning at startup, so a missing or mistyped value can never enable the development-only endpoints. Set `ENVIRONMENT=development` for local work. The variable is named `ENVIRONMENT`; `EDRI_ENVIRONMENT` is not read, and setting it alone logs a warning. **Behavior:** | Aspect | Development | Production | |---------------------------|------------------|-------------------| | `/edri/docs`, `/edri/topology` | Enabled | Disabled | | API timing in responses | Included | Stripped | | Watcher debounce delay | 1 second | 10 seconds | | Error detail | Full traceback | Minimal | --- ## Health checks ### `EDRI_HEALTH_CHECK_TIMEOUT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `10` | Interval (seconds) between health-check pings to Manager processes. ### `EDRI_HEALTH_CHECK_FAILURE_LIMIT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `3` | Consecutive missed health checks before a Manager is considered unhealthy. ### `EDRI_HEALTH_CHECK_LOG` | Attribute | Value | |---------------|------------------| | Type | `bool` | | Default | `true` | When `false`, the periodic `HealthCheck` event is not written to the debug log as it passes through the Router, Broker, Scheduler, and Manager processes. Only the routine per-cycle trace lines are affected; missed-health-check warnings and errors, and the `/edri/health` endpoints, are unchanged. --- ## CORS ### `EDRI_CORS_ORIGINS` | Attribute | Value | |---------------|------------------| | Type | `str \| None` | | Default | `None` | Allowed CORS origins (comma-separated if multiple). ### `EDRI_CORS_HEADERS` | Attribute | Value | |---------------|------------------| | Type | `str \| None` | | Default | `None` | Allowed CORS headers. ### `EDRI_CORS_CREDENTIALS` | Attribute | Value | |---------------|------------------| | Type | `bool` | | Default | `False` | Whether `Access-Control-Allow-Credentials` is set. ### `EDRI_CORS_MAX_AGE` | Attribute | Value | |---------------|------------------| | Type | `str \| None` | | Default | `None` | Max age (seconds) for CORS preflight caching. --- ## API responses > **Boolean convention:** Truthy values (case-insensitive): `1`, `true`, `yes`, `on`. ### `EDRI_API_RESPONSE_TIMEOUT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `60` | How long (seconds) the API layer waits for an event response. ### `EDRI_API_SENDER_THREADS` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `32` | Size of the Broker's outbound {ref}`non-blocking-outbound-send-sender-pool` — how many simultaneously slow clients can be served before further sends queue. See {doc}`/broker`. ### `EDRI_API_RESPONSE_WRAPPED` | Attribute | Value | |---------------|------------------| | Type | `bool` | | Default | `True` | Controls response shape: * `True` — wrapped: `{"data": {...}, "status": "OK"}` * `False` — unwrapped: `{"status": "OK", ...fields...}` ### `EDRI_API_RESPONSE_TIMING` | Attribute | Value | |---------------|-----------------------------| | Type | `bool` | | Default | `True` (dev), `False` (prod)| Include timing information in API responses. ### `EDRI_API_CACHE_CONTROL` | Attribute | Value | |---------------|------------------------------| | Type | `str` | | Default | `max-age=0, must-revalidate` | Value for the `Cache-Control` HTTP response header. ### `EDRI_API_CACHE_HEADERS` | Attribute | Value | |---------------|------------------| | Type | `str \| None` | | Default | Value of `EDRI_CORS_HEADERS` | Additional headers appended to the `Vary` response header for cache control. ### `EDRI_API_ENABLE_DOCUMENTATION` | Attribute | Value | |---------------|------------------| | Type | `bool` | | Default | dev: `True`, prod: `False` | When enabled, the built-in API documentation page is available at `/edri/docs`. Disable in production to hide internal API structure. ### `EDRI_API_ENABLE_TOPOLOGY` | Attribute | Value | |---------------|------------------| | Type | `bool` | | Default | dev: `True`, prod: `False` | When enabled, `GET /edri/topology` exposes the live routing graph — see {doc}`/router`. Disabled by default in production; while disabled, REST returns `404` and the endpoint is absent from the generated `/edri/docs` page. Over gRPC, `Topology` still appears in reflection (this flag isn't consulted at schema-generation time) but every call fails `NOT_FOUND` — see {doc}`/grpc`. ### `EDRI_API_EXPOSE_ERROR_DETAILS` | Attribute | Value | |---------------|------------------| | Type | `bool` | | Default | dev: `True`, prod: `False` | When enabled, error responses include detailed exception messages and context. Disable in production to avoid leaking internal information. --- ## Server binding and TLS ### `EDRI_HOST` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `localhost` | Host address to bind the API server to. ### `EDRI_PORT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `8080` | Port to bind the API server to. ### `EDRI_GRPC_HOST` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `localhost` | Host address `GRPCListener` binds to — see {doc}`/grpc`. Independent of `EDRI_HOST`; only read if `start_grpc()` is called. ### `EDRI_GRPC_PORT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `50051` | Port `GRPCListener` binds to — see {doc}`/grpc`. Independent of `EDRI_PORT`; only read if `start_grpc()` is called. ### `EDRI_SSL_KEY` | Attribute | Value | |---------------|------------------| | Type | `str \| None` | | Default | `None` | Path to the TLS private key file. ### `EDRI_SSL_CERTIFICATE` | Attribute | Value | |---------------|------------------| | Type | `str \| None` | | Default | `None` | Path to the TLS certificate file. --- ## Switch / Router connection ### `EDRI_SWITCH_HOST` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `localhost` | Switch server hostname or IP address. ### `EDRI_SWITCH_PORT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `8899` | Switch server TCP port. ### `EDRI_SWITCH_SECRET` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | *(unset)* | Pre-shared key the Switch and every Connector authenticate with, at least 32 bytes. The Switch refuses to bind to any non-loopback address without it, and a Connector refuses to dial a non-loopback Switch without it; on loopback both run unauthenticated. The key is cluster-wide — it authenticates against outsiders, not between members. Read by `edri.protocol.keys`. ### `EDRI_SWITCH_SECRET_FILE` | Attribute | Value | |---------------|------------------| | Type | `str` (path) | | Default | *(unset)* | Path to a file holding one key per line; blank lines and `#` comments are ignored. Multiple lines let a rotation keep the old and new key valid at once. Takes precedence over `EDRI_SWITCH_SECRET`. Prefer this over the inline value, which is readable through `/proc`, `docker inspect`, and pod specs. ### `EDRI_SWITCH_MAX_MESSAGE` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `67108864` (64 MiB) | Largest frame payload the Switch will accept and a Connector will send. Read on both sides independently, so raise it on every peer or not at all. Bulk data belongs on a {doc}`/shared_memory_pipe`. ### `EDRI_SWITCH_STATUS_ENABLED` | Attribute | Value | |---------------|------------------| | Type | `bool` | | Default | `True` | Start the Switch's read-only HTTP {ref}`switch-status-endpoint`. ### `EDRI_SWITCH_STATUS_HOST` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `localhost` | Bind address for the Switch status endpoint. ### `EDRI_SWITCH_STATUS_PORT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `8898` | TCP port for the Switch status endpoint. --- ## Cache ### `EDRI_CACHE_TIMEOUT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `30` | How long (seconds) events stay in the Router cache. Used to survive transient Switch outages. ### `EDRI_CACHE_INFO_MESSAGE` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `60` | Interval (seconds) at which the Router logs cache statistics (current size, etc.). --- ## Router outbound queues Per-connection backpressure for the Router's non-blocking fan-out. See {doc}`/router`. ### `EDRI_ROUTER_OUTBOUND_QUEUE_SOFT_LIMIT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `800` | Outbound backlog (queued events) at which a Manager is flagged `WARNING`. Events keep queueing. ### `EDRI_ROUTER_OUTBOUND_QUEUE_HARD_LIMIT` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `5000` | Outbound backlog at which a Manager is disconnected and restarted; it replays from the Router cache on re-subscribe. ### `EDRI_ROUTER_OUTBOUND_JOIN_TIMEOUT` | Attribute | Value | |---------------|------------------| | Type | `float` | | Default | `5.0` | Seconds the Router waits for an outbound worker thread to drain and stop when a connection is unregistered or the Router shuts down. --- ## Uploads (edri-upload-files-prefix)= ### `EDRI_UPLOAD_FILES_PREFIX` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `edri_` | Filename prefix for uploaded files stored on disk. (edri-upload-files-path)= ### `EDRI_UPLOAD_FILES_PATH` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `/tmp/upload` | Directory where uploaded files are stored. > **Important:** EDRI does not automatically delete uploaded files from this directory. Uploaded files may be processed asynchronously by managers or workers, so the framework cannot reliably know when a file is no longer needed. Application code must move, delete, or otherwise manage uploaded files. See {doc}`/uploads`. --- (templates-and-assets)= ## Templates and assets ### `EDRI_TEMPLATE_PATH` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `templates` | Directory containing Jinja2 template files (relative to the working directory, or absolute). ### `EDRI_ASSETS_PATH` | Attribute | Value | |---------------|------------------| | Type | `str` | | Default | `assets` | Directory for static files served for URLs that no event route matches (relative to the working directory, or absolute). Read when `edri.config.setting` is imported; a relative path is resolved against the working directory on each request. Everything in it is public — see {doc}`/static_files`. --- ## Request body limits ### `EDRI_MAX_BODY_SIZE` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `4 MiB` (`4096 * 1024`) | Maximum accepted HTTP request body size in bytes. ### `EDRI_MAX_MESSAGE_SIZE` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `4 MiB` (value of `EDRI_MAX_BODY_SIZE`) | Maximum accepted WebSocket message (text or binary) size in bytes. (edri-asgi-options)= ### `EDRI_ASGI_OPTIONS` | Attribute | Value | |---------------|------------------| | Type | `str \| None` | | Default | `None` | JSON object with arbitrary options passed directly to the ASGI server configuration (`uvicorn.Config`, `hypercorn.Config`, or `daphne.Server`). Keys and values depend on the server in use — see its documentation for available parameters. ```sh # example EDRI_ASGI_OPTIONS='{"ws_ping_interval": 10, "ws_ping_timeout": 10}' ``` With uvicorn, `workers` runs N worker processes instead of one, each sharing the same listening socket and feeding the same Broker queue — see {ref}`multi-worker-listener-uvicorn-workers` for measured throughput and how to size it. ```sh EDRI_ASGI_OPTIONS='{"workers": 4}' ``` ### `EDRI_CHUNK_SIZE` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `256 KiB` (`256 * 1024`) | Default `SharedMemoryPipe` slot size (bytes). (edri-max-file-size)= ### `EDRI_MAX_FILE_SIZE` | Attribute | Value | |---------------|------------------| | Type | `int` | | Default | `100 MiB` (`1024 * 1024 * 100`) | Maximum accepted file upload size in bytes. > **Note:** Default is `100 MiB` (`1024 * 1024 * 100`). The current release does not implement a special "unlimited" value. Use a large positive integer if you need a higher upload limit. --- ## Quick reference table | Variable | Default | Description | |---------------------------------|---------------------------|---------------------------------------------| | `ENVIRONMENT` | `production` | Runtime mode | | `EDRI_HEALTH_CHECK_TIMEOUT` | `10` | Health check interval (seconds) | | `EDRI_HEALTH_CHECK_FAILURE_LIMIT` | `3` | Max consecutive health check failures | | `EDRI_HEALTH_CHECK_LOG` | `true` | Trace periodic health-check events in the log | | `EDRI_CORS_ORIGINS` | `None` | Allowed CORS origins | | `EDRI_CORS_HEADERS` | `None` | Allowed CORS headers | | `EDRI_CORS_CREDENTIALS` | `False` | Allow CORS credentials | | `EDRI_CORS_MAX_AGE` | `None` | CORS preflight max age (seconds) | | `EDRI_API_RESPONSE_TIMEOUT` | `60` | API response timeout (seconds) | | `EDRI_API_SENDER_THREADS` | `32` | Broker outbound sender-pool size | | `EDRI_API_RESPONSE_WRAPPED` | `True` | Wrap API responses in `data` envelope | | `EDRI_API_RESPONSE_TIMING` | dev: `True`, prod: `False`| Include timing in responses | | `EDRI_API_CACHE_CONTROL` | `max-age=0, must-revalidate` | Cache-Control header value | | `EDRI_API_CACHE_HEADERS` | `EDRI_CORS_HEADERS` | Additional Vary headers | | `EDRI_API_ENABLE_DOCUMENTATION` | dev: `True`, prod: `False`| Enable built-in API docs at `/edri/docs` | | `EDRI_API_ENABLE_TOPOLOGY` | dev: `True`, prod: `False`| Enable `GET /edri/topology` (REST) / `Topology` (gRPC) | | `EDRI_API_EXPOSE_ERROR_DETAILS` | dev: `True`, prod: `False`| Include exception details in error responses | | `EDRI_HOST` | `localhost` | API server bind host | | `EDRI_PORT` | `8080` | API server bind port | | `EDRI_GRPC_HOST` | `localhost` | gRPC server bind host | | `EDRI_GRPC_PORT` | `50051` | gRPC server bind port | | `EDRI_SSL_KEY` | `None` | TLS private key path | | `EDRI_SSL_CERTIFICATE` | `None` | TLS certificate path | | `EDRI_SWITCH_HOST` | `localhost` | Switch host | | `EDRI_SWITCH_PORT` | `8899` | Switch port | | `EDRI_SWITCH_SECRET` | *(unset)* | Pre-shared auth key; required off loopback | | `EDRI_SWITCH_SECRET_FILE` | *(unset)* | Path to a one-key-per-line file; wins over `EDRI_SWITCH_SECRET` | | `EDRI_SWITCH_MAX_MESSAGE` | `67108864` | Largest frame payload in bytes (64 MiB) | | `EDRI_SWITCH_STATUS_ENABLED` | `True` | Start the Switch status endpoint | | `EDRI_SWITCH_STATUS_HOST` | `localhost` | Switch status endpoint bind host | | `EDRI_SWITCH_STATUS_PORT` | `8898` | Switch status endpoint port | | `EDRI_CACHE_TIMEOUT` | `30` | Router cache timeout (seconds) | | `EDRI_CACHE_INFO_MESSAGE` | `60` | Cache stats log interval (seconds) | | `EDRI_ROUTER_OUTBOUND_QUEUE_SOFT_LIMIT` | `800` | Outbound backlog that flags a Manager WARNING | | `EDRI_ROUTER_OUTBOUND_QUEUE_HARD_LIMIT` | `5000` | Outbound backlog that disconnects + restarts a Manager | | `EDRI_ROUTER_OUTBOUND_JOIN_TIMEOUT` | `5.0` | Seconds to wait for an outbound worker to stop | | `EDRI_UPLOAD_FILES_PREFIX` | `edri_` | Uploaded file prefix | | `EDRI_UPLOAD_FILES_PATH` | `/tmp/upload` | Upload directory | | `EDRI_TEMPLATE_PATH` | `templates` | Jinja2 templates directory | | `EDRI_ASSETS_PATH` | `assets` | Static assets directory | | `EDRI_MAX_BODY_SIZE` | `4 MiB` | Max request body size | | `EDRI_MAX_MESSAGE_SIZE` | `4 MiB` | Max WebSocket message size | | `EDRI_ASGI_OPTIONS` | `None` | JSON passthrough to ASGI server config | | `EDRI_CHUNK_SIZE` | `256 KiB` | SharedMemoryPipe slot size | | `EDRI_MAX_FILE_SIZE` | `100 MiB` | Max file upload size | --- ## See also * {doc}`/deployment` — production deployment guide * {doc}`/switch` — Switch configuration * {doc}`/connector` — Connector configuration * {doc}`/router` — Router cache configuration * {doc}`/api` — API response configuration