Ecko

A small language for work that is not.

Ecko is a programming language and runtime for work where a failure costs money: transaction processing, catalog pipelines, backfills that run for days, and APIs where uptime is revenue. It ships as one static binary.

It comes from production. A backfill of 1.3 billion transaction records kept failing in Python. Rewritten in Ecko, it ran end to end without a single failure.

import std.json

rows = [r"""{"id": 1, "cents": 1250}""", r"""{"id": 2, "cents": """, r"""{"id": 3, "cents": 990}"""]

mut total = 0
mut rejected = []
for line in rows {
    try {
        row = json.decode(line)
        total = total + row.cents  # checked: an overflow is an error, never a wrap
    } catch (e) {
        # A malformed row is set aside; anything else stops the job, loudly.
        match get(e, "kind") {
            "parse" => {
                rejected = push(rejected, line)
            }
            _ => error(e)
        }
    }
}
print("{total} cents, {len(rejected)} rejected")  # 2240 cents, 1 rejected

What happens when something goes wrong

  • A failure is a value, not an exit. Operational failures throw a { kind, message, ... } map from a closed, documented set of kinds, so a job recovers from what it expects and stops on everything else. See error handling.
  • A mistake fails where it was made. xs[9] and m.missing are errors at that line, and integer overflow is an error, never a wrap. null comes only from the lookups that document it, such as get. See data access and numbers.
  • Input has ceilings. Allocation, recursion, parse depth, decompression and I/O all have limits, and hitting one is a catchable error. See resource limits.
  • One failure stays one failure. An HTTP handler that throws or faults becomes a 500 and the worker keeps serving; tasks share nothing unless you give them a cell. See std.http and async tasks.
  • Most mistakes never run. ecko check refuses undefined names, wrong arity and use-before-definition before the first line executes. See ecko check.

Ecko is not crash-free yet. Several hundred places in the runtime can still abort on bad input; each is counted, the count only goes down, and when one fires it is reported as a bug in Ecko (exit code 70), not as an error in your program. Some guarantees are also still warnings rather than rules: error kinds are checked strings rather than a type.

It just works - one step, zero config, offline. Installing drops in a single binary with nothing to configure. Running is just ecko file.ecko - no project to set up, no package.json, no Cargo.toml. A file is a program. The standard library is already there - import std.http gives you an HTTP server, import std.sql gives you SQLite - with no apt install, no pip install, no cargo add.

AI, as a consequence

Code a model wrote fails the way yours does: a mistake is refused by ecko check or thrown at the line that made it, like anyone else's. Ecko can also call models itself. ai[T] returns output checked against your types, with tool calling, tracing and hard call budgets, and with no API key every call runs in a deterministic offline mock mode, so code that uses a model is tested like any other. See the ai keyword.


Quick start

Install Ecko - one command, a single binary, nothing to configure:

curl -fsSL https://ecko.sh/install | sh

The installer picks the right build for your machine, checks its checksum, and installs ecko to /usr/local/bin, or ~/.local/bin when that is not writable (see Install). Prebuilt binaries ship for:

platform
Linuxx86-64, arm64
macOSApple Silicon
Windowsx86-64

Intel Macs are not supported - an Apple Silicon binary cannot run on one.

Prefer a manual install? Download the archive for your platform and put the binary on your PATH:

https://ecko.sh/dl/latest/ecko-x86_64-linux.tar.gz
https://ecko.sh/dl/latest/ecko-aarch64-linux.tar.gz
https://ecko.sh/dl/latest/ecko-aarch64-macos.tar.gz
https://ecko.sh/dl/latest/ecko-x86_64-windows.zip

Each has a .sha256 beside it, so you can verify before unpacking:

curl -fsSLO https://ecko.sh/dl/latest/ecko-x86_64-linux.tar.gz
curl -fsSLO https://ecko.sh/dl/latest/ecko-x86_64-linux.tar.gz.sha256
shasum -a 256 -c ecko-x86_64-linux.tar.gz.sha256

