Files
odysseus/website/configuration-reference.md
T

27 KiB

layout
layout
default

Configuration reference: ODYSSEUS_* environment variables

Odysseus reads its runtime configuration from the Settings UI. The environment variables below are the deployment-level escape hatches underneath that: they are read directly from the process environment, mostly at import or startup, and most installs never need any of them.

.env.example stays a short, deployment-level example on purpose - APP_BIND, APP_PORT, AUTH_ENABLED, DATABASE_URL and a pre-seeded admin password. This page is the complete list, which is a different job.

Truthiness is not uniform across the codebase. Where a variable is described as "truthy" the read accepts 1, true, yes and usually on; where it is described as a switch that turns something off, the read rejects 0, false, no and off and treats everything else as on. The Default column is the value the code falls back to when the variable is unset, quoted from the source.

The source tree reads 117 ODYSSEUS_* variables: 81 an operator may want to set, and 36 that are internal - sentinels, fixture switches, capture hooks and development tooling. The internal ones are listed too, in their own section, so this page can be checked against the source mechanically.

This page is generated. Edit scripts/generate_env_reference.py and re-run it; tests/test_env_reference.py enforces that the committed page matches the source.

Variables you can set

Deployment and first run

Variable Default Read in What it does
ODYSSEUS_ADMIN_PASSWORD '' setup.py:103 (+4 more) Password for the admin account created on first run. Setup refuses a value shorter than its minimum length rather than silently falling back.
ODYSSEUS_ADMIN_USER '' setup.py:102 (+2 more) Username for the admin account created on first run. Setup uses env vars first, then an interactive prompt, then a random password.
ODYSSEUS_ALLOW_OLLAMA_CLI_SCAN unset routes/cookbook_helpers.py:544 (+1 more) On Windows only, set truthy to let the Cookbook dependency probe shell out to ollama list. Ignored on other platforms, where the scan always runs.
ODYSSEUS_CONTAINER_NETWORK_MODE '' app.py:1022 (+1 more) Declares the container's Docker network mode. Set to host to skip host-gateway probing when discovering local model endpoints.
ODYSSEUS_ENABLE_HOST_DOCKER '' src/host_docker_access.py:41 Security-relevant. Must be exactly true before tools may use a mounted host Docker socket, and the socket itself must exist.
ODYSSEUS_INPROCESS_POLLERS '1' routes/email/email_pollers.py:1722 The same off switch for the in-process email pollers, when odysseus-mail poll-scheduled is the sole external driver.
ODYSSEUS_INPROCESS_TASKS '1' app.py:1311 Set to 0, false, no or off to stop the in-process scheduled-task runner, for deployments where an external worker drives task firing.
ODYSSEUS_MODEL_KEEPALIVE '' app.py:1218 Opt-in periodic model keep-alive pings. Off by default: the ping path runs model discovery, so stale LAN endpoints add background pressure.
ODYSSEUS_REQUIRE_TOOL_INDEX_READY '' src/readiness.py:68 Set truthy to make semantic tool-index readiness gate startup. Off by default so an install stays available on deterministic tool selection.
ODYSSEUS_SKIP_ADMIN_PROMPT unset setup.py:113 Any non-empty value suppresses the interactive admin-credential prompt even on a TTY, for unattended installs.
ODYSSEUS_SLOW_REQUEST_LOG_SECONDS '0.75' app.py:239 Request duration in seconds above which the middleware logs a slow-request warning.
ODYSSEUS_STARTUP_WARMUPS '' app.py:1192 Opt-in startup pings of the configured model endpoints. Off by default because they compete with the first seconds of UI use.
ODYSSEUS_TOOL_INDEX_PREWARM '1' src/tool_index.py:772 Set to 0, false, no or off to skip background initialization of semantic tool retrieval at startup.

Data directories and paths

