Extensions
Edit on GitHubOrrery is a harness you shape. Extensions are how: tools, model providers, commands, UI clients, MCP servers, memory and more. The built-in harness is made of the same parts anyone else gets. Most of what ships with orrery (each provider, the git tools, the theme, the agents) is itself an extension.
What an extension can provide
One package can provide several things at once. Many extensions may contribute to each of these:
| Field | What it adds |
|---|---|
tools | Tools the model (and a person, through a command) can call. |
providers | Model providers. |
agents · workflows | Role agents and multi-step orchestrations. |
interceptors · lifecycle | Hooks around a call; session start and stop handlers. |
commands | CLI subcommands. |
skills | Prompt-level capabilities. |
mcp | MCP servers exposed as tools. |
clients · elements | A UI, and UI elements. |
views · graders | Surface bindings for loop events, and eval verdicts. Show things with ctx.ui.show and the other ctx.ui.* calls (Show things: placements, Tell the person). renderers is accepted and unused: an extension never draws. |
Exactly one extension may hold each of these: memory, session, permissions, router. A second claim is a load error, not a silent override.
The rule behind the list: if a subsystem can be replaced, it is a field here. If it is not a field, it is part of the kernel's boundary.
Runtimes
The manifest's runtime line says how the code runs. The rest is the same in every language: the same manifest, the same grants, the same namespacing.
| Runtime | How it runs | Boundary |
|---|---|---|
wasm | A WebAssembly component. | A real sandbox: no file access except through the host. |
node | A Node.js child process, using @getorrery/ext. | Capabilities are withheld, not sandboxed. |
python | A Python child process. | Same as node. |
process | Any program that speaks the protocol, started by a [process] table. | Same as node. |
native | Rust compiled into the harness. | No process boundary. |
none | No process: a manifest that only declares components, their data and actions (Components). | Nothing runs; it reads only the files its read grant covers. |
node, python and process extensions get no file, process or network handles: everything goes through ctx, which the policy engine checks. But a determined extension in those runtimes can reach around that. Only wasm is a real boundary. See Grants and the supply chain.
Tools are namespaced
A tool's name is always <extension id>.<tool>: buildgraph.impacted, git.commit, mcp.github.search_issues. Two extensions cannot claim the same name, and a name in a transcript says where it came from.
Where extensions live
| Layer | Folder |
|---|---|
| User | ~/.orrery/extensions/<id>/, in every project |
| Workspace | <workspace>/.orrery/extensions/<id>/, committed with the repository |
Each folder holds an orrery.toml manifest. Both folders are scanned at session start. A workspace's extensions load only once you trust the workspace.
Settings for an extension go under its id:
[extensions.theme]
default = "dark"See what loaded
orrery ext list # loaded, degraded and skipped, with reasons
orrery ext show theme # what one registered: tools, commands, statedegraded means a grant it asked for was not given. The tools that need it are disabled and the rest work. It is not a failure.