Answers to the questions that come up most often about how Orchard works, what it guarantees, and how it differs from calling a model SDK directly.
Is Orchard compiled or interpreted?
Both, and they agree. Source lowers to a stable, diffable JSON IR with orch compile, and the interpreter runs that IR. Because the compiled IR is exactly what the interpreter executes, a compiled run and an interpreted run of the same program produce identical behavior. You can inspect the IR with orch compile -o ir.json and diff it across changes.
Does it run offline?
Yes. The mock provider replays a scripted conversation (or echoes) instead of calling a real model, so you can develop and test with zero keys, zero network, and zero cost. Everything around the model stays real: tools run, remember writes to the store, and skills are genuinely invoked by the loop. To go live, swap the model block for a real provider, name, and key. The rest of the agent is unchanged.
model { provider: mock, name: "echo" } // offline
model { provider: anthropic, name: "claude-sonnet-4-6" } // liveWhich model providers are supported?
anthropic, openai, ollama, groq, together, openrouter, and mock. API keys are read from each provider's standard environment variable, so no key material lives in the file. Ollama needs no key and runs fully local. You can also declare a fallback chain so the runtime moves to the next provider if the first is unreachable after retries.
model {
provider: anthropic
name: "claude-sonnet-4-6"
fallback: [{ provider: openai, name: "gpt-5.2" }]
}How is model output validated?
With gen as T. The runtime lowers the type T to a JSON schema, instructs the model to emit matching JSON, then parses and coerces the result into a value of T. On a parse or validation failure it re-prompts, bounded by policy.max_gen_retries. If it still cannot produce a valid value, it raises a catchable GenError. So once a gen as Ticket returns, you have a real, valid Ticket, and a match over its enum fields is checked exhaustive at compile time.
What is the difference between fn, tool, and skill?
A fn is a pure helper: no gen, no delegate, no tools, no state. It is statically checked to be pure, so it is just computation. A tool is a deterministic effect (HTTP, shell, or computation) that the model can call by name during the loop; its body may use http or shell but never gen, delegate, or state. A skill is a model-using procedure that may itself call gen and orchestrate other steps; it is exposed to the loop as skill_<name> and is also callable from your code and the CLI.
How does concurrency work, and is it safe?
Three primitives: spawn e schedules e on the executor and returns a future, await f joins it, and parallel { a: …, b: … } is a barrier that returns only when every named branch has settled. Concurrency is structured: spans are bounded by their enclosing scope. Budget accounting (spend, steps, tool calls) is atomic and pre-reserved at spawn, shared safely across concurrent spans, and a cancelled span's pre-reserved budget is refunded. On native this runs on a real executor; on WebAssembly a cooperative executor interleaves the same code at await points.
Is agent state durable?
Yes. state is typed, author-declared, and transactional: changes are committed at the end of a turn and rolled back on an uncaught error. The store defaults to redb on native and an in-memory store on WebAssembly and in tests. Conversation history, free-form facts (the remember / recall / forget layer), semantic memory, and traces all live in the same store, and a host can inject its own.
How do I embed Orchard in my app?
Orchard is a library first; the orch CLI is a thin layer over it. The same core API is wrapped for Rust, C (FFI), Python (PyO3), and WebAssembly (wasm-bindgen). You load and check a program, build a session, and drive it with message, task, or a named skill. Every boundary (provider, store, HTTP client, tools, embedder, clock) is a trait with a pure-Rust default, so a session builds with zero config and a host can override any piece. See the embedding guide for code in each language.
How does Orchard keep an autonomous agent safe?
Every agent runs inside a policy envelope that the runtime, not the model, enforces. It includes spending, step, and tool-call budgets (atomic and pre-reserved); an egress guard that blocks loopback and private addresses; shell gating (off by default, or an interactive ask per command); secret taint tracking with redaction in logs and traces; a circuit breaker on repeatedly failing tools; and an unattended spend cap for long-running orch serve sessions. A budget block opens a nested scope that can only tighten an outer limit, never loosen it, and a host that embeds the runtime can tighten the declared policy but never relax it. A prompt-injected or confused model cannot talk its way past these gates.
How is it different from calling a model SDK directly?
With an SDK you write glue code by hand: prompt strings, JSON parsing, retries, tool dispatch, and budget tracking, all in a general-purpose language that knows nothing about any of it. Orchard puts those in the language. Generation is typed and validated (gen as T), routing over the result is an exhaustive match the checker proves complete, tools and concurrency are language primitives, and the safety envelope is declared and enforced. One program runs offline with the mock provider, against any supported model, and embeds in Rust, C, Python, or the browser without rewriting.
Where do I report bugs or contribute?
On GitHub. Open an issue for a bug or a feature request, or a pull request to contribute. The language, runtime, examples, and docs all live there.
