levain-runtime
Run a Levain agent on your own machine or in your own container image. The platform keeps the recipe, its versions, the run history and the improvement loop; this runtime executes the agent's nodes where your data, tools and dependencies live.
The runtime never accepts inbound connections. It opens one outbound tunnel to the platform and serves node execution over it. Model calls either go through the platform (metered as usual) or use the Claude login present on the runtime host, as configured per runtime.
Install
uv tool install levain-runtime
Set up
# Run nodes directly on this host:
levain-runtime init --mode direct
# Direct mode serving four sessions at once (Linux with bubblewrap):
levain-runtime init --mode direct --sessions 4
# Or run each session in a container built from your own image:
levain-runtime init --mode docker --image ghcr.io/you/agent-runtime:latest
Direct mode shows nodes /workspace, /recipes, /sdk and /sandbox from
the runtime's home directory (~/.levain/runtime). On Linux with bubblewrap
installed each node sees them inside its sandbox and init needs no root;
runtimes with different homes (LEVAIN_RUNTIME_HOME) can share a filesystem.
Without bubblewrap the command prints and runs ln commands linking them at
the filesystem root, with sudo when / is not writable. On macOS the links
come from /etc/synthetic.conf: init writes the entries and asks for one
restart.
A docker image needs Python 3.12 or later with levain-sdk[review,models]
installed, plus whatever your recipes import.
Register and run
export LEVAIN_API_KEY=... # a workspace API key
levain-runtime register --name workstation --model-auth local
levain-runtime up
register creates the runtime in your workspace and stores its credentials
here. If the runtime was created elsewhere (the Levain app or API), pass the
credentials it returned instead:
levain-runtime connect --credentials runtime-credentials.json.
Re-running register with a name the workspace already holds reclaims that
runtime with new credentials; the agents bound to it stay bound. It is refused
while the runtime is online or unreachable, unless you pass --take-over.
up starts the local node server, the tunnel client and a heartbeat so the
platform can show whether the runtime is online. It fetches the current tunnel
settings from the platform on every start. levain-runtime status prints the
configuration and the sessions on this runtime.
Upgrade
A platform release can need a newer runtime: the node and turn entrypoints
under /sandbox ship with this package, and runs wait while the runtime is too
old for them. The Runtimes page in the Levain app, and runner_version in
GET /api/v1/runtimes, show the version each runtime runs;
levain-runtime --version prints it here.
While up runs, the platform's heartbeat reply names the version it needs.
levain-runtime status prints it (required: 0.1.15 (upgrade needed)), and
GET http://127.0.0.1:17777/v1/health carries version, required_version
and upgrade_needed, so whatever keeps the runtime running can upgrade it
before any run waits.
uv tool upgrade levain-runtime
systemctl --user restart levain-runtime # or however `up` runs on this host
up rewrites /sandbox from the installed release on every start, so an
upgrade needs no new init. Restarting ends the nodes the runtime was running,
so upgrade between runs.
How it works
Each agent session gets a workspace directory under the runtime home
(sessions/<id>/workspace, or agents/<id>/workspace for agents with a
persistent workspace). The platform delivers the recipe and SDK into /recipes
and /sdk, then runs each node as a short-lived process with the session's
environment. Everything a node prints is streamed back and lands in the run's
logs on the platform.
Direct mode serves as many sessions at once as the machine can isolate, or
up to --sessions N: each node runs under bubblewrap with its session's
folders mounted at /workspace, /recipes/active and /sdk/active; the real
workspace path is in LEVAIN_HOST_WORKSPACE for tools outside the sandbox
such as Docker. Without bubblewrap it serves one session at a time, with
/workspace linked to the active session. Docker mode starts one container
per session and has no such limit.
Sessions and your Claude login
In direct mode an agent's turns run in the session's workspace directory with
this machine's Claude login and config dir (CLAUDE_CONFIG_DIR, or
~/.claude). Claude Code files the agent's sessions where it files yours, under
that directory's entry, so from the workspace directory claude --resume lists
the agent's sessions next to your own and continues any of them.
levain-runtime status prints each session's workspace and the Claude session
it continues.
The map from a Levain session to its Claude session lives in
<config dir>/levain-sessions/. To have an agent continue a session you started
yourself in its workspace directory:
levain-runtime adopt <levain session id> <claude session id>
The agent's next turn resumes that session. Docker mode copies your login into each container instead; its sessions stay in the container.
Per-turn environment
In direct mode the runtime reads env.json in its home before every node it
runs, so you can change a node's environment between turns without a restart.
For example, you can point one agent at another Claude login:
{
"all": { "HTTP_PROXY": "http://127.0.0.1:3128" },
"agents": {
"<agent id>": { "CLAUDE_CONFIG_DIR": "/home/me/.claude-second" }
}
}
An agent's entry wins over all, and both win over anything the platform sends
for the session or the turn. The session map follows the CLAUDE_CONFIG_DIR a
turn gets, as do status and adopt. To switch the value a turn starts with,
write the new file next to it and rename it into place.
The file must belong to the user the runtime runs as, and no one else may be
able to write it (chmod 600). If it is not, or it is malformed, nodes fail
with an error that names the file, never its values. The values stay on this
machine: they are not logged and not sent to the platform. Without the file,
nodes get the runtime's own environment. Docker mode ignores the file.
Waiting sessions
In direct mode, when an agent's turn stops to wait, the platform gives the
session's slot back and tells the runtime what the session waits on. The
runtime keeps one file per waiting session, waiting/<session id>.json under
its home, so a process on this machine can react without polling the API:
{
"session_id": "…",
"agent_id": "…",
"workspace_id": "…",
"run_id": "…",
"reason": "user_input",
"payload": { "question": "…" },
"since": "2026-10-29T08:12:44.018311+00:00"
}
reason is user_input (a person's reply), child_agent (another agent's
run) or external (an outside event); payload is what the agent passed with
the wait. Each file is renamed into place, so a watcher of *.json only sees
whole files. It is removed when the session resumes, is provisioned again or
is deleted; a restart of the runtime keeps it.
Metadata
Release files for levain-runtime 0.1.29
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| levain_runtime-0.1.29.tar.gz | 44.1 kB | With provenance | Details for levain_runtime-0.1.29.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| levain_runtime-0.1.29-py3-none-any.whl | Python 3 | none | any | Details for levain_runtime-0.1.29-py3-none-any.whl |
Total release size: 94.9 kB
Release files / levain_runtime-0.1.29.tar.gz
| Download URL | levain_runtime-0.1.29.tar.gz |
|---|---|
| Size | 44.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4c4e8c4b6a5c9338aa2ab98101d8f8c55dc29017791da8ed616a1dd3e41f418a
|
|
BLAKE2b-256 checksum How to use checksums |
eaf03135f75fc6da561debd72300032f2d75d389cf8fbf839a855c14087ed3c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logRelease files / levain_runtime-0.1.29-py3-none-any.whl
| Download URL | levain_runtime-0.1.29-py3-none-any.whl |
|---|---|
| Size | 50.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9b790a4741f113344479ad489f78b7594a2ad970098b1c966c645a083176659d
|
|
BLAKE2b-256 checksum How to use checksums |
b751ffdc7e0b38d969ece886b2154a33a4a6ac39abb8a47e5183c5a2aadcb6b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logSecurity for levain-runtime 0.1.29
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for levain_runtime-0.1.29.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.