To pin a version, swap latest for the tag - https://ecko.sh/dl/v0.64.0/...; https://ecko.sh/dl/latest.txt says which tag latest currently is.

Run a file, or start the REPL:

ecko hello.ecko            # run a file
ecko                       # start the REPL (or `ecko repl`)

Your first program is one line:

print("Hello, World!")  # Hello, World!

If your program calls a model, that needs no setup either - it runs in mock mode until you add a key:

# Typed output; runs offline in mock mode (a real key returns a real answer)
count = ai[Int] "Count the words in this sentence"
print(count)  # 42

Set ECKO_AI_API_KEY for real responses, and ECKO_AI_PROVIDER to switch between openai (default), openrouter, and ollama. Configuration is always through environment variables, never code - see environment variables for the full list.

Start here

  • Installing? The one-liner is at the top of this page; the full set of archives and checksums is on the download page.
  • Running a job that has to finish? Start with error handling and resource limits.
  • Calling a model? Every ai call runs offline in mock mode, so a program is testable before you configure a provider.
  • Looking for a package? The official packages below are written in Ecko, capability-gated, and installed with ecko get.

Reference

Every page below is written. The documentation covers the language, the ai keyword, concurrency, the standard library, packages, the CLI and the runtime's architecture.

Getting started

  • Install - the curl | sh one-liner, the platform archives, and verifying a checksum
  • A file is a program - project scaffolding is never required
  • Your first program - hello world to a typed ai call
  • The REPL - interactive sessions, multi-line input
  • Mock mode - how Ecko runs fully offline with no API key
  • Editor support - the VS Code grammar and the language server
  • Playground - try Ecko in the browser, no install, the real kernel compiled to WebAssembly

Language

The ai keyword

  • ai and typed output - ai "...", ai[Int] / ai[Bool] / ai[Enum] / ai[Struct] / ai[json<...>], schema coercion
  • Mock mode - deterministic schema-valid values with no key; how tests stay offline
  • Contracts - @requires / @ensures, boolean and natural-language, self-correcting retries
  • Majority voting - ai[T] "..." votes n runs n samples and takes the mode
  • Tool calling - @tool("...") functions, ai[T] "..." using [f, g], the runtime tool loop
  • Sessions - session() and ai "..." with chat for multi-turn conversations
  • Multimodal / vision - ai "..." on img, images as values, provider serialization
  • Embeddings & RAG - embed, embed_all, cosine, and retrieval over them
  • Streaming - ai "..." -> stream, live SSE tokens, consuming a stream
  • Token budgeting - tokens(text), cost(in, out, in_per_1m, out_per_1m), hard call caps
  • Retry - retry(n, f) with exponential backoff for any failing operation
  • Providers & configuration - OpenAI / OpenRouter / Ollama, swap with one env var
  • Caching - content-addressed prompt cache, replayable, budget-free
  • Tracing - ECKO_AI_TRACE JSONL: every call's provider, model, tokens, latency, retries

Concurrency and async