Variable Default Read in What it does
ODYSSEUS_DATA_DIR get_default_data_dir() src/constants.py:56 (+1 more) Root directory for every persisted file. Prefer this over the per-path overrides; the rest of src/constants.py derives from it.
ODYSSEUS_MAIL_ATTACHMENTS_DIR os.path.join(DATA_DIR, 'mail-attachments') src/constants.py:110 Dedicated override for the mail attachment store, which otherwise lives under the data directory.

Model routing and providers

Variable Default Read in What it does
ODYSSEUS_COPILOT_API_VERSION '2026-06-01' src/copilot.py:39 Dated API-version header the Copilot models and chat endpoints require.
ODYSSEUS_COPILOT_CLIENT_ID '01ab8ac9400c4e429b23' src/copilot.py:34 GitHub OAuth client id for the Copilot device flow. The default is the public VS Code client id; override it only with your own allow-listed app.
ODYSSEUS_DEEPSEEK_REASONING_EFFORT 'high' src/llm_core.py:1727 Reasoning effort for DeepSeek. Only high and max are accepted; any other value falls back to the default.
ODYSSEUS_FIRST_TOKEN_TIMEOUT '' src/llm_core.py:223 Seconds to wait for the first streamed token from a local endpoint before failing. Unset keeps the generous read timeout, which makes a stalled backend look like a hung agent.
ODYSSEUS_LOCAL_MODEL_GATE 'true' src/llm_core.py:95 On by default. Set 0, false, no or off to drop the gate that checks a local endpoint before routing a request to it.
ODYSSEUS_MISTRAL_REASONING_EFFORT 'high' src/llm_core.py:1723 Reasoning effort sent to Mistral thinking-capable models. The API accepts high, medium, low and none.
ODYSSEUS_MLX_IMAGE_VLM_MODEL unset scripts/mlx_image_server.py:299 Vision-language model id for the MLX image server script. Required unless --vlm-model is passed on the command line.
ODYSSEUS_QWEN_ROUTE_THINKING 'auto' src/agent_loop.py:182 Thinking policy for the Qwen routing step. An unrecognized value falls back to auto.

Agent loop and tool execution

Variable Default Read in What it does
ODYSSEUS_DISABLE_MCP '' src/builtin_mcp.py:89 Truthy disables MCP entirely, as an escape hatch for compatibility problems with a server.
ODYSSEUS_MAX_VISUAL_EVIDENCE_FRAMES '3' src/agent_loop.py:15986 How many video frames one tool result may contribute. Clamped to 1-8.
ODYSSEUS_MAX_VISUAL_EVIDENCE_IMAGES '1' src/agent_loop.py:15954 How many images one tool result may contribute to the model turn. Clamped to 1-8.
ODYSSEUS_MCP_ALLOWED_COMMANDS '' src/agent_tools/admin_tools.py:140 Security-relevant. Comma-separated allowlist of MCP launcher basenames the agent may start. Empty by default, and the deny list still wins.
ODYSSEUS_PYTHON_TOOL_SITE_PACKAGES '' src/agent_runtime/process_resources.py:59 (+2 more) Security-relevant. Absolute package roots, separated by the platform path separator, exposed to the sandboxed Python tool. Empty exposes none.
ODYSSEUS_SCRIPT_HOST 'localhost' src/builtin_actions.py:925 Default host for the run-script action. localhost, 127.0.0.1, local and empty run locally; any other value runs over SSH.
ODYSSEUS_TOOL_APPROVAL_GATE '0' src/tool_capabilities.py:645 Security-relevant. Truthy makes tool calls pass through the approval gate. Off by default.

Browser automation

