Getting Started
Telo is a runtime for declarative backends. You describe what your system should do in YAML, and Telo runs it — no imperative glue code.
This guide walks through installing Telo, running a minimal manifest, and understanding what just happened.
Install
The quickest route needs nothing installed first — not even Node.js. The standalone build is a single executable that carries its own runtime:
curl -fsSL https://telo.sh/install.sh | sh
On Windows:
irm https://telo.sh/install.ps1 | iex
The releases page also carries .deb, .rpm, .pkg and .msi packages, and a
plain archive per platform.
If you already have Node.js and would rather manage Telo with it, install the same CLI from npm:
npm install -g @telorun/cli
# or
pnpm add -g @telorun/cli
Either way the CLI is telo. Verify with:
telo --version
The two builds run the same code and the same manifests. The one difference is
that the standalone build carries no package manager, so a module whose
controller is delivered from npm — rather than bundled in the module's own
artifact, which is how the standard library ships — needs the npm-installed CLI.
telo check warns when a manifest depends on one and the tool is missing.
Prefer not to install globally? Run Telo via Docker instead — the image mounts the current directory and takes the same manifest path the CLI does, so every command below works by substituting it:
docker run -v .:/srv -w /srv telorun/node:latest-slim ./hello.yaml
Your first manifest
Create hello.yaml:
kind: Telo.Application
metadata:
name: HelloConsole
version: 1.0.0
imports:
Console: oci://ghcr.io/telorun/console@0.19.0
targets:
- invoke: !ref Console.writeLine
inputs:
output: "Hello from Telo!"
This declares:
- A
Telo.Application— the runnable root, withwriteLineas its target. - One
imports:entry — pulling in theConsolemodule from the standard library. - Inputs to the
Console.writeLinetarget — a single string to print.
Run it
telo ./hello.yaml
You should see:
Hello from Telo!
telo ./hello.yaml is shorthand for telo run ./hello.yaml — run is the
default command, and both forms appear throughout these docs. The path may be a
manifest file, a directory containing telo.yaml, or an HTTP(S) URL.
What just happened
When you ran telo ./hello.yaml, the kernel:
- Loaded the YAML, resolved each import, and compiled any
!cel "…"expressions into an in-memory registry. - Resolved the resource dependency graph — the target references
Console.writeLine, the singleton exported by the importedConsolemodule. - Initialized each resource in dependency order, calling its controller's lifecycle hook.
- Dispatched the
targetsdeclared on the Application — here, the singleinvoke:step — and ran it to completion.
Telo itself doesn't know what a console is. Each kind: is owned by a
controller, the code that implements the resource's lifecycle. Controllers
ship inside the module's own published artifact, so resolving the Console
import brings its controller with it — nothing is fetched from npm at load. The
kernel just orchestrates loading, init order, references, and dispatch.
Add something runtime-shaped
A console one-shot is the simplest possible Telo manifest, but the runtime really shines once you wire long-running services together. Your first HTTP API is the next step: a server with a validated route, a typed response, and configuration bound to the environment — about fifteen minutes, and it covers every piece an ordinary Telo application is made of.
What's next
- Standard library — the modules you import from: HTTP, SQL, AI, MCP, caching, scheduling, and the rest.
- Kernel reference — resources, capabilities, modules, and the CEL evaluation model.
- Style guide — naming, structure, and CEL conventions.
- CLI reference —
telo run,telo check,telo install,telo upgrade,telo publish(to OCI), and watch mode. - Catching errors before they run — what
telo checkfinds, and how to debug what it cannot.