openai-agents-hyperlight
A sandbox provider for the OpenAI Agents SDK that runs a sandbox agent's shell commands in local Hyperlight micro-VMs.
Each command runs in a fresh micro-VM with its own kernel, restored from a warm snapshot of a Unikraft guest in tens of milliseconds, through the hluk CLI. It needs no account, no daemon and no API key beyond the model's. The guest has no network unless you allow hosts with net_allow, and sees nothing of the host but the sandbox's workspace and the directories you grant it.
UnixLocalSandboxClient |
Docker | E2B, Modal, Daytona, … | Hyperlight | |
|---|---|---|---|---|
| Commands run as | host processes | containers, shared host kernel | remote sandboxes | micro-VMs, their own kernel |
| Runs on your machine | yes | yes | no | yes |
| Needs | nothing | Docker daemon | account, API key | hluk and KVM or MSHV |
| Network | host's | open by default | provider's | none unless allowed |
Install
Linux only, on a host with KVM or MSHV. Install hluk v0.19.0 or later:
curl -fsSL https://raw.githubusercontent.com/hyperlight-dev/hyperlight-unikraft/main/install.sh | sh
Then the package, which brings the SDK (openai-agents):
pip install openai-agents-hyperlight
Check both with the demo, which runs a command in a micro-VM through the SDK's Runner and a scripted model, so it needs no API key:
python -m openai_agents_hyperlight.demo # prints "hyperlight provider works"
The first run pulls the agent image of your hluk release (ghcr.io/hyperlight-dev/hyperlight-unikraft/agent:initrd-v<version>) and saves a warm snapshot of it; every command after that starts from the snapshot. The image ships BusyBox and CPython 3.12 with numpy, pandas, scipy, scikit-learn, matplotlib, pillow and requests. Both stay in hluk's cache ($HLUK_CACHE_DIR, else ~/.cache/hluk); hluk cache ls lists them.
Use
Pass the client to SandboxRunConfig:
from agents import Runner
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.capabilities import Shell
from agents.sandbox.entries import File
from openai_agents_hyperlight import HyperlightSandboxClient, HyperlightSandboxClientOptions
agent = SandboxAgent(
name="analyst",
instructions="Answer from the files in the workspace.",
default_manifest=Manifest(entries={"data.csv": File(content=b"x,y\n1,2\n3,4\n")}),
capabilities=[Shell()],
)
result = await Runner.run(
agent,
"What is the mean of y?",
run_config=RunConfig(
sandbox=SandboxRunConfig(
client=HyperlightSandboxClient(),
options=HyperlightSandboxClientOptions(net_allow=("pypi.org", "files.pythonhosted.org")),
)
),
)
The runner then owns the session: it creates it, starts it, and removes the workspace the client made once the run ends, however it ends. Before removing it, the SDK saves a copy of the workspace as the session's snapshot, by default a tar under $XDG_STATE_HOME/openai-agents-python/sandbox/snapshots/ (~/.local/state/...) that is kept; pass SandboxRunConfig(snapshot=...) to put it elsewhere, or NoopSnapshotSpec() to keep none. A session you create yourself, with await client.create(...) and SandboxRunConfig(session=session), is yours: close it with async with session: or await session.aclose(), and remove it with await client.delete(session), which removes only a workspace the client made, never a Manifest(root=...) of yours. A run interrupted for approval resumes from its saved state (RunState.to_json()) with any HyperlightSandboxClient in a process that imported this package, and the same temporary directory (TMPDIR) when the client made the workspace: it is made again there, owner-only, and refused elsewhere.
Options
HyperlightSandboxClientOptions, or, in SandboxRunConfig(options=...), {"type": "hyperlight", ...} as a dictionary. Unknown fields are refused.
| Option | Default | |
|---|---|---|
runtime |
"agent" |
Published image to run, by name or full reference. It needs hluk's Python driver and /bin/sh |
initrd |
Path to a rootfs CPIO of your own to run instead | |
scratch_mb |
the image's | Guest memory in MiB |
net_allow |
none | Hosts or IPs the guest may connect to. With none, every connection is refused |
HyperlightSandboxClient takes hluk_path (default: hluk on PATH), startup_timeout_s (300 s; the first run pulls the image) and max_workspace_bytes (1 GiB; None for no limit), the largest workspace archive it snapshots. They stay on the client, so a saved session state cannot choose which program the host runs. The guest's options do not: a saved state, and so a saved RunState, names its image, memory and net_allow, so resume only states you trust as you would the code that made them.
What persists
- The workspace. It is a host directory, shared read-write into each guest at the same path, so files written there are there for the next command. Directories in
Manifest.extra_path_grantsare shared the same way, read-only when the grant is. - Nothing else. Each command boots a fresh guest: packages a command installs outside the workspace, its environment changes, and processes it leaves running end with it. To keep a package, install it into the workspace:
HOMEis the workspace, sopython3 -m pip install --user <package>(withnet_allownaming the package index) puts it in its.local, where later commands import it from. The image has nopipcommand, onlypython3 -m pip. - No command outlives its wait. With no PTY, the SDK's
exec_commandtool stops a command, and its guest, once itsyield_time_ms(10 s by default) runs out, and the model is told it timed out. A model running a long command has to ask for a longer one: a largepip install, or any command importing heavy libraries, which every command does afresh in a new guest (scikit-learn about 7 s, scipy 4 s, pandas 2 s). - Snapshots. Workspace snapshots (
LocalSnapshotSpecand the others) work as they do forUnixLocalSandboxClient; the warm snapshot of the guest ishluk's and holds no workspace data. Commands still running when the session stops are stopped first, so that no guest writes the workspace while it is archived. The SDK archives it in memory, so a workspace whose archive would be larger thanmax_workspace_bytes(sized as the SDK would make it, a sparse file at its apparent size, ephemeral entries left out) is not snapshotted. A session the runner owns logs it and goes on, its workspace removed as usual, and the result has no sandbox state to resume from. On a session of yours,aclose()raisesWorkspaceStopErrorand leaves the session running: callshutdown()andclient.delete(session)yourself. Commands are stopped, and the size checked with none running, only as the session stops: a directpersist_workspace()call, while commands run, archives what they leave as it goes.
Not supported
Refused with an error, never run on the host instead: PTY sessions and write_stdin, exposed ports, sandbox users and groups, manifest mounts, Git repository entries, path grants with a host_path, and grants of single files.
The guest runs no script by its #! line (./run.sh fails with "Exec format error"), the console scripts pip installs included: run them with their interpreter (sh run.sh, python3 -m <module>).
The trust boundary
The guest is treated as untrusted. File operations run on the host, with the SDK's UnixLocal descriptor-relative checks, against a directory the guest can also write:
- A shared path may not go through a symlink or overlap another, checked when the session starts and before each command, so a guest cannot swap a link to widen what the next one sees.
- A command's environment reaches the guest in an owner-only file, never on
hluk's command line. - What the guest leaves for the host (exit status, output) is read without following links, from regular files only, and within bounds (16 MiB of output per stream).
- Removing a workspace never follows a link the guest left in it, and removes trees the guest made deep or unreadable.
- A command that stops its own guest (
kill -9 -1) is a failed command, with the output it wrote and the statushlukreports the guest exited with, as is one with a memory fault in any of its processes, which aborts the guest's kernel and so ends the whole command, with 139; the next command gets a fresh guest. A failure ofhlukor the hypervisor is still an error of the sandbox. - A guest can see the names, not the contents, of the empty directories that were mount points when its warm snapshot was saved: the workspace and control directory of the session that first started it.
Compatibility
The session builds on the SDK's UnixLocalSandboxSession, including some of its underscored hooks, so each SDK minor release is tested before the dependency range in pyproject.toml takes it in; CI tests its lowest and highest. Saved session states (type: "hyperlight") keep working across 0.1.x: a test resumes one saved by 0.1.0.
Development
pip install -e '.[test]'
pytest # the client, the Runner path and the guest's script, against a stand-in for hluk
RUN_ALL=1 pytest # also in real micro-VMs, with the hluk on PATH
To test a local build, set HLUK_PATH to its hluk, HLUK_INITRD to its agent rootfs, and HLUK_SCRATCH_MB to the guest memory that rootfs needs. CI does this so each commit is tested against its own build.
Metadata
Release files for openai-agents-hyperlight 0.1.0
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 |
|---|---|---|---|---|
| openai_agents_hyperlight-0.1.0.tar.gz | 41.1 kB | With provenance | Details for openai_agents_hyperlight-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| openai_agents_hyperlight-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for openai_agents_hyperlight-0.1.0-py3-none-any.whl |
Total release size: 67.8 kB
Release files / openai_agents_hyperlight-0.1.0.tar.gz
| Download URL | openai_agents_hyperlight-0.1.0.tar.gz |
|---|---|
| Size | 41.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
081d3659bf59fba8b520e65879d68ebf936b233deee7ff0136886689a3427111
|
|
BLAKE2b-256 checksum How to use checksums |
8dc43ff9a785679107f8f2d9813a50e1a83d4156dd1bdf24657eec0a2fddfe66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 27, 2026.
Transparency logRelease files / openai_agents_hyperlight-0.1.0-py3-none-any.whl
| Download URL | openai_agents_hyperlight-0.1.0-py3-none-any.whl |
|---|---|
| Size | 26.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7fc152a748be7945c24c1167036486902613e508891019d74b5cd2b7fdc35bd1
|
|
BLAKE2b-256 checksum How to use checksums |
0f2c9490df286872ecb310865fec42ed077e172e90d61f952a9a7996e5c2af5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 27, 2026.
Transparency logSecurity for openai-agents-hyperlight 0.1.0
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 openai_agents_hyperlight-0.1.0.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 Oct 27, 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.