Variable Default Read in What it does
ODYSSEUS_BROWSER_EXECUTABLE '' src/builtin_mcp.py:114 Absolute path to the Chrome or Chromium binary. Empty searches the usual names, then lets Playwright MCP pick its own browser.
ODYSSEUS_BROWSER_ISOLATED '1' src/builtin_mcp.py:139 Security-relevant. On by default, adding --isolated so each browser session starts clean. Set 0, false or no to keep a persistent profile.
ODYSSEUS_BROWSER_MCP_CACHE os.path.join(base_dir, 'data', 'local', 'playwright-mcp-cache') src/builtin_mcp.py:229 Cache directory handed to the browser MCP server, so its npm download survives a container rebuild.
ODYSSEUS_BROWSER_MCP_CALL_TIMEOUT_S '90' src/mcp_manager.py:27 Upper bound in seconds for one browser MCP tool call. A call that exceeds it fails without being retried.
ODYSSEUS_BROWSER_MCP_REQUIRE_CACHE '' src/builtin_mcp.py:90 Truthy refuses to start the browser MCP server unless its npm package is already in the npx cache, instead of installing it at startup.
ODYSSEUS_BROWSER_NAMESPACE 'odysseus-ui' src/agent_tools/web_tools.py:100 (+1 more) Namespace for the detached agent-browser daemon's pid files, so two runtimes on one machine do not terminate each other's browsers.
ODYSSEUS_BROWSER_NO_SANDBOX '1' src/builtin_mcp.py:142 Security-relevant. On by default, adding --no-sandbox because the Docker image cannot use the Chromium sandbox. Set 0, false or no to keep it.
ODYSSEUS_BROWSER_SCREENSHOT_DIR unset src/agent_tools/web_tools.py:2666 Where private-browser screenshots are written. Falls back to the container path, then the system temp directory.

Container and workspace mounts

Variable Default Read in What it does
ODYSSEUS_WORKSPACE_CONTAINER_ROOT unset src/workspace_paths.py:31 Container-side root that the host root maps onto. An or fallback, not a read default, supplies /workspace when it is unset.
ODYSSEUS_WORKSPACE_DEFAULT '' routes/workspace_routes.py:97 Default workspace path the admin-only workspace route reports. Empty means no default is configured.
ODYSSEUS_WORKSPACE_HOST_ROOT unset src/workspace_paths.py:29 Single host-side root, paired with the container root below. Simpler than the explicit mount list when there is only one mount.
ODYSSEUS_WORKSPACE_MOUNTS '' src/workspace_paths.py:19 host=container path pairs separated by commas or semicolons, so the agent can translate a container path back to the host path a user typed.

Email

Variable Default Read in What it does
ODYSSEUS_DOCUMENT_OWNER '' mcp_servers/email_server.py:208 Owner stamped on documents the email MCP server creates. Stdio MCP tools get no authenticated user, so without this a draft is invisible.
ODYSSEUS_IMAP_TIMEOUT_SECONDS unset routes/email/email_helpers.py:1294 IMAP socket timeout in seconds, clamped to 5-300. A non-numeric value falls back to 30 rather than failing.

Calendar, notes and single-user mode

Variable Default Read in What it does
ODYSSEUS_ALLOW_PRIVATE_CALDAV '0' src/caldav_sync.py:52 Security-relevant. Truthy lets CalDAV sync reach private and link-local addresses. Off by default; this is an SSRF guard.
ODYSSEUS_FALLBACK_OWNER 'owner@localhost' routes/calendar_routes.py:65 Owner address that single-user mode attributes an unauthenticated request to. Only reachable while single-user mode is on.
ODYSSEUS_SINGLE_USER '1' routes/calendar_routes.py:66 Security-relevant. On by default. Set to 0 on a real multi-user install so unauthenticated calendar writes are rejected rather than absorbed.

Upload and media limits

