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. Past it a connection is dropped before anything is read |
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.
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.