Standard library

  • std.str - the full UTF-8 string toolkit (upper, split, replace, pad_*, trim, ...)
  • std.math - constants (pi, e, tau) and float functions (sin, sqrt, log, clamp, ...)
  • std.io - streams and the console: one verb set for files, sockets, pipes, HTTP bodies and stdin (read, read_line, lines, write, timeout, close)
  • std.fmt - format (raw-string {} placeholders), fixed, inspect (secret-safe)
  • std.uuid - v4 (random) and v7 (time-ordered) UUIDs
  • std.random - seedable RNG plus a CSPRNG (bytes, token)
  • std.test - case, eq, ok, err, fail for the test runner
  • std.time - now, now_iso, monotonic, monotonic_ns, format, parse
  • std.re - regex: test, find, find_all, captures, split, replace
  • std.hash - sha256, hmac_sha256, sha1, md5, encrypt / decrypt (ChaCha20-Poly1305), and Argon2id password / verify
  • std.encoding - base64, hex, and URL encode/decode (*_decode_text variants)
  • std.term - TTY info, size, raw mode, key input, color_enabled (honors NO_COLOR); styling is in the tui package
  • std.zlib - gzip, gunzip, deflate, inflate over bytes
  • std.fs - files and directories (read, write, list_dir, copy, rename, ...) - fs:read / fs:write
  • std.archive - zip and tar containers (zip_create, zip_extract, zip_list, tar_*) - fs:read / fs:write
  • std.proc - child processes with a timeout that reaps the whole process tree, and stream pipes on a long-lived one (run, spawn, wait, kill, stdin, stdout, stderr) - exec
  • std.signal - OS signal handlers for graceful shutdown (on, next, close, raise) - exec
  • std.watch - filesystem events (open, next, close) - fs:read
  • std.os - host environment and process: env, env_or, set_env, args, cwd, platform, exec, exit (env / exec gated)
  • std.json - encode, decode, read, write
  • std.csv - parse, stringify, read, write (rows as column-keyed maps)
  • std.toml - parse, stringify, read, write
  • std.yaml - parse, stringify, read, write
  • std.log - leveled logging with text/JSON sinks and file rotation (fs:write for file sinks)
  • std.image - decode / resize / crop / encode PNG & JPEG; backs ai ... on <image>
  • std.config - layered config (env > file > default), with secret fields - env + fs:read
  • std.defaults - the project's ecko.json, loaded automatically: defaults.<key> plus an environment block applied to the process - ungated
  • std.http - HTTP client and server, streaming/SSE, TLS, WebSocket upgrade
  • std.web - a router over http.serve: routes, :params, middleware, static files (GET routes also answer HEAD)
  • std.ws - WebSocket client (connect, send, recv, close)
  • std.net - raw TCP/TLS sockets and DNS (connect, connect_tls, starttls, lookup), read and written with std.io
  • std.dns - DNS resolver (resolve, reverse, lookup; A/AAAA/CNAME/MX/TXT)
  • std.llm - low-level chat access to the provider layer
  • std.sql - embedded SQLite: open, exec, query, query_one, transaction; sql { ... } blocks (:memory: is pure, a file db needs fs:write)
  • Global builtins reference - the ~95 functions always in scope: collections (map, filter, reduce, sort, group_by, ...), conversions (int, string, bytes, ...), json_encode / json_decode, embed / tokens / cost / retry, secret / reveal, error / assert, cell / channel, and py(...) (Python FFI)

Packages

  • Concepts - bare imports, vendor/, and the three-layer model (kernel / std / packages)
  • The ecko.json manifest - module, version, entrypoint, capabilities, dependencies
  • Capabilities & gating - net, fs:read, fs:write, env, exec, device; granted by the importer, attenuated down the tree. The two fs names and device take a path scope (fs:write:./logs, device:/dev/ttyUSB0) that confines a package to one directory or one port
  • Lockfile & integrity - ecko.lock sha256 pins; commit vendor/ for offline clones
  • Package commands - init, get, install, remove, update, pack
  • Building an executable - ecko build bundles the interpreter + program into one file
  • Docker - a ~30 MB distroless image, no shell or package manager inside

CLI and tooling

  • ecko run / a file - run a program (a bare ecko file.ecko works too)
  • ecko repl - the interactive REPL
  • ecko dev - hot-reload for servers and scripts
  • ecko fmt - the canonical formatter (one style, zero config; --check)
  • ecko check - static analysis: undefined names, arity, use-before-def, exhaustiveness, unwrapped credentials
  • ecko test - the test runner (mock mode forced); --generate scaffolds tests
  • ecko explain / lint / fix - explain a program, lint (deprecated: check --strict), AI-assisted fixes, fix --migrate
  • ecko doc - markdown documentation from ## comments
  • ecko scaffold - project templates, fetched and cached; never required
  • ecko lsp - the stdio language server for editors
  • ecko profile - where a program's time went, per function
  • Package commands - init / get / install / remove / update / pack / build
  • Global flags - --model, --provider, --cache, --trace