Variable Default Read in What it does
ODYSSEUS_CHAT_UPLOAD_MAX_BYTES 10 * 1024 * 1024 src/upload_limits.py:33 Maximum bytes accepted for a chat attachment.
ODYSSEUS_EDITOR_DRAFT_MAX_BYTES 256 * 1024 * 1024 src/upload_limits.py:47 Maximum bytes accepted for a saved editor draft.
ODYSSEUS_EMAIL_COMPOSE_UPLOAD_MAX_BYTES 25 * 1024 * 1024 src/upload_limits.py:56 Maximum bytes accepted for an attachment added while composing mail.
ODYSSEUS_GALLERY_TRANSFORM_UPLOAD_MAX_BYTES 25 * 1024 * 1024 src/upload_limits.py:44 (+1 more) Maximum bytes accepted for an image handed to a gallery transform.
ODYSSEUS_GALLERY_UPLOAD_MAX_BYTES 100 * 1024 * 1024 src/upload_limits.py:41 (+1 more) Maximum bytes accepted for a gallery upload.
ODYSSEUS_ICS_MAX_BYTES 10 * 1024 * 1024 src/upload_limits.py:62 Maximum bytes accepted for an imported ICS file.
ODYSSEUS_MEDIA_FRAME_TIMEOUT 30 src/media_ingress.py:146 Seconds allowed for extracting frames from a video before giving up.
ODYSSEUS_MEDIA_MAX_AUDIO_BYTES 32 * 1024 * 1024 src/media_ingress.py:129 Largest source audio file the media pipeline will read.
ODYSSEUS_MEDIA_MAX_DIMENSION 1600 src/media_ingress.py:138 Longest edge in pixels an image is resized down to before encoding.
ODYSSEUS_MEDIA_MAX_DOCUMENT_BYTES 32 * 1024 * 1024 src/media_ingress.py:126 Largest source document the media pipeline will read.
ODYSSEUS_MEDIA_MAX_DOCUMENT_CHARS 24000 src/media_ingress.py:135 How many characters of an ingested document are inlined into the turn.
ODYSSEUS_MEDIA_MAX_ENCODED_BYTES 24 * 1024 * 1024 src/media_ingress.py:132 Budget for the encoded payload handed to the model, counted cumulatively across one turn's attachments rather than per file.
ODYSSEUS_MEDIA_MAX_FILES 4 src/media_ingress.py:119 How many local media files one agent turn may ingest.
ODYSSEUS_MEDIA_MAX_IMAGE_BYTES 12 * 1024 * 1024 src/media_ingress.py:120 Largest source image the media pipeline will read.
ODYSSEUS_MEDIA_MAX_PIXELS 40000000 src/media_ingress.py:139 Total pixel budget for a source image, as a decompression-bomb guard.
ODYSSEUS_MEDIA_MAX_VIDEO_BYTES 128 * 1024 * 1024 src/media_ingress.py:123 Largest source video the media pipeline will read.
ODYSSEUS_MEDIA_MAX_VIDEO_FRAMES 8 src/media_ingress.py:140 How many frames are sampled from a video.
ODYSSEUS_MEDIA_PROBE_TIMEOUT 15 src/media_ingress.py:143 Seconds allowed for probing a video's metadata before giving up.
ODYSSEUS_MEMORY_IMPORT_MAX_BYTES 10 * 1024 * 1024 src/upload_limits.py:50 (+1 more) Maximum bytes accepted for a memory import file.
ODYSSEUS_PERSONAL_UPLOAD_MAX_BYTES 25 * 1024 * 1024 src/upload_limits.py:53 (+1 more) Maximum bytes accepted for a personal-documents upload.
ODYSSEUS_STT_MAX_AUDIO_BYTES 25 * 1024 * 1024 src/upload_limits.py:59 Maximum bytes accepted for an audio file submitted for transcription.
Variable Default Read in What it does
ODYSSEUS_SEARCH_PROVIDER '' services/search/providers.py:43 Forces the search provider, overriding the Settings value. Empty keeps the UI authoritative, which is what a normal install wants.

Memory and skills

