Environment Variables¶
The variables the backend reads, grouped by what they switch. Values entered under Customizations in the web studio are saved to settings.json in the state directory and take effect without a restart; an environment variable set at start applies to the whole process. Every default below is the value the code uses when the variable is unset.
Provider keys¶
| Variable | Purpose |
|---|---|
OPENAI_API_KEY | OpenAI models (openai: prefix). |
ANTHROPIC_API_KEY | Anthropic models (anthropic: prefix). |
GEMINI_API_KEY | Google models (google: prefix). |
PERPLEXITY_API_KEY | Perplexity Sonar models and the OpenAI, Anthropic, and Google models routed through Perplexity (perplexity: prefix). |
AZURE_OPENAI_API_KEY | Azure OpenAI deployments (azure: prefix). |
NCBI_API_KEY | Raises the rate limit for the SRA and GEO searches. |
Model routing¶
| Variable | Default | Purpose |
|---|---|---|
CODARIS_PREFER_DIRECT_ANTHROPIC | unset | 1 runs a Claude model picked through Perplexity (perplexity:anthropic/…) on Anthropic's own API when ANTHROPIC_API_KEY is set — the route that caches tools, system prompt and history. Logged with both ids; never silent. |
CODARIS_PERPLEXITY_CONTINUATION | unset | 1 sends each model round on the Perplexity route (perplexity:anthropic/…) as a continuation of the previous response (previous_response_id + the new messages only), so the conversation prefix is read from Anthropic's cache instead of re-sent; measured 2026-10-18 at 3,039 of 3,744 input tokens read on round 2. Falls back to the whole conversation when the local history is not an exact extension of what the server holds, or the API refuses the id. |
CODARIS_DEFAULT_MODEL | balanced tier of a configured provider | Chat, planning, review, and research. |
CODARIS_BUILD_MODEL | flagship tier of a configured provider | Compilation and repair. |
CODARIS_DIGEST_MODEL | cheap tier of a configured provider | Digests, compliance scoring, and the judge. |
CODARIS_JUDGE_MODEL | cheap tier | The pipeline judge. |
CODARIS_DEBUG_MODEL | unset | The model a /run turn uses for failure analysis. |
CODARIS_FORCE_BUILD_MODEL, CODARIS_FORCE_DEBUG_MODEL | 0 | 1 turns the matching override into a hard pin. |
CODARIS_PERPLEXITY_PRESET | unset | A Perplexity preset name applied to the Sonar models. |
CODARIS_REASONING_EFFORT | unset | Overrides the reasoning-effort value sent to models that need it set for tool calls; an empty value sends nothing. |
CODARIS_LOCAL_LLM_BASE_URL | http://localhost:11434/v1 | OpenAI-compatible local server (local-api: prefix). |
CODARIS_LOCAL_LLM_API_KEY | not-needed | Key for that server when it requires one. |
CODARIS_LOCAL_MODEL_NAME | llama3.1 | The model name offered as local-api:<name> when discovery is off. |
CODARIS_LOCAL_CONTEXT_WINDOW | unset | Context window to assume for the local model. |
CODARIS_LOCAL_ANTHROPIC_BASE_URL, CODARIS_LOCAL_ANTHROPIC_API_KEY, CODARIS_LOCAL_ANTHROPIC_MODEL | unset | An Anthropic-compatible local endpoint (local-anthropic: prefix). |
A routing value whose provider has no key is skipped with a warning, and the Customizations page shows the model the role resolves to instead.
Decision model¶
| Variable | Default | Purpose |
|---|---|---|
CODARIS_NF_PILOT_MODEL | Primeomicx/nf-pilot | Hugging Face id or local directory. |
CODARIS_NF_PILOT_REVISION | pinned in the image | Revision to load. |
CODARIS_NF_PILOT_MODEL_PATH | unset | A local directory that takes precedence over the id. |
CODARIS_NF_PILOT_DEVICE | cpu | cpu, cuda, or mps. |
CODARIS_NF_PILOT_THRESHOLD | 0.8 | The confidence above which nf-pilot settles a qc:, sheet:layout, or struct: Setup question; below it the stage default stands. A fitting nf-core subworkflow default is kept at any confidence. |
HF_HOME | /app/models/cache in the image | Weight cache. |
HF_HUB_OFFLINE, TRANSFORMERS_OFFLINE | 1 in the image | Load from the cache only. |
State, paths, and access¶
| Variable | Default | Purpose |
|---|---|---|
CODARIS_HOME | ~/.codaris | The state directory: settings.json, the chat database, runs.db, the unpacked test-asset cache, MCP servers, session workspaces. |
CODARIS_ALLOWED_DIRS | CODARIS_HOME and the current directory | Colon-separated roots the agent may read and write. The Compose file sets ${HOME}:/media:/mnt:/Volumes:/tmp. |
CODARIS_RUNS_DB | $CODARIS_HOME/runs.db | The run ledger. |
CODARIS_WORKSPACE_ROOT | CODARIS_HOME | Root for session workspaces. |
CODARIS_SCRATCH_DIR | unset | Scratch directory for fixtures and temporary files. |
CODARIS_PUBLISH_DIR | unset | Default destination for codaris publish. |
CODARIS_DOTENV_PATH | unset | The .env file the Customizations page keeps in step with settings.json; otherwise backend/.env beside the package, created with mode 0600 on the first save of a writable checkout. Inside a container the image carries no .env, the sync is off, and settings.json on the mounted state directory is the persistent store. |
CODARIS_STORES_BASE_URL | the GitHub release URL | Mirror for codaris stores fetch. |
Toolchain¶
| Variable | Default | Purpose |
|---|---|---|
CODARIS_TOOLCHAIN_CONTAINER | 0 | 1 routes Nextflow, nf-core, and the other shell-outs through the toolchain image (docker/toolchain.Dockerfile, built as codaris/toolchain:latest); 0 uses the binaries on PATH. |
CODARIS_TOOLCHAIN_IMAGE | codaris/toolchain:latest | The toolchain image for container mode. |
CODARIS_TOOLCHAIN_NAME | codaris-toolchain | The container's name. |
CODARIS_TOOLCHAIN_BIN | unset | A directory searched for nextflow, nf-core and nf-test when container mode is off. The order is the backend's own bin/ (a virtualenv), then PATH, then this directory, then the usual install locations; the evidence, the lint and run tools and the shell all use it, so the binary the evidence names is the one that runs. |
CODARIS_TOOLCHAIN_MOUNT | ~ | The host directory mounted into it. |
CODARIS_TOOLCHAIN_DOCKER_SOCKET | /var/run/docker.sock | The socket it reaches the engine through. |
CODARIS_CONTAINER_LOOKUP | 1 | 0 turns off the registry lookup that verifies container tags (air-gapped hosts). |
CODARIS_CONTAINER_CACHE | on | 0 disables the verdict cache for container lookups. |
Seqera, Wave, and the nf-core pin¶
| Variable | Default | Purpose |
|---|---|---|
TOWER_ACCESS_TOKEN | unset | A Seqera Platform access token. The optional Seqera MCP entry sends it as Authorization: Bearer, and build_container_image sends it to wave.seqera.io (builds per hour rise from 25 a day anonymous to 250). The backend reads it once at start-up into memory and removes it from its own environment (the same goes for every variable a server's headers_env names, and WAVE_API_TOKEN), so a changed token needs a backend restart. It is never written to mcp_servers.json, never put in the built-in server's environment, and never shown in a log, a status line or a reply. It is removed from the environment of every shell-out (see below), so the agent's shell cannot read it. |
CODARIS_SHELL_ENV_PASSTHROUGH | empty | Comma-separated names of variables the shell-outs the agent drives may keep even though they look secret (GITHUB_TOKEN, an AWS variable a cloud run needs). TOWER_ACCESS_TOKEN and CODARIS_WAVE_APPROVAL_KEY cannot be listed. |
CODARIS_SHELL_ENV_DROP | empty | The inverse: comma-separated names (or PREFIX*) to withhold from those shell-outs as well, case-insensitive, and it wins over the pass-through list. SSH_AUTH_SOCK is kept by default because tools such as git over ssh need it; it lets a command use your ssh keys without reading them, so list it here if the agent should not. |
CODARIS_WAVE_BUILD_REPOSITORIES | empty | Comma-separated registries build_container_image may push to (docker.io/acme/images, or docker.io/acme/* for a namespace). Empty means no custom registry: builds go to Wave's community registry only. A push to a listed registry still needs a person's approval on a card. |
CODARIS_WAVE_DOCKERFILE_BUILDS | 0 | 1 lets build_container_image accept a Dockerfile. Off, only conda package lists and environment files are built. A Dockerfile also needs a listed registry and TOWER_ACCESS_TOKEN. |
CODARIS_NFCORE_MODULES_SHA | the manifest's upstream_modules_sha | The nf-core/modules commit that nf-core modules install --sha uses. Forty hex characters moves the pin; head lifts it, so installs take the head of the local modules clone. |
CODARIS_ACTION_GATE=0 also waives the approval card for a registry push, and nothing else about it: the registry list and the Dockerfile switch still apply.
What the agent's shell can see. A command the agent runs (run_command, the builder's nf-core and nextflow calls, a pipeline it launches on the local executor, a fix command) starts with the backend's environment after secrets are removed from the environment of every shell-out: names ending _API_KEY or _TOKEN, or containing SECRET, PASSWORD or CREDENTIAL, access and private keys, the AWS credential variables, CODARIS_WAVE_APPROVAL_KEY, TOWER_ACCESS_TOKEN, database, mail and cache passwords (*_PASS, PASS), AUTHORIZATION, KEY, NETRC, the *ASKPASS helpers, *CONNECTION_STRING* and *SAS_URL*, and any value that is a credential whatever the variable is called: one that carries user:password@ in a URL (a credentialed proxy), a connection string or SAS URL (AccountKey=, sig=), a password= pair, a bearer token, or a token with a well-known prefix (sk-, ghp_, xox). PATH, HOME, LANG, TMPDIR, DOCKER_HOST, NXF_*, JAVA_*, CONDA_* and the non-secret CODARIS_* settings are kept. A command that names TOWER_ACCESS_TOKEN, wave.seqera.io, mcp.seqera.io or a process's environment (/proc/<pid>/environ, ps eww) is refused, with a message pointing at build_container_image and the Seqera tools. The registry host under it, community.wave.seqera.io, where those builds' images live, is not one of them: docker pull, singularity pull, grep and nextflow run on such an image reference are not refused.
Credential files. run_command and the file tools also refuse to read or write files that hold credentials, wherever they are, with a message that names the rule: .env and .env.* (but not the template .env.example), *.pem, *.key, id_rsa*, id_ed25519*, .netrc, .nextflow/secrets/*, and everything in or under .ssh, .aws, .kube and .docker (so .aws/credentials, .docker/config.json and .kube/config among the rest). The shell's environment is scrubbed, but a file on the disk is not an environment variable, and CODARIS_ALLOWED_DIRS includes your home directory by default. A command is judged the way the state files below are, and conservatively where it cannot tell: a bare name such as entry.key in a script counts only when that file exists, but not when the command walks trees (find, grep -r, rg, fd, locate, rsync, tar, cp -r, ls -R), or has a cd it cannot resolve or a cd with a glob, and a name it writes by redirection (> .env) counts whether or not the file is there yet. A command that reads whole trees fails closed: every token is checked for a walker (find, rg, ag, ack, fd, locate, rsync, tar, zip, tree, du, grep/cp/ls/chmod/chown/scp with a recursive option in any cluster or long form, python -m tarfile|zipfile|shutil), wherever it sits in a wrapper (env, nice, timeout, sudo, stdbuf, ionice, chrt, busybox, xargs), and the command is refused when any of its path words, or the directory it runs in, is at or above $HOME or $CODARIS_HOME, is inside or above $NXF_HOME/secrets, the directory of $AWS_SHARED_CREDENTIALS_FILE or of a file in $KUBECONFIG (when set), is in .nextflow (it holds secrets/), or holds a variable the gate cannot expand, or when xargs hands it paths it cannot see, or it takes its paths from a file (--files-from, --files0-from, tar -T); the value of a directory option is judged as a path in every spelling (tar -C dir, -Cdir, --directory=dir, cp -t dir, -rtdir, --target-directory=dir), and python -m tarfile|zipfile|shutil is found after any interpreter options (-u, -W ignore, -mtarfile). There is no walk of the disk to look for credentials, so a recursive read of a folder below your home that holds a .env is not caught by name, and this is best effort. a path with a variable it cannot expand is judged with the variable left out (~/.s${X}sh/id_rsa); a hard link to a credential file is recognised by its inode; and code that builds its paths at run time (an interpreter, $(...), a backtick) is refused when it holds a fragment of a credential name (.env, id_rsa, .ssh, .aws, .kube, .docker, .netrc, secrets, credentials, .pem, .key). If a pipeline really needs one of these, give the agent a non-secret copy or a path to the value outside these rules. All of this reads what a command says, and the sandbox is the real control. An OS-level sandbox (the toolchain container without the credentials mounted, or a separate UID that cannot read them) is the only complete enforcement; a program that computes a path from data the gate cannot see gets past a gate that reads text.
The state directory is an allowed root, so the agent could once write mcp_servers.json there and, on the next reload, have the registry send a token to a URL it chose. mcp_servers.json, settings.json (the provider keys), notifications.json (who is emailed), permissions*, grants* and the chat and ledger database codaris.db with its -wal, -shm and -journal files (the capability grants are a table in it) directly under CODARIS_HOME are therefore protected files, and so is a notification file named by CODARIS_NOTIFY_SETTINGS. run_command and the file tools refuse any command or path that names one, however it is spelled (.., an absolute path, ~, $CODARIS_HOME, quotes and escapes inside the path, $'...', braces, a glob, a symlink or a hard link), with no card. The command is first normalised into plain words: $'...' and $"..." are decoded, quotes and escapes removed, heredoc bodies and every quoted command line inside it (bash -c "...") read again, each cd and pushd tracked in order. A command that is not a plain look at the disk (ls, cat, grep, find without -exec, every word resolvable) is then refused when it can write and cannot be shown to leave the state files alone: it names the state directory itself as a destination or runs in it (cp x $CODARIS_HOME/, mv, rsync, install, ln, tar -C); it names the state directory together with a state file's name; it names the state directory and builds part of a path with $(...), a backtick, $'...' or an escape such as \x41; it changes directory more than once into the state directory or somewhere it cannot work out; it creates a link (ln, cp -s) while naming a path in the state directory; or it cannot be read as shell words. Where it cannot tell what a variable or a cd means it refuses rather than guesses: a word with a variable it cannot expand and a state file's name ($X/settings.json), a cd into a directory it cannot name followed by a state file's name, quoting nested more than three levels deep, and a command with more than 20,000 distinct words (write a large file with the file tool). $PWD and $OLDPWD are the directory the command is in at that point. sessions/, logs/ and the chat's own workspace are not affected. Only the operator endpoints (the add and remove calls, with their one-time code from the log) and the CLI write these files. This gate reads what a command says, so it is defence in depth, not a sandbox: a program that builds a path at run time, or a script written in one step and run in the next, is not caught. The robust protection is to keep the agent's commands out of the state directory's reach: set CODARIS_TOOLCHAIN_CONTAINER=1 so its shell-outs run in the toolchain container, which does not mount it, or run the backend under a different UID from the commands and keep the state files private to it (CODARIS_ALLOWED_DIRS does not cover a command that names a path outright). A server entry may name only the variables in CODARIS_MCP_HEADER_VARS, and the secret store no longer falls back to the environment for a name it did not capture at start-up.
The built-in MCP server is started with none of its secrets in its environment, and no tool call can give it any: the backend makes a pipe for that one child, starts it with the read end inherited and --provision-fd <n> on its command line (a number, not a secret), writes one line holding the Perplexity key, the Platform token and the approval key, and closes it. The child reads the line once, before it serves a request, closes the pipe and keeps the values in memory, so ps eww on it shows nothing and there is no tool a model could call to replace them. A built-in server that Codaris started and that did not receive them refuses Wave builds. A built-in server started by something else (another harness, a script) uses its own environment, which its owner chose.
The Seqera token is not left in the backend's own process environment either. At start-up the backend moves TOWER_ACCESS_TOKEN, WAVE_API_TOKEN and every variable a server's headers_env names into memory, removes them from os.environ so no child inherits them, and overwrites the strings exec left in the process's environment block with zeros, which is what ps -E (macOS) and /proc/<pid>/environ (Linux) read. Deleting the variable alone does not do that: the string stays in the block, and ps -E printed it after a del os.environ[...] in the check made for this change.
Which process holds the container's environment. A process's startup environment stays readable in /proc/<pid>/environ for as long as it runs, and a process that spawns the backend keeps its own copy. In the all-in-one image, docker/entrypoint.sh used to stay PID 1 as bash and start uvicorn as its child, so /proc/1/environ held the token for the life of the container, and unset in bash does not change what that file prints. Now the entrypoint starts the frontend, with an environment without the secrets (env -u for the Seqera and Wave tokens, PERPLEXITY_API_KEY, the approval key, every CODARIS_WAVE_* variable, and the variables listed in CODARIS_MCP_HEADER_VARS both as written and in upper case, never PATH, HOME and the like), and a watcher that ends the container if the frontend dies, and then execs the backend. The backend is PID 1: nothing is left whose environment holds the secrets, and the backend overwrites its own copy as described above. The registry's start-up takes the same names out of the backend (the Seqera and Wave tokens, PERPLEXITY_API_KEY, CODARIS_WAVE_APPROVAL_KEY, the Seqera header variables and everything in CODARIS_MCP_HEADER_VARS, whether or not a server uses it); the other CODARIS_WAVE_* variables are settings, not credentials, so the backend keeps them and only the services started before it lose them. Two consequences. The backend, as PID 1, does not reap orphaned grandchildren (a background process the agent leaves behind stays a zombie until the container restarts). And do not run the image with docker run --init or Compose init: true: the init process becomes PID 1, is started with the container's whole environment, and nothing overwrites it. The separate backend image runs uvicorn directly as PID 1 and has the same property. Outside Docker, nothing starts the backend from a shell script, but a shell that does (TOWER_ACCESS_TOKEN=... uvicorn ...) leaves that shell's own copy for as long as it lives.
--reload is for development without real tokens. uvicorn --reload (used by docker/docker-compose.dev.yml) runs a reloader process that starts the server as its child. The reloader holds the environment it was started with, including TOWER_ACCESS_TOKEN, for as long as it runs, and the backend's overwrite cannot reach it, so the agent's shell can read the token from the reloader's /proc/<pid>/environ (or ps -E). Use --reload only with throwaway or no tokens.
PERPLEXITY_API_KEY is held in memory too, and for the same reason: the built-in server receives it on the pipe, so the backend's own copy only made it readable. At start-up it is moved into the store and out of the environment, and the research client, the Perplexity router, the model resolver and the Customizations page read it from there (a key saved on that page goes straight into the store). What remains in the backend's environment is everything else you started it with, including the other provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY and the rest). Moving those into the same store is a follow-up and is not part of this change. On Linux the backend and the built-in child also call prctl(PR_SET_DUMPABLE, 0) at start-up, so another process of the same user cannot read their /proc/<pid>/environ or descriptors, and the gate above refuses a command that names /proc/<pid>/environ or ps eww. Those are defence in depth, not a sandbox: they do nothing on macOS and do not stop root. The robust fix is to keep the agent's commands out of the backend's reach: set CODARIS_TOOLCHAIN_CONTAINER=1, so its shell-outs run in the toolchain container, which never had the backend's environment, or run the backend under a different UID from the commands.¶
Build and agent behaviour¶
| Variable | Default | Purpose |
|---|---|---|
CODARIS_MCP_SERVERS | unset | A JSON list of extra stdio MCP servers, each with an absolute command (MCP servers). Servers registered from the studio live in $CODARIS_HOME/mcp_servers.json instead. |
CODARIS_MCP_HEADER_VARS | TOWER_ACCESS_TOKEN,SEQERA_ACCESS_TOKEN | Comma-separated names of the backend environment variables a server's headers_env may send as a header. A provider key, PATH, HOME and CODARIS_* are refused even if listed. Set it only to add the token of another MCP server you trust; naming it here is your decision, not a configuration file's. |
CODARIS_MCP_UI_ADD | unset (off) | 1, true, yes, or on lets the Tools panel add and remove MCP servers; otherwise POST /api/mcp/servers answers 403. Each add also needs a one-time code from the backend log. Servers already in mcp_servers.json load either way. |
CODARIS_LEAN_7_TOOLS | 1 | Bind the lean tool surface (see the tool surface); 0 binds the whole catalog. |
CODARIS_TOOL_SEARCH_API | auto | On the direct Anthropic route, send the tools a turn is not bound as defer_loading tools and answer search_tools with tool_reference blocks, so a discovery leaves the prompt cache alone. auto turns it on when the installed Anthropic SDK supports it, 0 never, 1 always. A 400 about deferral switches it off for the process and the round is re-sent the client-side way. |
CODARIS_LEAN_PROMPT | 1 | The compact system prompt. |
CODARIS_TOOL_RESULT_RESEND_CAP | 4096 | Characters of a tool result re-sent on a later round of a turn, on a route that does not cache the conversation; the round that produced it sees it whole. |
CODARIS_HISTORY_KEEP_ROUNDS | 12 | Rounds of the current turn re-sent verbatim on a chat turn; older rounds fold into the history digest. |
CODARIS_HISTORY_KEEP_ROUNDS_BUILD | 6 | The same window for a build or autopilot turn (the intent carries /build, /extend, /run or autopilot): the build, run and fix loop rarely needs rounds older than a few. |
CODARIS_HISTORY_REWRITE | auto | Whether the history a round re-sends may be rewritten (old results stubbed or capped, old rounds folded). auto decides by route: direct Anthropic, OpenAI, Azure OpenAI, Google, Perplexity's openai/*, google/* and xai/* ids, and any provider named in CODARIS_HISTORY_CACHED_PROVIDERS cache an unchanged prefix, so the history goes out as it is; Perplexity's anthropic/* ids (whose API refuses a cache marker inside a tool result) and the rest keep the trimmed history. off never rewrites it; on always does. A fold on a cached route is made only when it removes at least 8 messages, because it starts a new cache entry. |
CODARIS_HISTORY_CACHED_PROVIDERS | unset | Comma-separated provider names (for example local-api) that advertise prompt caching, so auto treats their history as cached. |
CODARIS_FIX_BUDGET | 2 | Fix attempts per gate in a build. |
CODARIS_LLM_REPAIR | unset | 1 lets codaris test escalate to model-proposed repairs. |
CODARIS_LLM_REPAIR_BUDGET | 3 | Attempts for those repairs. |
CODARIS_RULE_PROFILE | core_v1 | Rule profile for review and remediation (full for every rule). |
CODARIS_JUDGE | 1 | 0 answers the judge from the keyword fallback (no model call). |
CODARIS_SELF_REVIEW | unset | 1 adds a self-review pass at the end of a build. |
CODARIS_PLAN_ASSETS | 1 | 0 skips the references stage of the plan questions. |
CODARIS_PLAN_DATASETS | 1 | 0 skips the test-data lookup on NCBI. |
CODARIS_PERMISSION_GATE | on | The gate that asks before a command runs outside the allowed roots. |
CODARIS_ACTION_GATE | 1 | The dispatch-time policy for tool calls. |
CODARIS_CAPABILITY_LEDGER | 1 | Record grants in the task ledger. |
CODARIS_DAG_NEXTFLOW | unset | Whether the Pipeline DAG tab draws a pipeline from Nextflow's own preview (nextflow run <dir> -preview -with-dag). A preview runs no task but evaluates the pipeline's nextflow.config and scripts, which are code, so unset it runs only for a folder this backend built, meaning one listed in $CODARIS_HOME/built_pipelines.json (real path to a random token) whose .codaris_build carries that same token, and graphs any other folder from its files without executing it; a .codaris_build copied or created by hand, and a build made before this registry existed, are not trusted until the backend scaffolds the folder again. 1, true, yes, or on previews any folder inside CODARIS_ALLOWED_DIRS; 0, false, no, or off never previews. A preview runs in a scratch directory with HOME and the JVM's user.home set to it, only allow-listed variables passed (none matching AWS_*, *_API_KEY, *_TOKEN, or *SECRET*, and NXF_OPTS stripped of -D credentials), NXF_HOME (and NXF_SCM_FILE) pointed at the scratch directory so your real Nextflow home, with its scm credentials and config, never reaches a preview, and NXF_PLUGINS_DIR a scratch directory seeded with hard links of your ~/.nextflow/plugins cache (nothing is written to the real cache). GET /api/pipelines/dag answers 403 for a folder outside CODARIS_ALLOWED_DIRS. |
CODARIS_MAX_TOOL_ROUNDS | unset | An optional ceiling on model rounds per turn. Unset or 0 is no ceiling: a turn runs until the work is done, and the loop guard ends one that is going nowhere (the same call with exactly the same answer three times since anything changed, the same answer six rounds running, a run status unchanged for twenty minutes) with a notice saying what it stopped and a Continue control; a run polled every few seconds is told to wait, and six polls with the same answer inside two minutes are a loop too. A positive number ends every turn after that many rounds with the same control; a 20 saved before this version was the old default and is dropped on the next start. The first six calls of a round run. Also set under Customizations, effective from the next turn. |
CODARIS_MAX_CALLS_PER_ROUND | 6 | Calls that run from one round; the rest come back Not run: for the next round. The per-round half of the tool budget, in place of a round ceiling. Also set under Customizations. |
CODARIS_TURN_TOKEN_BUDGET | unset | Cumulative input tokens one turn may use, cached or not; unset or 0 is none. When a turn crosses it the next round's calls are refused, the model is told to report, and the chat shows a notice saying how many input tokens and model calls the turn has used, with a Continue control that starts the count again. |
CODARIS_STALL_ROUNDS | 4 | Rounds in a row that wrote no file, ran no command and came back with the same error before the turn pauses with the same notice ("no progress in 4 rounds") and Continue control; 0 turns the rule off. |
CODARIS_UNIT_GATE | 1 | The unit gate: every local module Codaris writes gets a stub: block, an nf-test (tests/main.nf.test) and an entry in .codaris/generated.json, and its stub test runs the moment it is written (the verdict goes to .codaris/unit_checks.json and the [UNIT] line of the write result). 0 still writes the stub block and the test but does not run them. Every nf-test run, stub or real, goes through the normal run grant (nf-test executes the module and the configs it loads); without a grant, or without nf-test on the toolchain, the check is reported as skipped, never as passed. |
CODARIS_MAX_REFUSED_ROUNDS_AFTER_STOP | 1 | Rounds a model may spend ignoring a stop (the loop guard, the round ceiling, the invalid-call limit, the time ceiling) before the turn ends without another model call. The stop tells the model to stop and report; one more round is its chance; a further round of nothing but refused calls ends the turn with a closing line, the notice, and the Continue control. 0 ends the turn at the stop round. |
CODARIS_TURN_MAX_SECONDS | 1800 | A wall-clock ceiling per turn, in seconds (30 minutes); 0 is none. Soft: a running tool is not interrupted, and the next round's calls are refused with the time, the model is told to report, and the Continue control appears. The notice says how long the turn ran. |
CODARIS_COMMAND_TIMEOUT_SECONDS | 300 | How long one run_command may run before it is ended and answered "Command timed out after N s" (a failure, so the loop guard counts it). Launchers (nextflow run, downloads) keep their own one-hour limit; a command that only reads (grep, cat, ls) never gets it. |
LANGGRAPH_RECURSION_LIMIT | 600 | The chat graph's step backstop behind the guards above, two steps a round; raised automatically to clear a round ceiling. A turn that reaches it ends with an error, not a Continue control. |
CODARIS_REFERENCE_GATE | on | off skips the reference check before a real nextflow run (missing files, reads simulated from another reference, references from two organisms). |
CODARIS_TURN_SURVIVES_DISCONNECT | 1 | A turn keeps running when its connection closes, and a reload rejoins it; 0 ends the turn with the connection. |
CODARIS_STREAM_HEARTBEAT_SECONDS | 15 | Seconds of quiet after which a chat stream writes an SSE comment, so a proxy between the browser and the backend keeps the connection open. The studio's own proxy closes a request after 30 s without a byte, and the page treats 45 s as a lost connection. |
CODARIS_ALLOW_MISSING_SKILLS | unset | 1 starts the backend with a warning when a harness skill is missing or empty; otherwise startup stops. |
CODARIS_AUTO_CONFIRM | unset | 1 accepts the intent brief without a confirmation question when the elicitation has nothing to ask (headless runs). |
Server and logging¶
| Variable | Default | Purpose |
|---|---|---|
CODARIS_API_URL | http://127.0.0.1:8000 | Where the REPL and the CLI reach the API. |
CODARIS_BACKEND_URL | http://127.0.0.1:8000 | Where the Next.js server proxies /api and /health (frontend). |
NEXT_PUBLIC_API_URL | empty (same origin) | Build-time: an absolute API origin for a studio served apart from its API. |
CODARIS_CORS_ORIGINS | unset | Extra browser origins the API accepts, comma-separated. |
CODARIS_PUBLIC_URL | http://localhost:3000 | The studio's public URL, used in notifications and share links. |
CODARIS_VERSION | dev | The release version shown in the banner and /health; set by the image build. |
CODARIS_LOG_LEVEL | INFO | Backend log level. |
CODARIS_LOG_JSON | unset | 1 emits JSON log lines. |
CODARIS_VERBOSE | 0 | Verbose CLI output. |
CODARIS_SMTP_HOST, CODARIS_SMTP_PORT, CODARIS_SMTP_USER, CODARIS_SMTP_PASSWORD, CODARIS_SMTP_FROM, CODARIS_SMTP_SECURITY | unset | Mail for run notifications. |
CODARIS_NOTIFY_SETTINGS | $CODARIS_HOME/notifications.json | Notification preferences file. |
Variables not listed here are internal switches for subgraphs and experiments; the authoritative list is grep -rho 'CODARIS_[A-Z0-9_]*' backend/app | sort -u.