Configuration

  • Environment variables - the complete reference. Grouped:
    • AI / LLM - ECKO_AI_API_KEY, ECKO_AI_PROVIDER, ECKO_AI_MODEL, ECKO_AI_BASE_URL, ECKO_AI_EMBED_MODEL, ECKO_AI_MAX_RETRIES, ECKO_AI_MAX_CALLS, ECKO_AI_MAX_TOOL_ROUNDS, ECKO_AI_TOOL_TIMEOUT_MS, ECKO_AI_REASONING, ECKO_AI_READ_TIMEOUT_MS, ECKO_AI_OLLAMA_FORMAT, ECKO_AI_MOCK, ECKO_AI_TOOL_MAX_RESULT, ECKO_AI_CACHE, ECKO_AI_TRACE, ECKO_RETRY_BASE_MS
    • Concurrency - ECKO_LIMIT_PARALLEL, ECKO_LIMIT_TASKS, ECKO_LIMIT_QUEUED_TASKS
    • HTTP / WebSocket server - ECKO_HTTP_WORKERS, ECKO_HTTP_REQUEST_TIMEOUT_MS, ECKO_HTTP_DRAIN_MS, ECKO_HTTP_MAX_BODY, ECKO_HTTP_READ_TIMEOUT_MS, ECKO_HTTP_MAX_CONNS, ECKO_HTTP_MAX_STREAMS, ECKO_HTTP_MAX_WS_CONNS
    • Network and database - ECKO_NET_TIMEOUT_MS, ECKO_SQL_BUSY_TIMEOUT_MS
    • Runtime limits - ECKO_LIMIT_DEPTH, ECKO_LIMIT_PARSE_DEPTH, ECKO_LIMIT_STEPS, ECKO_LIMIT_ALLOC, ECKO_LIMIT_UNPACKED
    • Python - ECKO_PY_TIMEOUT_MS, ECKO_PY_ENV
    • Logging and terminal - ECKO_LOG, ECKO_BACKTRACE, NO_COLOR, CLICOLOR_FORCE
    • Packages - ECKO_PKG_MAX_BYTES, ECKO_PKG_MAX_UNPACKED, ECKO_NET_ALLOW_HTTP, ECKO_PKG_GIT_BASE, ECKO_PKG_TOKEN, ECKO_PKG_TOKEN_HOSTS
    • Scaffolding - ECKO_TEMPLATES_REPO, ECKO_TEMPLATES_DIR

Architecture

Official packages

Batteries that live outside the binary: each one is written in Ecko, versioned on its own, and deletable. ecko get vendors a package under vendor/ and pins a hash; what it is allowed to do is the grant you give it at the import, not what its manifest asks for.