Variable Default Read in What it does
ODYSSEUS_MCP_MEMORY_OWNER unset src/mcp_manager.py:190 Application owner binding for the configured memory MCP backend. Takes precedence over ODYSSEUS_MEMORY_OWNER; missing ownership fails closed.
ODYSSEUS_MEMORY_OWNER unset src/mcp_manager.py:190 Fallback application owner binding for the memory MCP backend. This configuration identifies ownership; it does not grant read or egress authority.
ODYSSEUS_SKILL_SEMANTIC_RETRIEVAL '1' services/memory/skills.py:796 On by default. Set 0, false, no or off to fall back to keyword-only skill retrieval when no vector store is reachable.
ODYSSEUS_SKILL_SEMANTIC_THRESHOLD '0.4' services/memory/skills.py:807 Minimum semantic score a skill needs to be retrieved. A non-numeric value falls back to the default.

Speech and vision models

Variable Default Read in What it does
ODYSSEUS_GROUNDING_MODEL 'google/owlvit-base-patch32' routes/gallery/gallery_routes.py:96 Object-grounding model id the gallery loads for text-driven selection.
ODYSSEUS_SAM_MODEL 'facebook/sam-vit-base' routes/gallery/gallery_routes.py:60 Segmentation model id the gallery loads for subject selection.
ODYSSEUS_STT_MODEL unset src/agent_tools/media_tools.py:2189 Default speech-to-text model for media transcription when the tool call does not name one.
ODYSSEUS_TTS_CACHE_MAX_BYTES 500 * 1024 * 1024 services/tts/tts_service.py:47 Cap on the synthesized-speech cache. A non-numeric value falls back to the default.

Auth and internal API

Variable Default Read in What it does
ODYSSEUS_INTERNAL_BASE unset src/constants.py:197 Base URL the in-app tool layer uses for loopback HTTP calls. Set it when the app is not reachable at the port it thinks it is bound to.
ODYSSEUS_INTERNAL_TOKEN unset core/middleware.py:20 Security-relevant. Token that lets the in-app tool layer reach admin-gated routes over loopback. Unset generates a fresh per-process token, which is what you want unless something outside the process needs the same value.

Integrations (Claude, Codex)

Variable Default Read in What it does
ODYSSEUS_API_TOKEN '' integrations/claude/skills/odysseus/scripts/odysseus_api.py:40 (+1 more) API token those scripts authenticate with. Both this and the URL are required; the scripts name whichever is missing.
ODYSSEUS_URL '' integrations/claude/skills/odysseus/scripts/odysseus_api.py:39 (+1 more) Base URL of the Odysseus instance the bundled integration scripts call.

Internal and development-only variables

Listed for completeness. Setting one of these on a real install is either a no-op or a way to break something quietly.

Model routing and providers

Variable Default Read in What it does
ODYSSEUS_COPILOT_EDITOR_VERSION 'Odysseus/1.0' src/copilot.py:54 Editor-version header presented to the Copilot API. Kept stable on purpose.
ODYSSEUS_COPILOT_INTEGRATION_ID 'vscode-chat' src/copilot.py:51 Integration id presented to the Copilot API. Kept stable on purpose.
ODYSSEUS_COPILOT_USER_AGENT 'Odysseus/1.0' src/copilot.py:48 Editor-like User-Agent presented to the Copilot API. Kept stable on purpose.
ODYSSEUS_DEBUG_LLM_SHAPE '' src/llm_core.py:3552 (+1 more) Truthy logs the shape of streamed provider chunks. Debugging aid for provider response parsing.

Agent loop and tool execution

Variable Default Read in What it does
ODYSSEUS_CAPTURE_MODEL_REQUESTS '' src/agent_loop.py:4131 Truthy writes model-request snapshots for local debugging. The marker file /tmp/odysseus_capture_model_requests enables the same thing.
ODYSSEUS_EXPOSE_RAW_BROWSER_MCP '' src/agent_loop.py:4336 Truthy stops hiding the raw Playwright MCP tools from agent prompts when the private-browser tool is available.
ODYSSEUS_TOOL_CONTRACT_ROOT str(Path(__file__).resolve().parents[1] / 'scripts') src/clean_agent_preview.py:2324 (+1 more) Directory holding the tool-contract scripts the clean-agent preview loads. The default resolves to the bundled scripts directory relative to the installed/source tree.

