langchain-hyperlight 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langchain_hyperlight-0.1.0/.gitignore +17 -0
- langchain_hyperlight-0.1.0/LICENSE +15 -0
- langchain_hyperlight-0.1.0/PKG-INFO +304 -0
- langchain_hyperlight-0.1.0/README.md +272 -0
- langchain_hyperlight-0.1.0/examples/basic_agent.py +66 -0
- langchain_hyperlight-0.1.0/manual_test.py +153 -0
- langchain_hyperlight-0.1.0/pyproject.toml +57 -0
- langchain_hyperlight-0.1.0/src/langchain_hyperlight/__init__.py +33 -0
- langchain_hyperlight-0.1.0/src/langchain_hyperlight/py.typed +0 -0
- langchain_hyperlight-0.1.0/src/langchain_hyperlight/tool.py +545 -0
- langchain_hyperlight-0.1.0/tests/test_tool.py +150 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: langchain-hyperlight
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LangChain tool for executing untrusted code in Microsoft Hyperlight micro VMs (experimental, AI-generated)
|
|
5
|
+
Project-URL: Homepage, https://github.com/wildaces215/langchain-hyperlight
|
|
6
|
+
Project-URL: Repository, https://github.com/wildaces215/langchain-hyperlight
|
|
7
|
+
Project-URL: Documentation, https://github.com/wildaces215/langchain-hyperlight#readme
|
|
8
|
+
Author: theph03*nix215
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: code-execution,hyperlight,langchain,micro-vm,sandbox,wasm
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: hyperlight-sandbox[python-guest,wasm]>=0.5.0
|
|
25
|
+
Requires-Dist: langchain-core>=0.3.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Provides-Extra: js
|
|
30
|
+
Requires-Dist: hyperlight-sandbox[hyperlight-js,javascript-guest]>=0.5.0; extra == 'js'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
> ⚠️ **VIBE-CODED — AI-GENERATED, NOT PRODUCTION-READY**
|
|
34
|
+
>
|
|
35
|
+
> This project was written by an AI ("vibe coded"). It is **experimental** and has had
|
|
36
|
+
> **no security review, fuzzing, or adversarial testing**. It has only been smoke-tested
|
|
37
|
+
> on **Linux (x86_64)**. **Use at your own risk** — do not rely on it for anything
|
|
38
|
+
> security-sensitive or mission-critical. See [Limitations](#limitations).
|
|
39
|
+
|
|
40
|
+
# langchain-hyperlight
|
|
41
|
+
|
|
42
|
+
A [LangChain](https://www.langchain.com/) tool that executes untrusted code inside a
|
|
43
|
+
[Microsoft Hyperlight](https://github.com/hyperlight-dev/hyperlight) **micro virtual machine**.
|
|
44
|
+
|
|
45
|
+
Hyperlight is a lightweight Virtual Machine Manager (VMM) designed to be embedded within
|
|
46
|
+
applications. It runs untrusted code in hardware-isolated micro VMs (KVM, MSHV, or Hyper-V)
|
|
47
|
+
with very low latency and minimal overhead. This package exposes that capability to LangChain
|
|
48
|
+
agents as a standard tool, so an LLM can safely run arbitrary code without touching the host.
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- **Hardware isolation** — code runs in a micro VM, not on the host.
|
|
53
|
+
- **Host tool dispatch** — register host callables that guest code invokes by name with
|
|
54
|
+
schema-validated arguments (`call_tool(...)`).
|
|
55
|
+
- **Capability-based file access** — read-only `/input`, writable `/output`, strict path isolation.
|
|
56
|
+
- **Network allow-listing** — network is off by default; opt in per-domain and per-HTTP-verb.
|
|
57
|
+
- **Snapshot / restore** — capture and rewind sandbox state.
|
|
58
|
+
- **Lazy sandbox creation** — constructing the tool is cheap; the micro VM boots on first use.
|
|
59
|
+
|
|
60
|
+
## Limitations
|
|
61
|
+
|
|
62
|
+
This is an early-stage, AI-generated integration. Be aware of the following before adopting it.
|
|
63
|
+
|
|
64
|
+
### Platform
|
|
65
|
+
|
|
66
|
+
- **x86_64 only.** Hyperlight currently targets x86_64; there are no `aarch64` (ARM) wheels.
|
|
67
|
+
Raspberry Pi, Apple Silicon, and AWS Graviton are unsupported.
|
|
68
|
+
- **glibc 2.34+.** The Rust backend ships `manylinux_2_34_x86_64` wheels, so it needs a recent
|
|
69
|
+
glibc. Works on Ubuntu 22.04+, Debian 12+, Fedora 36+, RHEL 9+. Does **not** work on
|
|
70
|
+
Ubuntu 20.04, Debian 11, RHEL 8, or musl-based distros (Alpine, Void) without building the
|
|
71
|
+
Rust backend from source.
|
|
72
|
+
- **Python 3.10–3.14.**
|
|
73
|
+
- **A hypervisor is required at runtime:** KVM (`/dev/kvm`) or MSHV on Linux.
|
|
74
|
+
- **Tested on Linux only.** This package has only been tested on **Linux (x86_64)**. It is
|
|
75
|
+
**not tested on Windows or macOS** — use on those platforms at your own risk.
|
|
76
|
+
|
|
77
|
+
### Security model
|
|
78
|
+
|
|
79
|
+
- The micro VM isolates the *guest code* you run, but any **host tools you register via
|
|
80
|
+
`host_tools` run with full host privileges** inside the sandbox's `call_tool(...)`. Only
|
|
81
|
+
register callables you trust, and treat their inputs as untrusted.
|
|
82
|
+
- Network is off by default and gated by `allowed_domains`, but an allow-listed domain is
|
|
83
|
+
reachable by any code running in the sandbox.
|
|
84
|
+
- This package has **not** been security-reviewed. Do not treat it as a hardened sandbox
|
|
85
|
+
boundary without your own audit.
|
|
86
|
+
|
|
87
|
+
### Maturity
|
|
88
|
+
|
|
89
|
+
- **Alpha / vibe-coded.** No fuzzing, no adversarial testing, no cross-platform CI matrix.
|
|
90
|
+
- The thread-confinement worker (required because the `WasmSandbox` is `unsendable` in PyO3)
|
|
91
|
+
is correct for the tested paths but has not been stress-tested under heavy concurrency.
|
|
92
|
+
- `host_tools` accepts plain Python callables only — it does not yet wrap LangChain
|
|
93
|
+
`BaseTool` instances directly.
|
|
94
|
+
|
|
95
|
+
## Installation
|
|
96
|
+
|
|
97
|
+
> **Platform support:** this package is **tested on Linux (x86_64) only**. It is
|
|
98
|
+
> **not tested on Windows or macOS** — install and use on those platforms at your own risk.
|
|
99
|
+
|
|
100
|
+
```shell
|
|
101
|
+
pip install langchain-hyperlight
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
This pulls in `langchain-core` and `hyperlight-sandbox[wasm,python_guest]`.
|
|
105
|
+
|
|
106
|
+
> **Prerequisite:** a working hypervisor is required at *runtime* (not at install time):
|
|
107
|
+
>
|
|
108
|
+
> - **Linux:** KVM (`/dev/kvm`) or MSHV (`/dev/mshv`)
|
|
109
|
+
|
|
110
|
+
## Quick start
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from langchain_hyperlight import HyperlightSandboxTool
|
|
114
|
+
|
|
115
|
+
tool = HyperlightSandboxTool(
|
|
116
|
+
host_tools={
|
|
117
|
+
"add": lambda a=0, b=0: a + b,
|
|
118
|
+
"greet": lambda name="world": f"Hello, {name}!",
|
|
119
|
+
},
|
|
120
|
+
allowed_domains={"https://httpbin.org": ["GET"]},
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
result = tool.invoke({
|
|
124
|
+
"code": """
|
|
125
|
+
total = call_tool('add', a=3, b=4)
|
|
126
|
+
greeting = call_tool('greet', name='James')
|
|
127
|
+
print(f"3 + 4 = {total}")
|
|
128
|
+
print(greeting)
|
|
129
|
+
""",
|
|
130
|
+
})
|
|
131
|
+
print(result)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Using it inside an agent
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from langchain_core.tools import create_agent # or your agent of choice
|
|
138
|
+
|
|
139
|
+
agent = create_agent(model, tools=[tool])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The tool is a standard `langchain_core.tools.BaseTool`, so it works with any LangChain agent
|
|
143
|
+
runtime (LangGraph, `create_agent`, `AgentExecutor`, etc.).
|
|
144
|
+
|
|
145
|
+
## Relationship to Microsoft's Agent Framework
|
|
146
|
+
|
|
147
|
+
Microsoft ships an official Hyperlight integration for *its own* Agent Framework:
|
|
148
|
+
[`agent-framework-hyperlight`](https://github.com/microsoft/agent-framework/tree/main/python/packages/hyperlight)
|
|
149
|
+
(`HyperlightExecuteCodeTool` / `HyperlightCodeActProvider`). This package is the **LangChain**
|
|
150
|
+
equivalent: it targets `langchain_core.tools.BaseTool` and mirrors the same concepts — the
|
|
151
|
+
`execute_code` tool name, `file_mounts`, `allowed_domains`, and host-tool dispatch via
|
|
152
|
+
`call_tool(...)` — so the mental model transfers directly.
|
|
153
|
+
|
|
154
|
+
### Thread safety
|
|
155
|
+
|
|
156
|
+
The Hyperlight `WasmSandbox` is `unsendable` in PyO3: it may only be accessed and dropped from
|
|
157
|
+
the OS thread that created it, or it panics. This tool routes every sandbox operation through a
|
|
158
|
+
dedicated single-threaded worker, so it is safe to call from arbitrary threads and event loops
|
|
159
|
+
(including LangChain's async `ainvoke`).
|
|
160
|
+
|
|
161
|
+
## Guest environment
|
|
162
|
+
|
|
163
|
+
By default the sandbox runs **Python**. Inside the guest, these built-ins are available:
|
|
164
|
+
|
|
165
|
+
| Function | Purpose |
|
|
166
|
+
| --- | --- |
|
|
167
|
+
| `call_tool(name, **kwargs)` | Invoke a host-registered tool by name |
|
|
168
|
+
| `http_get(url)` / `http_post(url, body=...)` | HTTP to allow-listed domains only |
|
|
169
|
+
| `read_file(path)` / `write_file(path, data)` | Capability-based file I/O (`/input`, `/output`) |
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
`HyperlightSandboxTool` forwards its constructor arguments to
|
|
174
|
+
[`hyperlight_sandbox.Sandbox`](https://github.com/hyperlight-dev/hyperlight-sandbox):
|
|
175
|
+
|
|
176
|
+
| Argument | Default | Description |
|
|
177
|
+
| --- | --- | --- |
|
|
178
|
+
| `backend` | `"wasm"` | `"wasm"` (Python/JS guest) or `"hyperlight-js"` |
|
|
179
|
+
| `module` | `"python_guest.path"` | Packaged guest module reference |
|
|
180
|
+
| `module_path` | `None` | Explicit path to a `.aot`/`.wasm` guest |
|
|
181
|
+
| `input_dir` / `output_dir` | `None` | Host directories mounted into the guest |
|
|
182
|
+
| `temp_output` | `False` | Use a temporary output directory |
|
|
183
|
+
| `heap_size` / `stack_size` | `None` | Guest memory limits (e.g. `"25Mi"`) |
|
|
184
|
+
| `host_tools` | `{}` | `{name: callable}` exposed to the guest |
|
|
185
|
+
| `allowed_domains` | `{}` | Network allow-list (see below) |
|
|
186
|
+
| `file_mounts` | `{}` | Host paths staged into the guest `/input` tree (see below) |
|
|
187
|
+
|
|
188
|
+
`allowed_domains` accepts a domain string, a `(target, methods)` tuple, an `AllowedDomain`, or a
|
|
189
|
+
sequence of any of these:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from langchain_hyperlight import AllowedDomain
|
|
193
|
+
|
|
194
|
+
tool = HyperlightSandboxTool(
|
|
195
|
+
allowed_domains=[
|
|
196
|
+
"api.github.com", # all methods
|
|
197
|
+
("internal.example.com", "GET"), # GET only
|
|
198
|
+
AllowedDomain("https://httpbin.org", ("GET", "POST")),
|
|
199
|
+
],
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`file_mounts` accepts a path string (same path on host and in the sandbox), a
|
|
204
|
+
`(host_path, mount_path)` tuple, a `FileMount`, or a sequence of any of these. Mounted files are
|
|
205
|
+
staged into a managed temporary `/input` tree and are readable in the guest via `read_file(...)`:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from langchain_hyperlight import FileMount
|
|
209
|
+
|
|
210
|
+
tool = HyperlightSandboxTool(
|
|
211
|
+
file_mounts=[
|
|
212
|
+
"/host/data", # -> /input/data
|
|
213
|
+
("/host/models", "models"), # -> /input/models
|
|
214
|
+
FileMount("/host/config", "config"), # -> /input/config
|
|
215
|
+
],
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The `create_hyperlight_tool()` factory is a thin convenience over the same constructor:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from langchain_hyperlight import create_hyperlight_tool
|
|
223
|
+
|
|
224
|
+
tool = create_hyperlight_tool(
|
|
225
|
+
host_tools={"add": lambda a=0, b=0: a + b},
|
|
226
|
+
allowed_domains=["api.github.com"],
|
|
227
|
+
)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
> **Note:** the tool always creates and owns its sandbox on a dedicated thread. Do not
|
|
231
|
+
> construct a `hyperlight_sandbox.Sandbox` yourself and try to share it across threads — the
|
|
232
|
+
> underlying `WasmSandbox` is `unsendable` and will panic if touched from a different thread
|
|
233
|
+
> than the one that created it. The tool manages this confinement for you.
|
|
234
|
+
|
|
235
|
+
## Running on Bluefin / Fedora Silverblue (immutable)
|
|
236
|
+
|
|
237
|
+
Bluefin is an immutable Fedora (Silverblue) image. The package itself installs normally into a
|
|
238
|
+
virtual environment, but the **KVM hypervisor** must be available on the host:
|
|
239
|
+
|
|
240
|
+
1. **Verify virtualization is enabled** in firmware (AMD-V / Intel VT-x):
|
|
241
|
+
|
|
242
|
+
```shell
|
|
243
|
+
grep -E 'vmx|svm' /proc/cpuinfo
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
2. **Ensure the KVM device exists** (the `kvm_amd`/`kvm_intel` module is loaded):
|
|
247
|
+
|
|
248
|
+
```shell
|
|
249
|
+
ls -l /dev/kvm
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
If it is missing, the module is not loaded. On Bluefin this is usually a firmware/BIOS
|
|
253
|
+
setting (enable SVM/VT-x) rather than a package issue, since the kernel ships KVM.
|
|
254
|
+
|
|
255
|
+
3. **Add your user to the `kvm` group** so you can open `/dev/kvm` without root:
|
|
256
|
+
|
|
257
|
+
```shell
|
|
258
|
+
sudo usermod -aG kvm $USER
|
|
259
|
+
# log out and back in, then verify:
|
|
260
|
+
groups
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
4. **Install the package in a venv** (never layer Python packages system-wide on an immutable
|
|
264
|
+
image — use `uv`, `pipx`, or a `distrobox`/`toolbox` container):
|
|
265
|
+
|
|
266
|
+
```shell
|
|
267
|
+
uv venv .venv
|
|
268
|
+
uv pip install --python .venv/bin/python langchain-hyperlight
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
For a fully isolated dev environment, `distrobox` is the idiomatic Bluefin approach:
|
|
272
|
+
|
|
273
|
+
```shell
|
|
274
|
+
distrobox create --name hyperlight-dev --image fedora:latest
|
|
275
|
+
distrobox enter hyperlight-dev
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Development
|
|
279
|
+
|
|
280
|
+
```shell
|
|
281
|
+
uv venv .venv
|
|
282
|
+
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
283
|
+
.venv/bin/pytest
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Tests that require a hypervisor are skipped automatically when `/dev/kvm` (or `/dev/mshv`) is
|
|
287
|
+
unavailable.
|
|
288
|
+
|
|
289
|
+
## References
|
|
290
|
+
|
|
291
|
+
- [Hyperlight project site](https://hyperlight.org/) — official docs and getting-started guide
|
|
292
|
+
- [hyperlight-dev/hyperlight](https://github.com/hyperlight-dev/hyperlight) — the VMM itself
|
|
293
|
+
- [hyperlight-dev/hyperlight-sandbox](https://github.com/hyperlight-dev/hyperlight-sandbox) — the
|
|
294
|
+
multi-backend sandbox framework this tool wraps
|
|
295
|
+
- [hyperlight-dev/hyperlight-wasm](https://github.com/hyperlight-dev/hyperlight-wasm) — the Wasm
|
|
296
|
+
component backend
|
|
297
|
+
- [Microsoft Agent Framework Hyperlight integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/hyperlight) —
|
|
298
|
+
the canonical `agent-framework-hyperlight` package this tool mirrors
|
|
299
|
+
- [Microsoft Learn: Hyperlight integration](https://learn.microsoft.com/en-us/agent-framework/integrations/hyperlight)
|
|
300
|
+
- [`hyperlight-sandbox` on PyPI](https://pypi.org/project/hyperlight-sandbox/)
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
Apache-2.0. Hyperlight is a [CNCF](https://cncf.io/) sandbox project.
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
> ⚠️ **VIBE-CODED — AI-GENERATED, NOT PRODUCTION-READY**
|
|
2
|
+
>
|
|
3
|
+
> This project was written by an AI ("vibe coded"). It is **experimental** and has had
|
|
4
|
+
> **no security review, fuzzing, or adversarial testing**. It has only been smoke-tested
|
|
5
|
+
> on **Linux (x86_64)**. **Use at your own risk** — do not rely on it for anything
|
|
6
|
+
> security-sensitive or mission-critical. See [Limitations](#limitations).
|
|
7
|
+
|
|
8
|
+
# langchain-hyperlight
|
|
9
|
+
|
|
10
|
+
A [LangChain](https://www.langchain.com/) tool that executes untrusted code inside a
|
|
11
|
+
[Microsoft Hyperlight](https://github.com/hyperlight-dev/hyperlight) **micro virtual machine**.
|
|
12
|
+
|
|
13
|
+
Hyperlight is a lightweight Virtual Machine Manager (VMM) designed to be embedded within
|
|
14
|
+
applications. It runs untrusted code in hardware-isolated micro VMs (KVM, MSHV, or Hyper-V)
|
|
15
|
+
with very low latency and minimal overhead. This package exposes that capability to LangChain
|
|
16
|
+
agents as a standard tool, so an LLM can safely run arbitrary code without touching the host.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Hardware isolation** — code runs in a micro VM, not on the host.
|
|
21
|
+
- **Host tool dispatch** — register host callables that guest code invokes by name with
|
|
22
|
+
schema-validated arguments (`call_tool(...)`).
|
|
23
|
+
- **Capability-based file access** — read-only `/input`, writable `/output`, strict path isolation.
|
|
24
|
+
- **Network allow-listing** — network is off by default; opt in per-domain and per-HTTP-verb.
|
|
25
|
+
- **Snapshot / restore** — capture and rewind sandbox state.
|
|
26
|
+
- **Lazy sandbox creation** — constructing the tool is cheap; the micro VM boots on first use.
|
|
27
|
+
|
|
28
|
+
## Limitations
|
|
29
|
+
|
|
30
|
+
This is an early-stage, AI-generated integration. Be aware of the following before adopting it.
|
|
31
|
+
|
|
32
|
+
### Platform
|
|
33
|
+
|
|
34
|
+
- **x86_64 only.** Hyperlight currently targets x86_64; there are no `aarch64` (ARM) wheels.
|
|
35
|
+
Raspberry Pi, Apple Silicon, and AWS Graviton are unsupported.
|
|
36
|
+
- **glibc 2.34+.** The Rust backend ships `manylinux_2_34_x86_64` wheels, so it needs a recent
|
|
37
|
+
glibc. Works on Ubuntu 22.04+, Debian 12+, Fedora 36+, RHEL 9+. Does **not** work on
|
|
38
|
+
Ubuntu 20.04, Debian 11, RHEL 8, or musl-based distros (Alpine, Void) without building the
|
|
39
|
+
Rust backend from source.
|
|
40
|
+
- **Python 3.10–3.14.**
|
|
41
|
+
- **A hypervisor is required at runtime:** KVM (`/dev/kvm`) or MSHV on Linux.
|
|
42
|
+
- **Tested on Linux only.** This package has only been tested on **Linux (x86_64)**. It is
|
|
43
|
+
**not tested on Windows or macOS** — use on those platforms at your own risk.
|
|
44
|
+
|
|
45
|
+
### Security model
|
|
46
|
+
|
|
47
|
+
- The micro VM isolates the *guest code* you run, but any **host tools you register via
|
|
48
|
+
`host_tools` run with full host privileges** inside the sandbox's `call_tool(...)`. Only
|
|
49
|
+
register callables you trust, and treat their inputs as untrusted.
|
|
50
|
+
- Network is off by default and gated by `allowed_domains`, but an allow-listed domain is
|
|
51
|
+
reachable by any code running in the sandbox.
|
|
52
|
+
- This package has **not** been security-reviewed. Do not treat it as a hardened sandbox
|
|
53
|
+
boundary without your own audit.
|
|
54
|
+
|
|
55
|
+
### Maturity
|
|
56
|
+
|
|
57
|
+
- **Alpha / vibe-coded.** No fuzzing, no adversarial testing, no cross-platform CI matrix.
|
|
58
|
+
- The thread-confinement worker (required because the `WasmSandbox` is `unsendable` in PyO3)
|
|
59
|
+
is correct for the tested paths but has not been stress-tested under heavy concurrency.
|
|
60
|
+
- `host_tools` accepts plain Python callables only — it does not yet wrap LangChain
|
|
61
|
+
`BaseTool` instances directly.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
> **Platform support:** this package is **tested on Linux (x86_64) only**. It is
|
|
66
|
+
> **not tested on Windows or macOS** — install and use on those platforms at your own risk.
|
|
67
|
+
|
|
68
|
+
```shell
|
|
69
|
+
pip install langchain-hyperlight
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This pulls in `langchain-core` and `hyperlight-sandbox[wasm,python_guest]`.
|
|
73
|
+
|
|
74
|
+
> **Prerequisite:** a working hypervisor is required at *runtime* (not at install time):
|
|
75
|
+
>
|
|
76
|
+
> - **Linux:** KVM (`/dev/kvm`) or MSHV (`/dev/mshv`)
|
|
77
|
+
|
|
78
|
+
## Quick start
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from langchain_hyperlight import HyperlightSandboxTool
|
|
82
|
+
|
|
83
|
+
tool = HyperlightSandboxTool(
|
|
84
|
+
host_tools={
|
|
85
|
+
"add": lambda a=0, b=0: a + b,
|
|
86
|
+
"greet": lambda name="world": f"Hello, {name}!",
|
|
87
|
+
},
|
|
88
|
+
allowed_domains={"https://httpbin.org": ["GET"]},
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
result = tool.invoke({
|
|
92
|
+
"code": """
|
|
93
|
+
total = call_tool('add', a=3, b=4)
|
|
94
|
+
greeting = call_tool('greet', name='James')
|
|
95
|
+
print(f"3 + 4 = {total}")
|
|
96
|
+
print(greeting)
|
|
97
|
+
""",
|
|
98
|
+
})
|
|
99
|
+
print(result)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Using it inside an agent
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from langchain_core.tools import create_agent # or your agent of choice
|
|
106
|
+
|
|
107
|
+
agent = create_agent(model, tools=[tool])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The tool is a standard `langchain_core.tools.BaseTool`, so it works with any LangChain agent
|
|
111
|
+
runtime (LangGraph, `create_agent`, `AgentExecutor`, etc.).
|
|
112
|
+
|
|
113
|
+
## Relationship to Microsoft's Agent Framework
|
|
114
|
+
|
|
115
|
+
Microsoft ships an official Hyperlight integration for *its own* Agent Framework:
|
|
116
|
+
[`agent-framework-hyperlight`](https://github.com/microsoft/agent-framework/tree/main/python/packages/hyperlight)
|
|
117
|
+
(`HyperlightExecuteCodeTool` / `HyperlightCodeActProvider`). This package is the **LangChain**
|
|
118
|
+
equivalent: it targets `langchain_core.tools.BaseTool` and mirrors the same concepts — the
|
|
119
|
+
`execute_code` tool name, `file_mounts`, `allowed_domains`, and host-tool dispatch via
|
|
120
|
+
`call_tool(...)` — so the mental model transfers directly.
|
|
121
|
+
|
|
122
|
+
### Thread safety
|
|
123
|
+
|
|
124
|
+
The Hyperlight `WasmSandbox` is `unsendable` in PyO3: it may only be accessed and dropped from
|
|
125
|
+
the OS thread that created it, or it panics. This tool routes every sandbox operation through a
|
|
126
|
+
dedicated single-threaded worker, so it is safe to call from arbitrary threads and event loops
|
|
127
|
+
(including LangChain's async `ainvoke`).
|
|
128
|
+
|
|
129
|
+
## Guest environment
|
|
130
|
+
|
|
131
|
+
By default the sandbox runs **Python**. Inside the guest, these built-ins are available:
|
|
132
|
+
|
|
133
|
+
| Function | Purpose |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `call_tool(name, **kwargs)` | Invoke a host-registered tool by name |
|
|
136
|
+
| `http_get(url)` / `http_post(url, body=...)` | HTTP to allow-listed domains only |
|
|
137
|
+
| `read_file(path)` / `write_file(path, data)` | Capability-based file I/O (`/input`, `/output`) |
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
`HyperlightSandboxTool` forwards its constructor arguments to
|
|
142
|
+
[`hyperlight_sandbox.Sandbox`](https://github.com/hyperlight-dev/hyperlight-sandbox):
|
|
143
|
+
|
|
144
|
+
| Argument | Default | Description |
|
|
145
|
+
| --- | --- | --- |
|
|
146
|
+
| `backend` | `"wasm"` | `"wasm"` (Python/JS guest) or `"hyperlight-js"` |
|
|
147
|
+
| `module` | `"python_guest.path"` | Packaged guest module reference |
|
|
148
|
+
| `module_path` | `None` | Explicit path to a `.aot`/`.wasm` guest |
|
|
149
|
+
| `input_dir` / `output_dir` | `None` | Host directories mounted into the guest |
|
|
150
|
+
| `temp_output` | `False` | Use a temporary output directory |
|
|
151
|
+
| `heap_size` / `stack_size` | `None` | Guest memory limits (e.g. `"25Mi"`) |
|
|
152
|
+
| `host_tools` | `{}` | `{name: callable}` exposed to the guest |
|
|
153
|
+
| `allowed_domains` | `{}` | Network allow-list (see below) |
|
|
154
|
+
| `file_mounts` | `{}` | Host paths staged into the guest `/input` tree (see below) |
|
|
155
|
+
|
|
156
|
+
`allowed_domains` accepts a domain string, a `(target, methods)` tuple, an `AllowedDomain`, or a
|
|
157
|
+
sequence of any of these:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from langchain_hyperlight import AllowedDomain
|
|
161
|
+
|
|
162
|
+
tool = HyperlightSandboxTool(
|
|
163
|
+
allowed_domains=[
|
|
164
|
+
"api.github.com", # all methods
|
|
165
|
+
("internal.example.com", "GET"), # GET only
|
|
166
|
+
AllowedDomain("https://httpbin.org", ("GET", "POST")),
|
|
167
|
+
],
|
|
168
|
+
)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`file_mounts` accepts a path string (same path on host and in the sandbox), a
|
|
172
|
+
`(host_path, mount_path)` tuple, a `FileMount`, or a sequence of any of these. Mounted files are
|
|
173
|
+
staged into a managed temporary `/input` tree and are readable in the guest via `read_file(...)`:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from langchain_hyperlight import FileMount
|
|
177
|
+
|
|
178
|
+
tool = HyperlightSandboxTool(
|
|
179
|
+
file_mounts=[
|
|
180
|
+
"/host/data", # -> /input/data
|
|
181
|
+
("/host/models", "models"), # -> /input/models
|
|
182
|
+
FileMount("/host/config", "config"), # -> /input/config
|
|
183
|
+
],
|
|
184
|
+
)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The `create_hyperlight_tool()` factory is a thin convenience over the same constructor:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from langchain_hyperlight import create_hyperlight_tool
|
|
191
|
+
|
|
192
|
+
tool = create_hyperlight_tool(
|
|
193
|
+
host_tools={"add": lambda a=0, b=0: a + b},
|
|
194
|
+
allowed_domains=["api.github.com"],
|
|
195
|
+
)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
> **Note:** the tool always creates and owns its sandbox on a dedicated thread. Do not
|
|
199
|
+
> construct a `hyperlight_sandbox.Sandbox` yourself and try to share it across threads — the
|
|
200
|
+
> underlying `WasmSandbox` is `unsendable` and will panic if touched from a different thread
|
|
201
|
+
> than the one that created it. The tool manages this confinement for you.
|
|
202
|
+
|
|
203
|
+
## Running on Bluefin / Fedora Silverblue (immutable)
|
|
204
|
+
|
|
205
|
+
Bluefin is an immutable Fedora (Silverblue) image. The package itself installs normally into a
|
|
206
|
+
virtual environment, but the **KVM hypervisor** must be available on the host:
|
|
207
|
+
|
|
208
|
+
1. **Verify virtualization is enabled** in firmware (AMD-V / Intel VT-x):
|
|
209
|
+
|
|
210
|
+
```shell
|
|
211
|
+
grep -E 'vmx|svm' /proc/cpuinfo
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
2. **Ensure the KVM device exists** (the `kvm_amd`/`kvm_intel` module is loaded):
|
|
215
|
+
|
|
216
|
+
```shell
|
|
217
|
+
ls -l /dev/kvm
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
If it is missing, the module is not loaded. On Bluefin this is usually a firmware/BIOS
|
|
221
|
+
setting (enable SVM/VT-x) rather than a package issue, since the kernel ships KVM.
|
|
222
|
+
|
|
223
|
+
3. **Add your user to the `kvm` group** so you can open `/dev/kvm` without root:
|
|
224
|
+
|
|
225
|
+
```shell
|
|
226
|
+
sudo usermod -aG kvm $USER
|
|
227
|
+
# log out and back in, then verify:
|
|
228
|
+
groups
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
4. **Install the package in a venv** (never layer Python packages system-wide on an immutable
|
|
232
|
+
image — use `uv`, `pipx`, or a `distrobox`/`toolbox` container):
|
|
233
|
+
|
|
234
|
+
```shell
|
|
235
|
+
uv venv .venv
|
|
236
|
+
uv pip install --python .venv/bin/python langchain-hyperlight
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
For a fully isolated dev environment, `distrobox` is the idiomatic Bluefin approach:
|
|
240
|
+
|
|
241
|
+
```shell
|
|
242
|
+
distrobox create --name hyperlight-dev --image fedora:latest
|
|
243
|
+
distrobox enter hyperlight-dev
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
```shell
|
|
249
|
+
uv venv .venv
|
|
250
|
+
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
251
|
+
.venv/bin/pytest
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Tests that require a hypervisor are skipped automatically when `/dev/kvm` (or `/dev/mshv`) is
|
|
255
|
+
unavailable.
|
|
256
|
+
|
|
257
|
+
## References
|
|
258
|
+
|
|
259
|
+
- [Hyperlight project site](https://hyperlight.org/) — official docs and getting-started guide
|
|
260
|
+
- [hyperlight-dev/hyperlight](https://github.com/hyperlight-dev/hyperlight) — the VMM itself
|
|
261
|
+
- [hyperlight-dev/hyperlight-sandbox](https://github.com/hyperlight-dev/hyperlight-sandbox) — the
|
|
262
|
+
multi-backend sandbox framework this tool wraps
|
|
263
|
+
- [hyperlight-dev/hyperlight-wasm](https://github.com/hyperlight-dev/hyperlight-wasm) — the Wasm
|
|
264
|
+
component backend
|
|
265
|
+
- [Microsoft Agent Framework Hyperlight integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/hyperlight) —
|
|
266
|
+
the canonical `agent-framework-hyperlight` package this tool mirrors
|
|
267
|
+
- [Microsoft Learn: Hyperlight integration](https://learn.microsoft.com/en-us/agent-framework/integrations/hyperlight)
|
|
268
|
+
- [`hyperlight-sandbox` on PyPI](https://pypi.org/project/hyperlight-sandbox/)
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
Apache-2.0. Hyperlight is a [CNCF](https://cncf.io/) sandbox project.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""End-to-end example: a LangChain agent using the Hyperlight sandbox tool.
|
|
2
|
+
|
|
3
|
+
Requires a working hypervisor (KVM / MSHV) on Linux. See the README for setup.
|
|
4
|
+
|
|
5
|
+
Run with:
|
|
6
|
+
uv run examples/basic_agent.py
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from langchain_hyperlight import HyperlightSandboxTool
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main() -> None:
|
|
15
|
+
"""Run the end-to-end demo: execution, tool dispatch, and snapshot/restore."""
|
|
16
|
+
# Register host capabilities that the guest may call by name.
|
|
17
|
+
tool = HyperlightSandboxTool(
|
|
18
|
+
host_tools={
|
|
19
|
+
"add": lambda a=0, b=0: a + b,
|
|
20
|
+
"multiply": lambda a=0, b=0: a * b,
|
|
21
|
+
"greet": lambda name="world": f"Hello, {name}!",
|
|
22
|
+
},
|
|
23
|
+
allowed_domains={"https://httpbin.org": ["GET"]},
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# 1. Basic execution
|
|
27
|
+
result = tool.invoke({"code": 'print("hello from a micro VM!")'})
|
|
28
|
+
print("--- basic execution ---")
|
|
29
|
+
print(result)
|
|
30
|
+
|
|
31
|
+
# 2. Host tool dispatch
|
|
32
|
+
result = tool.invoke(
|
|
33
|
+
{
|
|
34
|
+
"code": """
|
|
35
|
+
total = call_tool('add', a=3, b=4)
|
|
36
|
+
product = call_tool('multiply', a=6, b=7)
|
|
37
|
+
print(f"3 + 4 = {total}")
|
|
38
|
+
print(f"6 * 7 = {product}")
|
|
39
|
+
print(call_tool('greet', name='James'))
|
|
40
|
+
"""
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
print("\n--- tool dispatch ---")
|
|
44
|
+
print(result)
|
|
45
|
+
|
|
46
|
+
# 3. Snapshot / restore
|
|
47
|
+
tool.invoke({"code": "x = 42; print(f'x = {x}')"})
|
|
48
|
+
tool.snapshot()
|
|
49
|
+
tool.invoke({"code": "x = 99"})
|
|
50
|
+
tool.restore()
|
|
51
|
+
result = tool.invoke(
|
|
52
|
+
{
|
|
53
|
+
"code": """
|
|
54
|
+
try:
|
|
55
|
+
print(f"x = {x}")
|
|
56
|
+
except NameError:
|
|
57
|
+
print("x is not defined (state was rolled back)")
|
|
58
|
+
"""
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
print("\n--- snapshot / restore ---")
|
|
62
|
+
print(result)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
main()
|