Every reference below is generated from the ## comments in the package's own source, so it says what the code says.

  • bisect - Binary search over a sorted list: insertion points, membership, and order-preserving insert. Pure - no capabilities.
  • cache - A general-purpose cache written in Ecko: in-memory LRU with TTL over an optional disk store. get/set/remember(key, ttl, fn).
  • checkpoint - Resumable long-running jobs over std.sql: batch a source through a handler with the cursor committed in the same transaction as the writes, and memoise named pipeline steps. A crashed job resumes where it stopped and applies nothing twice.
  • cli - Declarative command-line parsing: options, short flags, positionals, subcommands, --help and usage text from one spec map. The std.cli module, as a package. Pure - no capabilities.
  • clipboard - Read and write the OS clipboard by driving the platform's own tool: pbcopy on macOS, clip on Windows, wl-copy or xclip on Linux.
  • cookies - Parse and serialize HTTP cookies for client sessions: read Set-Cookie headers into a jar, build the Cookie request header. Pure - no capabilities.
  • cron - Cron expressions for Ecko: parse the five classic fields, ask when a schedule next fires, and test whether a moment matches. Pure - no capabilities.
  • datetime - Calendar dates, times, and durations for Ecko: components, arithmetic, and formatting over Unix-ms timestamps. Pure - no capabilities.
  • deque - A double-ended queue for Ecko: push/pop/peek at both ends, amortized O(1). Immutable two-stack deque. Pure - no capabilities.
  • fuzzy - String similarity: Levenshtein, Jaro-Winkler, Dice and Jaccard, plus best-match selection over a list. Pure - no capabilities.
  • geo - Geospatial basics: geohash encode/decode, great-circle distance and bearing, bounding boxes. Pure - no capabilities.
  • graph - Adjacency-list graphs: build, traverse, and answer reachability and path questions. Pure - no capabilities.
  • heap - A priority queue (min-heap) for Ecko: push/pop/peek, heapify, and top-k. Immutable skew heap. Pure - no capabilities.
  • html - A tolerant HTML parser for Ecko: parse to a node tree, extract text, and find elements. Great for feeding web content to ai. Pure - no capabilities.
  • humanize - Durations, byte sizes, relative times, ordinals and counted words for people to read. Pure - no capabilities. The std.humanize module, as a package.
  • i18n - Message catalogues for Ecko: JSON files, {name} interpolation, and CLDR plural rules for the languages that need more than two forms.
  • idempotency - Idempotency-Key store for mutating HTTP APIs, backed by std.sql. Stripe-style create-refund semantics: double-clicks do not double-pay.
  • inflect - URL slugs, English pluralisation, ordinals and identifier case conversion. Pure - no capabilities.
  • ip - IPv4/IPv6 address parsing, validation, CIDR membership, and private-range checks for Ecko. Pure - no capabilities.
  • markdown - Parse Markdown into a block list, render it to HTML, and pull out the headings, code blocks and tables a docs pipeline or RAG ingest actually wants. Pure - no capabilities.
  • mcp - Model Context Protocol, both directions: call an MCP server's tools over stdio or HTTP, or be one.
  • mime - Build MIME messages (multipart, attachments, RFC 5322) and look up media types by file extension. Pure - no capabilities.
  • money - Currency amounts on Decimal: exact arithmetic, ISO 4217 minor units, and allocation that never loses a cent. Pure - no capabilities.
  • mysql - A MySQL/MariaDB client written in Ecko: classic protocol + mysql_native_password over std.net.
  • outbox - Transactional outbox over std.sql: write your business row and the message you owe in the same transaction, then relay it with at-least-once delivery, retry and backoff.
  • perf - Measure the performance of your own Ecko code: time, measure, and bench (best-of-N stats)
  • postgres - A PostgreSQL client written in Ecko: v3 wire protocol + SCRAM-SHA-256 over std.net.
  • queue - Durable job queue with leases over std.sql: enqueue work, workers claim it with a lease, a worker that dies has its job reclaimed once the lease expires.
  • redact - Sensitive-key detection and masking for safe logging: is_sensitive_key, mask, and recursive map_of. Pure - no capabilities.
  • redis - A Redis client (RESP2) written in Ecko, over std.net raw sockets + TLS.
  • semver - Semantic Versioning 2.0.0: parse, compare, sort and match version ranges. Pure - no capabilities.
  • smtp - An SMTP client (RFC 5321) written in Ecko, over std.net raw sockets + STARTTLS.
  • stats - Descriptive statistics for Ecko: mean, median, mode, variance, stdev, quantiles. Pure - no capabilities.
  • struct - Pack and unpack binary data with a struct-style format string, over the bytes type. Pure - no capabilities.
  • textwrap - Wrap, fill, indent, dedent, and shorten text for Ecko - prompt building and terminal output. Pure - no capabilities.
  • tui - Terminal styling and UI composition for Ecko: colours, attributes, hyperlinks and cursor control, plus width-aware padding, bordered boxes and aligned tables.
  • url - URL parsing, query strings, and reference resolution for Ecko.
  • validate - A data/input validation library written in Ecko: composable validator functions, whole-object schemas, collect-all errors. Pure - no capabilities.
  • webkit - Web-app batteries for Ecko: HTML templates that escape by default, CSRF protection, revocable sessions, and the middleware around them. Builds on std.web rather than replacing it.
  • xml - An XML parser for Ecko: parse to a node tree with entities and CDATA decoded, query by tag or path, extract text, and rebuild. Malformed input throws a parse error with a position. Pure - no capabilities.

Ecko - built in Rust. Home at ecko.sh.