Email

Variable Default Read in What it does
ODYSSEUS_EMAIL_FIXTURE unset mcp_servers/email_server.py:1122 (+7 more) Exactly 1, plus a fixture file on disk, makes the email MCP server serve fixtures instead of a real mailbox.

Testing, capture and development tooling

Variable Default Read in What it does
ODYSSEUS_AJAX_TEST_URL unset tests/test_ajax_email_live.py:17 (+4 more) Chat-completions URL of a live Ajax endpoint. Unset skips the opt-in live Ajax email tests.
ODYSSEUS_BROWSER_LIVE_CONTRACT unset tests/test_browser_producer_live_contract.py:19 Set 1 only in the allowlisted release Docker environment to run the browser producer contract tests. Does not enable browser page operations.
ODYSSEUS_DOCKER_TEST_IMAGE 'odysseus-odysseus:latest' tests/test_docker_devops_hardening.py:180 Docker image tag exercised by the DevOps Docker entrypoint integration tests.
ODYSSEUS_EDITOR_ACTIONS ','.join([*actions, 'edit', 'update']) tests/tools/editor_writing_smoke.py:71 Comma-separated writing actions the editor-writing smoke tool runs. Unset runs every action plus edit and update.
ODYSSEUS_EDITOR_MAX_TOKENS '4096' tests/tools/editor_writing_smoke.py:110 Completion token limit for each editor-writing smoke request.
ODYSSEUS_EDITOR_RICH_FIXTURE unset tests/tools/editor_writing_smoke.py:80 Set to 1 to run the editor-writing smoke tool against a rich-text document fixture instead of Markdown.
ODYSSEUS_EDITOR_TEST_ENDPOINT unset tests/tools/editor_writing_smoke.py:110 (+1 more) Chat-completions URL the opt-in editor-writing and organizer smoke tools drive. Both tools require it.
ODYSSEUS_EDITOR_TRACE unset tests/tools/editor_writing_smoke.py:140 Any non-empty value prints every stream event after each editor-writing smoke action.
ODYSSEUS_LLAMA_SERVER 'llama-server' scripts/odysseus_related_flow_audit.py:181 Binary name or path to llama-server used by the related-flow audit script.
ODYSSEUS_ORGANIZER_AUTO_CHOICE unset tests/tools/organizer_smoke.py:199 With organizer tracing on, any non-empty value replaces forced tool choice with auto on traced requests.
ODYSSEUS_ORGANIZER_CASES unset tests/tools/organizer_smoke.py:223 (+1 more) Comma-separated organizer smoke case names to run. Unset runs every case.
ODYSSEUS_ORGANIZER_TRACE unset tests/tools/organizer_smoke.py:193 Any non-empty value prints each provider request the organizer smoke tool sends.
ODYSSEUS_ORGANIZER_TRACE_MESSAGES unset tests/tools/organizer_smoke.py:203 With organizer tracing on, any non-empty value also prints the request messages.
ODYSSEUS_QA_PASSWORD unset scripts/odysseus_related_flow_audit.py:581 (+10 more) Account password passed to the related-flow audit script when authenticating.
ODYSSEUS_QA_TEACHER_ATTEMPTS '3' scripts/odysseus_conversation_qa.py:368 Retry budget for the conversation-QA teacher model call. Clamped to 1-3.
ODYSSEUS_QA_TEACHER_TIMEOUT '120' scripts/odysseus_conversation_qa.py:370 Timeout in seconds for that call. Clamped to 15-120.
ODYSSEUS_RUNTIME_REVISION '' routes/chat_helpers.py:200 (+1 more) Revision string stamped into each captured SFT trace record, so a trace can be tied back to the build that produced it.
ODYSSEUS_SFT_DIR unset scripts/run_odysseus_search_teacher_pipeline.py:19 (+1 more) Base directory containing SFT training datasets for the search teacher pipeline.
ODYSSEUS_SFT_DISABLE_WORKSPACE_TOOLS '1' src/agent_loop.py:7844 On by default. Keeps synthetic personal-assistant fixtures out of workspace mode; set 0, false, no or off to let them through.
ODYSSEUS_SFT_FORCE_UTC_TIMEZONE '0' routes/chat_routes.py:2145 Truthy forces sft_ accounts to UTC for deterministic batch generation. Interactive accounts still follow the browser timezone.
ODYSSEUS_SFT_TRACE_CAPTURE '1' routes/chat_helpers.py:163 (+1 more) On by default, but only for owners whose name starts with sft_. Set 0, false, no or off to stop writing training traces.
ODYSSEUS_SFT_TRACE_DIR unset routes/chat_helpers.py:197 (+2 more) Directory the SFT trace JSONL files are written to. Defaults to sft_traces under the data directory.
ODYSSEUS_SKIP_RUN_HINT unset setup.py:285 Any non-empty value suppresses the start the server with hint at the end of setup. start-macos.sh sets it because it starts the server itself.
ODYSSEUS_TEST_STATIC_ORIGIN unset scripts/css_snapshot.py:254 (+6 more) Origin an already-running static server is serving the repository from, so snapshot tooling reuses it instead of starting its own.
ODYSSEUS_TEST_STATIC_PORT unset tests/conftest.py:201 Fixed port for the test suite's static server. Unset takes an ephemeral port, which is what keeps parallel runs from colliding.
ODYSSEUS_TINY_MODEL_PATH unset scripts/odysseus_related_flow_audit.py:181 Path to a local compact model GGUF file used in Cookbook serve lifecycle audit flows.

