Browse documentation

Quickstart

Scaffold a project, start a local runtime, run an agent, and answer its escalation.

You need the CLI and Docker from Installation. Everything here happens in one terminal.

Scaffold a project

katari init hello --dir hello
cd hello
  + README.md
  + compose.yaml
  + .env.example
  + .gitignore
  + katari.toml
  + src/hello.ktr
Initialized hello
hint: docker compose up -d && katari apply && katari run hello.main

katari.toml names the package and points at a runtime (http://localhost:3000 by default). src/hello.ktr defines one request and one agent:

request ask_name(prompt: string) -> string
 
agent main() -> string with ask_name {
  let name = ask_name(prompt = "What is your name?")
  f"Hello, ${name}!"
}

Nothing handles ask_name, so calling it escalates: the question leaves the run and waits for a human. You are about to be that human.

Start a local runtime

The scaffolded compose.yaml runs the runtime image, PostgreSQL, and an S3-compatible blob store. It needs two keys in .env:

export KATARI_API_KEY=$(openssl rand -hex 32)
cp .env.example .env
echo "KATARI_API_KEY=$KATARI_API_KEY" >> .env
echo "KATARI_SECRET_KEY=$(openssl rand -base64 32)" >> .env
docker compose up -d

KATARI_API_KEY is the bearer token every caller authenticates with; KATARI_SECRET_KEY encrypts secret values at rest. The runtime refuses to boot without either.

Note: the CLI reads KATARI_API_KEY from your shell environment, not from .env — that is why the export comes first. In a new terminal, export it again (the value is in .env).

Compile and deploy

katari check
OK — 16 module(s), no errors

check compiles locally and reports diagnostics; nothing leaves your machine. apply compiles too, then uploads the result to the runtime as an immutable snapshot:

katari apply
Creating project hello
Deploying hello to http://localhost:3000
  16 changed, 0 unchanged, 0 removed
  + hello
  + prelude
  ...
Applied snapshot 980c94ce-fa15-45bb-ac6c-3f9b064ee87b to project hello

Run it

katari run hello.main
Started run 3acbdecd-f74c-424f-bff9-33087242aec2 (Ctrl-C detaches; the run keeps going)
  12:11:34 delegate api→core hello.main [04fbd8f5]
  12:11:34 delegate core→core hello.ask_name [e233d5ab]
  12:11:34 escalate core→core request hello.ask_name [e233d5ab/362ed1d7]
  12:11:34 escalate core→api request hello.ask_name [04fbd8f5/42d264c0]

The run is asking hello.ask_name: {"prompt":"What is your name?"}
? answer (string) Ada
Answered; waiting on the run again...
  12:11:34 escalateAck api→core [04fbd8f5/42d264c0]
  12:11:34 escalateAck core→core [e233d5ab/362ed1d7]
  12:11:34 delegateAck core→core [e233d5ab]
  12:11:34 delegateAck core→api [04fbd8f5]
"Hello, Ada!"

The trace streams live: the run delegates to hello.main, hits ask_name, and escalates. katari run turns the open question into a terminal prompt; your answer resumes the run, and the result comes back.

Answer from outside the run

That inline prompt is a convenience, not the model. The escalation is a durable row on the runtime — the run parks on it for as long as it takes. Start a run without waiting on it:

katari run hello.main --detach
katari ls escalations
ID        RUN       REQUEST        QUESTION                         CREATED
0affdd73  f51154d2  hello.ask_name  {"prompt":"What is your name?"}  2026-07-17 12:09

Answer it — hours later, from another machine, id prefixes are enough:

katari answer 0affdd73 --value '"Grace"'
katari status f51154d2
Run           f51154d2-1403-45c5-bb23-0016daf6320d
Name          hello.main
Agent         hello.main
State         done
Snapshot      980c94ce-fa15-45bb-ac6c-3f9b064ee87b
Argument      {}
Result        "Hello, Grace!"
...

Look at the console

Open http://localhost:3000 and paste your KATARI_API_KEY (it is in .env). The console shows the project you just deployed: its runs with their delegation trees and traces, the escalation inbox — where you could have answered ask_name instead — snapshots, env entries, and files. The runtime tours the screens.

When you are done:

docker compose down

State lives in named Docker volumes, so up -d brings everything back — including any run still parked on a question. Add -v to wipe it.

Next

  • Tutorial — from here to a Discord bot that gives a model your agents as tools.
  • Escalation — what actually happened when ask_name had no handler.
  • The CLI — the commands you just used, and the rest.