Environment variables
Configuration is environment variables, never code. There is no config file to load, no Ecko.configure(...) call, and no way for a library to change your provider behind your back. A program's behaviour is a function of its source and its environment.
A project can give defaults in the environment block of ecko.json. The shell wins: an entry applies only to a variable you have not set, so what you export is what runs - the same env-over-file rule as std.config. (Before 0.58 the block overrode the shell.)
Three rules hold for every variable on this page:
- An invalid value is an error, not a default.
ECKO_LIMIT_DEPTH=lotsstopseckobefore the program starts, naming the variable and what it should be. - For a timeout,
0means off. For a safety limit - depth, allocation, unpacked size, connection caps -0is refused: off would mean unbounded. - Names are
ECKO_<AREA>_<SETTING>. The names used before 0.58 are not read since 0.61; one that is still set says which name replaced it - see No longer read.
The global flags --provider, --model, --trace and --cache, written before the file, are one-run overrides of the matching variable. The API key is ECKO_AI_API_KEY only; it has no flag.
AI and LLM
| variable | meaning |
|---|---|
ECKO_AI_API_KEY | Provider API key. Unset means mock mode. |
ECKO_AI_PROVIDER | openai (default), openrouter, ollama |
ECKO_AI_MODEL | Model name; each provider has a default |
ECKO_AI_BASE_URL | Override the provider endpoint, for a proxy or a compatible server. Must be https, or http to a loopback host, since the API key travels on it; ECKO_NET_ALLOW_HTTP=1 overrides |
ECKO_AI_EMBED_MODEL | Embedding model; defaults per provider |
ECKO_AI_MAX_RETRIES | Retries for typed and contract calls. Default 3 |
ECKO_AI_MAX_CALLS | Hard cap on paid provider calls - ai and embeddings alike, so rag and db count too. Per run for a script; per request (or WebSocket message) in a server. Unset means unlimited |
ECKO_AI_REASONING | How hard a reasoning model thinks, for every call without its own reasoning:: none, minimal, low, medium, high, xhigh or max. Unset leaves it to the provider. See model() |
ECKO_AI_READ_TIMEOUT_MS | How long an AI request may go without receiving anything before it fails (and is retried). A limit on silence, not on the whole request: a long reply that keeps arriving is never cut off. Default 120000; 0 turns it off |
ECKO_AI_OLLAMA_FORMAT | 0 stops sending a typed call's schema to Ollama as format, for an Ollama older than 0.5. Default: sent |
ECKO_AI_MOCK | 1 forces mock mode whatever the keys, provider or a call's via say. ecko test sets it |
OPENROUTER_API_KEY, OPENAI_API_KEY | The key for a call that switches to that provider with via. ECKO_AI_API_KEY is never sent to a provider other than ECKO_AI_PROVIDER |
ECKO_AI_MAX_TOOL_ROUNDS | Tool-call rounds per ai ... using. Default 8 |
ECKO_AI_TOOL_TIMEOUT_MS | Per-round tool timeout. Default 30000; 0 disables |
ECKO_AI_TOOL_MAX_RESULT | Max bytes of one tool result fed back to the model. Default 32768; 0 disables |
ECKO_AI_CACHE | Directory for the prompt cache |
ECKO_AI_TRACE | 1 traces to stderr; a path writes JSONL |
ECKO_RETRY_BASE_MS | Base backoff for retry. Default 50 |
ECKO_AI_MAX_CALLS is the one to set in anything unattended. It is a hard budget: the call that would exceed it fails rather than spending. In a server it limits each request rather than the server's lifetime - see budgeting.
Concurrency
| variable | meaning |
|---|---|
ECKO_LIMIT_PARALLEL | Concurrent workers in pmap and tool rounds. Default: all cores |
ECKO_LIMIT_TASKS | Cap on running async tasks. Default 256 |
ECKO_LIMIT_QUEUED_TASKS | Max tasks waiting for a slot. Default 4096; past it spawning is refused rather than queued |
HTTP and WebSocket server
Each of these is also an option to http.serve, named after the variable: http.serve(port, app, max_body: 1048576, max_conns: 256). The option is the program's own setting; the variable, when set, overrides it, so an operator can still change a deployed server without editing it.
| variable | http.serve option | meaning |
|---|---|---|
ECKO_HTTP_WORKERS | workers: | How many http.serve handler invocations run at once. Default 8. Worth tuning: leave it alone for handlers that compute, raise it for handlers that wait on a database, a service or ai - see sizing the pool |
ECKO_HTTP_MAX_STREAMS | max_streams: | Concurrent streaming/SSE responses. Default 1024; past it a stream is refused with 503 |
ECKO_HTTP_REQUEST_TIMEOUT_MS | request_timeout_ms: | Per-request handler timeout: past it the client gets 503, whether the handler is computing or waiting. 0 or unset means unlimited |
ECKO_HTTP_MAX_BODY | max_body: | Max request body in bytes; a larger one is answered 413 rather than truncated. Default 10 MiB |
ECKO_HTTP_DRAIN_MS | drain_ms: | How long a stopping http.serve (SIGTERM, Ctrl-C or http.stop()) waits for in-flight requests before closing what is left. Default 25000, inside the 30 s a container orchestrator allows before SIGKILL |
ECKO_HTTP_READ_TIMEOUT_MS | read_timeout_ms: | The slow-client deadline: how long a connection may take to send its request headers, and thereafter how long it may go silent mid-body before the server answers 408. Applied per body frame, so a slow but steady upload of any size completes. Default 30000; 0 turns the defence off |
ECKO_HTTP_MAX_CONNS | max_conns: | Max concurrent HTTP connections. Default 1024. At the limit the connection idle longest (at least 1 s) is closed to make room for a new one; the new one is dropped before anything is read only if none is idle. Each connection is a file descriptor, so raising this past about 1,000 also needs a higher open-files limit (ulimit -n) |
ECKO_HTTP_MAX_WS_CONNS | max_ws_conns: | Max concurrent WebSocket connections. Default 1024 |
Network and database
| variable | meaning |
|---|---|
ECKO_NET_TIMEOUT_MS | Read and write deadline on a std.net socket, so a silent peer surfaces an error rather than blocking forever. 0 disables it. Default 30000 |
ECKO_SQL_BUSY_TIMEOUT_MS | How long a std.sql connection waits for another connection's lock before failing with database is locked. A busy_timeout_ms passed to sql.open overrides it. 0 means do not wait. Default 5000 |
Runtime limits
| variable | meaning | ||
|---|---|---|---|
ECKO_LIMIT_DEPTH | Call and recursion depth. Default 2000 | ||
ECKO_LIMIT_PARSE_DEPTH | How deep an expression tree may nest, by brackets or by chaining (a + b + c, `x | > f | > g, else if arms). Default 128` |
ECKO_LIMIT_STEPS | Opt-in loop and step budget. Unset means unlimited | ||
ECKO_LIMIT_ALLOC | Bytes one value built from a count may hold, and the most one read from a stream may return: a string from *, repeat or the pad family, bytes from random.bytes or io.read_exact, a whole-file fs.read, a buffered HTTP body. Default 256 MiB | ||
ECKO_LIMIT_UNPACKED | Total bytes an std.archive extraction or a std.zlib decompression may produce. Default 1 GiB |
These exist so that adversarial or accidental input degrades into a catchable error instead of exhausting the machine - see Resource limits.
Engine
| variable | meaning |
|---|---|
ECKO_VM_MODULES | 0 runs the code of imported modules on the slower tree-walking evaluator, as through 0.73, instead of the bytecode VM. A temporary escape hatch for a program that behaves differently since 0.74; it will be removed. Default on |
ECKO_JIT_ENABLE | 1 compiles functions to machine code before they run (a module's when it is imported). Experimental. Every function that contains a loop compiles, and so does a function without one that calls itself or is called from compiled code, when it can be called directly (no default parameter values, no try, and only operations the compiler covers, so no calls to module functions such as math.sqrt); inside them, whole-number and float arithmetic, comparisons, local variables, field reads, for loops, calls between compiled functions (including a call through a variable or a value to a compiled function that captures no variables), calls to built-in functions and the checks of typed parameters and results run as machine code, and any other operation is handed to the bytecode VM and the function carries on after it. Other functions run on the VM as usual. ecko check --native shows what compiles, and why a function does not. Results, errors and exit codes are identical to the default engine; a difference is a bug, so please report it. Unset or 0 runs everything on the VM. Default off |
Python
| variable | meaning |
|---|---|
ECKO_PY_TIMEOUT_MS | How long one py() call may take before it fails with kind timeout and the worker is restarted. Default 30000; 0 turns it off |
ECKO_PY_ENV | Comma-separated variable names to forward to the Python worker, which otherwise inherits nothing |
Logging and terminal
| variable | meaning |
|---|---|
ECKO_LOG | Level filter for the default std.log sink: debug < info < warn < error. Default info |
ECKO_BACKTRACE | Show Rust's own panic output when the interpreter hits an internal fault. Off by default: the fault is reported as a message and exit 70 either way, and this adds the raw detail for diagnosing it. |
NO_COLOR | Any value disables terminal colour (the convention) |
CLICOLOR_FORCE | Force colour on even when stdout is not a TTY |
Packages
| variable | meaning |
|---|---|
ECKO_PKG_MAX_BYTES | Max download size for a package. Default 50 MiB |
ECKO_PKG_MAX_UNPACKED | Max unpacked size. Default 200 MiB |
ECKO_NET_ALLOW_HTTP | 1 or true permits plain-http package sources from non-loopback hosts |
ECKO_PKG_GIT_BASE | Override the forge base URL, for a private mirror |
ECKO_PKG_TOKEN | Bearer token attached to package fetches, for a private repository. Sent only to the hosts below |
ECKO_PKG_TOKEN_HOSTS | Comma-separated hosts that may receive the token. Defaults to ECKO_PKG_GIT_BASE's host, or github.com |
The two size caps and the https default are there because fetching a package is the moment a program trusts something it did not write. ECKO_NET_ALLOW_HTTP exists for a local registry; needing it against a real host is a warning sign.
Scaffolding
| variable | meaning |
|---|---|
ECKO_TEMPLATES_REPO | Where ecko scaffold gets templates: an owner/repo, an archive URL, or a local directory read with no network and no cache. Default ecko-lang/templates |
ECKO_TEMPLATES_DIR | Where they are cached. Default: the platform cache directory |
No longer read
These were renamed in 0.58, and the old name was read, with a warning, through 0.60. From 0.61 it is not read at all: one that is still set prints which name replaced it, and the run goes ahead as if it were unset - so a limit set under an old name is not in force. Change your shell, CI and ecko.json.
| old | new |
|---|---|
ECKO_API_KEY | ECKO_AI_API_KEY |
ECKO_TRACE | ECKO_AI_TRACE |
ECKO_MAX_DEPTH | ECKO_LIMIT_DEPTH |
ECKO_MAX_STEPS | ECKO_LIMIT_STEPS |
ECKO_MAX_ALLOC | ECKO_LIMIT_ALLOC |
ECKO_MAX_PARSE_DEPTH | ECKO_LIMIT_PARSE_DEPTH |
ECKO_MAX_TASKS | ECKO_LIMIT_TASKS |
ECKO_MAX_QUEUED_TASKS | ECKO_LIMIT_QUEUED_TASKS |
ECKO_MAX_PARALLEL | ECKO_LIMIT_PARALLEL |
ECKO_ARCHIVE_MAX_UNPACKED | ECKO_LIMIT_UNPACKED |
ECKO_MAX_CONNS | ECKO_HTTP_MAX_CONNS |
ECKO_MAX_STREAMS | ECKO_HTTP_MAX_STREAMS |
ECKO_MAX_WS_CONNS | ECKO_HTTP_MAX_WS_CONNS |
ECKO_ALLOW_HTTP | ECKO_NET_ALLOW_HTTP |
ECKO_NET_TOKEN | ECKO_PKG_TOKEN |
ECKO_NET_TOKEN_HOSTS | ECKO_PKG_TOKEN_HOSTS |
ECKO_GIT_BASE | ECKO_PKG_GIT_BASE |
Removed in 0.58: ECKO_LLM_API_KEY (a second spelling of the key - set ECKO_AI_API_KEY) and ECKO_FUZZ_TRACE (internal). If either is still set, ecko says so rather than ignoring it silently.
Secrets
Nothing here should hold a secret except ECKO_AI_API_KEY and ECKO_PKG_TOKEN, and neither belongs in a committed file. Put them in your shell, your CI secret store, or a secret manager. Inside a program, wrap sensitive values in secret so they cannot be printed or logged by accident, and reach for std.config when you want layered configuration with declared secret fields.