Build and release metadata

Variable Default Read in What it does
ODYSSEUS_BUILD_VERSION '' src/constants.py:15 Overrides the build-version string the API and UI report, without touching the public application version.
ODYSSEUS_SOURCE_COMMIT '' src/constants.py:33 Overrides the source commit reported for runtime provenance, for builds that ship without a git directory.

How this page is generated

The generator walks the Python sources under app.py, launcher.py, setup.py, companion, config, core, integrations, mcp_servers, routes, scripts, services, src, tests and finds reads three ways, because no single pattern covers the codebase:

  • Direct reads: os.getenv(...), and any .get / .setdefault / .pop call or subscript keyed by an ODYSSEUS_* literal, including names held in a module-level constant. The receiver is not required to be os.environ, because several call sites read through a mapping passed in as an argument (src/tool_index.py, src/host_docker_access.py).
  • Calls to env-reader helpers - any function that forwards one of its own parameters to an environment read. This is detected rather than hardcoded, so a new helper needs no change here. It is what finds the upload caps in src/upload_limits.py and the media-ingress overrides in src/media_ingress.py.
  • A regex sweep of the raw file text, for reads the AST cannot see. routes/cookbook_helpers.py builds an Ollama probe script as a list of source lines, so one read lives inside a string literal.

The three passes are not redundancy. A line-based grep for a direct os.environ.get("ODYSSEUS_... call finds 87 of the 117 variables on this page. What it misses is reads through an env-reader helper, reads whose call spans more than one line, reads whose variable name is held in a module constant, and reads through a mapping passed in as an argument - which is the whole reason this page is generated rather than maintained.

The Default column shows the expression as written, with one level of indirection resolved: a module-level constant and a dataclass field default are replaced by the literal they hold, so defaults.max_media_files shows as 4. Anything computed at import time - get_default_data_dir() - is shown as written, because that is the honest answer. A few call sites supply their fallback with or rather than a default argument; those show as unset and say so in the last column.

Regenerate it with:

python3 scripts/generate_env_reference.py