self-hosted-agent-sandbox 0.3.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.
- self_hosted_agent_sandbox-0.3.0/.github/workflows/publish.yml +52 -0
- self_hosted_agent_sandbox-0.3.0/.gitignore +5 -0
- self_hosted_agent_sandbox-0.3.0/CHANGELOG.md +42 -0
- self_hosted_agent_sandbox-0.3.0/LICENSE +202 -0
- self_hosted_agent_sandbox-0.3.0/PKG-INFO +188 -0
- self_hosted_agent_sandbox-0.3.0/README.md +176 -0
- self_hosted_agent_sandbox-0.3.0/README.zh-CN.md +176 -0
- self_hosted_agent_sandbox-0.3.0/SECURITY.md +5 -0
- self_hosted_agent_sandbox-0.3.0/cluster/cache-gc-install.sh +25 -0
- self_hosted_agent_sandbox-0.3.0/cluster/cache-gc.sh +33 -0
- self_hosted_agent_sandbox-0.3.0/cluster/example-pool.yaml +31 -0
- self_hosted_agent_sandbox-0.3.0/cluster/gvisor-node.sh +28 -0
- self_hosted_agent_sandbox-0.3.0/cluster/make-kubeconfig.sh +23 -0
- self_hosted_agent_sandbox-0.3.0/cluster/node-install.sh +57 -0
- self_hosted_agent_sandbox-0.3.0/cluster/rbac.yaml +33 -0
- self_hosted_agent_sandbox-0.3.0/cluster/render-pool.py +73 -0
- self_hosted_agent_sandbox-0.3.0/cluster/runtimeclass.yaml +4 -0
- self_hosted_agent_sandbox-0.3.0/docs/omniroute.md +55 -0
- self_hosted_agent_sandbox-0.3.0/pyproject.toml +23 -0
- self_hosted_agent_sandbox-0.3.0/src/self_hosted_agent_sandbox/__init__.py +5 -0
- self_hosted_agent_sandbox-0.3.0/src/self_hosted_agent_sandbox/cli.py +74 -0
- self_hosted_agent_sandbox-0.3.0/src/self_hosted_agent_sandbox/core.py +1019 -0
- self_hosted_agent_sandbox-0.3.0/tests/core_unit.py +229 -0
- self_hosted_agent_sandbox-0.3.0/tests/e2e.sh +29 -0
- self_hosted_agent_sandbox-0.3.0/tests/route_fuzz.py +48 -0
- self_hosted_agent_sandbox-0.3.0/tests/route_table.py +175 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# Builds on every tag push; uploads to PyPI through a trusted publisher (no stored token).
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
16
|
+
with:
|
|
17
|
+
persist-credentials: false
|
|
18
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Check the tag matches the package version
|
|
22
|
+
run: |
|
|
23
|
+
v=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
24
|
+
test "v$v" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME != version $v"; exit 1; }
|
|
25
|
+
- name: Unit tests
|
|
26
|
+
run: python tests/core_unit.py && python tests/route_table.py
|
|
27
|
+
- name: Build
|
|
28
|
+
run: python -m pip install build==1.3.0 && python -m build
|
|
29
|
+
- name: Smoke test the wheel
|
|
30
|
+
run: |
|
|
31
|
+
python -m venv /tmp/v && /tmp/v/bin/pip install dist/*.whl
|
|
32
|
+
/tmp/v/bin/python -c "from self_hosted_agent_sandbox import Sandbox, __version__; print(__version__)"
|
|
33
|
+
/tmp/v/bin/sbx --help >/dev/null
|
|
34
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
|
|
39
|
+
publish:
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment:
|
|
43
|
+
name: pypi
|
|
44
|
+
url: https://pypi.org/p/self-hosted-agent-sandbox
|
|
45
|
+
permissions:
|
|
46
|
+
id-token: write
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
49
|
+
with:
|
|
50
|
+
name: dist
|
|
51
|
+
path: dist/
|
|
52
|
+
- uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
- **Published on PyPI**: `pip install self-hosted-agent-sandbox`. Releases are built and uploaded
|
|
6
|
+
by `.github/workflows/publish.yml` on a version tag, through a PyPI trusted publisher (no stored token).
|
|
7
|
+
- **Import name is now `self_hosted_agent_sandbox`** (was `agent_sandbox`), matching the
|
|
8
|
+
distribution name; another PyPI project already installs a top-level `agent_sandbox`.
|
|
9
|
+
Upgrading: `from self_hosted_agent_sandbox import Sandbox`. Behaviour, the `sbx` command,
|
|
10
|
+
the `AGENT_SANDBOX_*` variables and `agent_sandbox_*` config keys are unchanged.
|
|
11
|
+
|
|
12
|
+
## 0.2.0
|
|
13
|
+
|
|
14
|
+
- **Variables are an allowlist now.** Host variables reach the sandbox only when
|
|
15
|
+
`AGENT_SANDBOX_FORWARD_ENV` (adapters: `agent_sandbox_forward_env`) names them, by name or glob;
|
|
16
|
+
the default is empty. `AGENT_SANDBOX_ENV_SKIP` still withholds names the allowlist matches.
|
|
17
|
+
Upgrading: list the variables your builds need, e.g. `MY_BUILD_FLAG,NPM_CONFIG_REGISTRY`; never tokens or keys.
|
|
18
|
+
- A variable forwarded earlier in the session and then dropped (from the host or the allowlist)
|
|
19
|
+
is unset in the sandbox.
|
|
20
|
+
- Git config is an exact key allowlist: identity and everyday settings from `~/.gitconfig`; for
|
|
21
|
+
each repository's `.git/config` and `config.worktree` in the code dirs also format, branches, remote URLs and fetch
|
|
22
|
+
refspecs. No aliases, `core.sshCommand`, `core.askpass`, hooks paths, helpers, includes,
|
|
23
|
+
`http.*`, `url.*`, nor values with `user:pass@`. A reused sandbox's copy is always replaced.
|
|
24
|
+
- Tests: `core_unit.py` and `route_table.py` cover the allowlists with a host API key, credential
|
|
25
|
+
settings in key names and in values (aliases, `sshCommand`), a repository config with a token in
|
|
26
|
+
its remote URL, and unset of a dropped variable.
|
|
27
|
+
|
|
28
|
+
## 0.1.0 โ first release
|
|
29
|
+
|
|
30
|
+
- Core library `agent_sandbox.Sandbox`, extracted unchanged from the Hermes plugin
|
|
31
|
+
(hermes-sandbox-cluster 0.22.7-1): session-scoped claims with a renewed cluster-side expiry,
|
|
32
|
+
wholesale variable forwarding with a glob skip list, declared file sync, two-way code sync with a
|
|
33
|
+
`.git` allowlist, host-command routing confined to the code dirs.
|
|
34
|
+
- `sbx` CLI: `exec` (with `--keep`), `shell`, `release`.
|
|
35
|
+
- Cluster scripts moved here from the plugin repo; resource names are neutral (`agent-sandbox`
|
|
36
|
+
service account, `agent-4g` example pool).
|
|
37
|
+
- `Sandbox.release(session)` deletes kept claims without claiming one; `stream=` accepts any binary
|
|
38
|
+
file-like object.
|
|
39
|
+
- Known limit: the agent host must be Linux or macOS (process groups are used to stop timed-out
|
|
40
|
+
commands).
|
|
41
|
+
- Tests: offline routing table, bash-backed fuzzer and core unit checks; `tests/e2e.sh` for the CLI
|
|
42
|
+
on a real cluster.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: self-hosted-agent-sandbox
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Disposable gVisor sandboxes for AI coding agents on your own k3s nodes (kubernetes-sigs/agent-sandbox)
|
|
5
|
+
Project-URL: Homepage, https://github.com/xiaoyaner0201/self-hosted-agent-sandbox
|
|
6
|
+
Author: xiaoyaner0201
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: ai-agents,gvisor,k3s,kubernetes,sandbox,self-hosted
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
|
|
15
|
+
# ๐ง self-hosted-agent-sandbox
|
|
16
|
+
|
|
17
|
+
**Disposable gVisor sandboxes for AI coding agents, on k3s nodes you own: a shared warm pool, per-node package caches, and git credentials that never enter the sandbox.**
|
|
18
|
+
|
|
19
|
+
English ยท [็ฎไฝไธญๆ](README.zh-CN.md)
|
|
20
|
+
|
|
21
|
+
      
|
|
22
|
+
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
The agent keeps running where it runs today. Only **the commands it executes** go into a sandbox: one per session, claimed from a warm pool in about 0.2 s and deleted when the session ends. The task's code is synced both ways, so the host's working tree stays the git source of truth, and git network commands (`push`, `fetch`, `clone`, โฆ) run back on the host, where the credentials are.
|
|
28
|
+
|
|
29
|
+
Built on [k3s](https://k3s.io), [kubernetes-sigs/agent-sandbox](https://github.com/kubernetes-sigs/agent-sandbox) and [gVisor](https://gvisor.dev). It needs no KVM, so ordinary cloud VMs work.
|
|
30
|
+
|
|
31
|
+
## ๐งฉ What is in this repo
|
|
32
|
+
|
|
33
|
+
| Part | What it is |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `cluster/` | node install with a hard pod memory budget, gVisor, least-privilege RBAC and kubeconfig, per-tenant warm pools with per-node caches, cache GC |
|
|
36
|
+
| `src/self_hosted_agent_sandbox/` | the core library (`Sandbox`): claim / renew / release, environment and file sync, two-way code sync with a `.git` allowlist, host-command routing. Pure Python, no dependencies |
|
|
37
|
+
| `sbx` | a CLI over the core: `sbx exec`, `sbx shell`, `sbx release` |
|
|
38
|
+
|
|
39
|
+
## ๐ Adapters
|
|
40
|
+
|
|
41
|
+
| Agent / platform | How | Status |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| [Hermes Agent](https://github.com/NousResearch/hermes-agent) | terminal backend plugin: [**hermes-sandbox-cluster**](https://github.com/xiaoyaner0201/hermes-sandbox-cluster) | โ
in use; acceptance suites pass on Hermes 0.21.5 and 0.22.7 |
|
|
44
|
+
| Claude Code | built-in Bash/Read/Write/Edit replaced by MCP tools backed by the sandbox | ๐ ๏ธ planned |
|
|
45
|
+
| Codex CLI | native remote execution environment (`codex exec-server` in the sandbox), MCP as fallback | ๐ ๏ธ planned |
|
|
46
|
+
| Multica runtimes | through the agent adapters above; covers assigned issues, webhook autopilots (e.g. on `pull_request`) and PR wakeups | ๐ ๏ธ planned |
|
|
47
|
+
| [OmniRoute](https://github.com/cheaper-inference/OmniRoute) | `agent-sandbox` skill runtime: `execute_command` / `eval_code` claim a pod from the pool ([guide](docs/omniroute.md)) | ๐ upstream PR [#19130](https://github.com/cheaper-inference/OmniRoute/pull/19130) open, not merged; verified on a lab cluster |
|
|
48
|
+
|
|
49
|
+
Anything else can call the CLI or the library directly.
|
|
50
|
+
|
|
51
|
+
## ๐ Quick start
|
|
52
|
+
|
|
53
|
+
**1. Nodes** (on every Linux machine; the script fetches the official k3s installer, or uses `$SBX_DL/install-k3s.sh` when air-gapped)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# first node (becomes the k3s server)
|
|
57
|
+
SBX_NODE_IP=10.0.0.11 cluster/node-install.sh server
|
|
58
|
+
sudo cat /var/lib/rancher/k3s/server/node-token # copy this to the other nodes as ./node-token
|
|
59
|
+
|
|
60
|
+
# every other node
|
|
61
|
+
SBX_NODE_IP=10.0.0.12 cluster/node-install.sh agent https://10.0.0.11:6443 ./node-token
|
|
62
|
+
|
|
63
|
+
# every node
|
|
64
|
+
cluster/gvisor-node.sh
|
|
65
|
+
cluster/cache-gc-install.sh # optional: daily cache prune
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**2. Cluster** (once, on the first node, where `kubectl` is `k3s kubectl`)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
kubectl apply --server-side -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.9/sandbox-with-extensions.yaml
|
|
72
|
+
kubectl create namespace sbx
|
|
73
|
+
kubectl apply -f cluster/runtimeclass.yaml -f cluster/rbac.yaml
|
|
74
|
+
cluster/render-pool.py --tenant team-a --image ghcr.io/you/agent-host:latest \
|
|
75
|
+
--cache-mount /var/cache/pkg | kubectl apply -f -
|
|
76
|
+
cluster/make-kubeconfig.sh https://10.0.0.11:6443 > agent-sandbox.kubeconfig # copy to the agent host
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> [!TIP]
|
|
80
|
+
> Use the **same image** your agent host runs. Paths, toolchains and cache variables then match on both sides with no extra configuration.
|
|
81
|
+
|
|
82
|
+
**3. Agent host**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install self-hosted-agent-sandbox
|
|
86
|
+
export AGENT_SANDBOX_KUBECONFIG=$PWD/agent-sandbox.kubeconfig AGENT_SANDBOX_POOL=sbx-team-a
|
|
87
|
+
cd /workspaces/my-repo
|
|
88
|
+
sbx exec --session demo -- 'cat /proc/version; make test' # runs in a fresh gVisor sandbox
|
|
89
|
+
sbx exec --session demo -- git push origin HEAD # runs on this host, with its credentials
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`--keep` keeps the claim, so later `sbx exec --session demo --keep` calls share one sandbox until `sbx release --session demo`. `kubectl` must be on `PATH`, or set `AGENT_SANDBOX_KUBECTL`.
|
|
93
|
+
|
|
94
|
+
From Python:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from self_hosted_agent_sandbox import Sandbox
|
|
98
|
+
sb = Sandbox(cwd="/workspaces/my-repo", session="task-42")
|
|
99
|
+
print(sb.execute("pytest -q")["output"])
|
|
100
|
+
sb.close()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## ๐งญ What runs where
|
|
104
|
+
|
|
105
|
+
A command runs on the host only when it is listed host commands plus glue that cannot touch files: a leading `cd <dir> &&` (an existing directory reached without symlinks), `echo`/`printf`, and filters fed by a pipe (`grep`, `jq`, `head`, `tail`, `sort`, `uniq`, `cut`, `tr`, `wc`) with short options from a fixed list, plus `>/dev/null`. It must also stay inside the code dirs: the working directory, every `cd` and every local path in a git command (a clone target, a `file://` remote) must resolve inside them, and `$VAR`, `~` and unquoted globs are not expanded on the host. Anything with a substitution, subshell, heredoc, file redirection, another program, a variable prefix other than on/off switches such as `GIT_TRACE=1`, or a git option that runs a program (`--upload-pack`, `clone -u/-c/--template`, โฆ) stays in the sandbox.
|
|
106
|
+
|
|
107
|
+
> [!IMPORTANT]
|
|
108
|
+
> List **exact sub-commands**, never a whole CLI that can run user-defined code. `gh` aliases and extensions, for example, execute arbitrary programs, so list `gh pr create`, `gh pr view` rather than `gh`. Inside the sandbox a listed CLI is a stub that prints a hint and exits with code 97, so the agent learns to issue it separately.
|
|
109
|
+
|
|
110
|
+
## โ๏ธ Configuration
|
|
111
|
+
|
|
112
|
+
Environment variables (adapters may also take them from their own config, e.g. Hermes `config.yaml` as `agent_sandbox_*`):
|
|
113
|
+
|
|
114
|
+
| Variable | Default | Purpose |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `AGENT_SANDBOX_POOL` | `agent-4g` | warm pool to claim from |
|
|
117
|
+
| `AGENT_SANDBOX_NAMESPACE` | `sbx` | namespace of pool and claims |
|
|
118
|
+
| `AGENT_SANDBOX_KUBECONFIG` | `~/.kube/agent-sandbox.yaml` | least-privilege kubeconfig |
|
|
119
|
+
| `AGENT_SANDBOX_KUBECTL` | `kubectl` | kubectl binary |
|
|
120
|
+
| `AGENT_SANDBOX_SYNC_PATHS` | โ | config files/dirs pushed at the same path |
|
|
121
|
+
| `AGENT_SANDBOX_CODE_DIRS` | task dir | two-way synced code dirs |
|
|
122
|
+
| `AGENT_SANDBOX_HOST_COMMANDS` | git network ops, `git remote add/set-url` | command prefixes that run on the host, matched word by word (replaces the default list) |
|
|
123
|
+
| `AGENT_SANDBOX_FORWARD_ENV` | โ (nothing) | host variables to forward, by name or glob, e.g. `MY_BUILD_FLAG,NPM_CONFIG_REGISTRY`. Never list tokens or keys |
|
|
124
|
+
| `AGENT_SANDBOX_ENV_SKIP` | โ | names to withhold even when the allowlist matches them (globs like `*_TOKEN` work) |
|
|
125
|
+
| `AGENT_SANDBOX_TASK_FILES` | โ | files in a task root (`/workspaces/<ws>/<task>/`) pushed with the workdir, never pulled back |
|
|
126
|
+
| `AGENT_SANDBOX_IDLE_TTL` | `1800` | cluster-side expiry in seconds, renewed while active |
|
|
127
|
+
| `AGENT_SANDBOX_PERSISTENT` | `false` | reuse one claim per session label |
|
|
128
|
+
| `AGENT_SANDBOX_CLAIM_TIMEOUT` | `180` | seconds to wait for a sandbox before giving up |
|
|
129
|
+
|
|
130
|
+
## ๐ Network allowlist
|
|
131
|
+
|
|
132
|
+
Each arrow starts at the side that opens the connection.
|
|
133
|
+
|
|
134
|
+
**Agent host โ cluster** ยท the only path the library needs
|
|
135
|
+
|
|
136
|
+
| Port | To | Why |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| TCP 6443 | k3s server | Kubernetes API: claim and release sandboxes, and `kubectl exec`, which is tunnelled through the API server |
|
|
139
|
+
|
|
140
|
+
**Between cluster nodes**
|
|
141
|
+
|
|
142
|
+
| Port | Direction | Why |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| TCP 6443 | agents โ server | node registration and API |
|
|
145
|
+
| TCP 10250 | server โ every node | kubelet: exec and logs are proxied to the node running the pod |
|
|
146
|
+
| UDP 8472 | every node โ every node | flannel VXLAN pod network |
|
|
147
|
+
| TCP 2379โ2380 | server โ server | only for multi-server (embedded etcd HA) |
|
|
148
|
+
|
|
149
|
+
**Sandboxes โ outside:** open only what your builds need, such as your Maven/npm/PyPI mirrors, a Go proxy, and container registries for the nodes' image pulls.
|
|
150
|
+
|
|
151
|
+
> [!NOTE]
|
|
152
|
+
> No inbound rule is needed toward the agent host or toward any sandbox. Code sync and host commands are always started by the host. If the host can reach the cluster only through a proxy, add `proxy-url:` to the kubeconfig cluster entry; `kubectl exec` works through HTTP and SOCKS proxies.
|
|
153
|
+
|
|
154
|
+
## ๐ Security
|
|
155
|
+
|
|
156
|
+
- Sandboxes run under **gVisor**, as a non-root uid, with no service-account token and no service links.
|
|
157
|
+
- **Credentials stay on the host.** The sandbox runs and builds code; it gets no host variable you have not named in `AGENT_SANDBOX_FORWARD_ENV` (empty by default), and git network commands that need credentials run on the host.
|
|
158
|
+
- Git config reaches the sandbox through an exact key allowlist: from the host `~/.gitconfig` only identity and everyday settings (`user.name`, `user.email`, `init.defaultBranch`, `pull.rebase`, `push.default`, line-ending and file-mode options); from each repository's `.git/config` and `config.worktree` in the code dirs also what git needs there (format, branches, remote URLs and fetch refspecs). Aliases, `core.sshCommand`, `core.askpass`, hooks paths, credential helpers, includes, `http.*` and `url.*` never go in, and any value with `user:pass@` is dropped. On a reused sandbox the copy is replaced, never kept; if a file cannot be parsed the sandbox gets an empty one.
|
|
159
|
+
- Other files in the code dirs, in the sync paths and in any seed directories an adapter passes are sent as they are: keep tokens out of the working tree (no `.env` with real keys, no `.npmrc` with an auth token there).
|
|
160
|
+
- Pull-back into `.git` is an allowlist (objects, refs, logs, `HEAD`, `index`, `packed-refs`, โฆ). Config, hooks, gitfiles and everything else stay as the host has them, host-side git runs with `safe.bareRepository=explicit`, and writes through host symlinks that lead out of the code dirs are refused, so nothing built in the sandbox can plant a hook that a later host-side `git push` runs.
|
|
161
|
+
- Package caches are per tenant, so one tenant cannot poison another tenant's cache.
|
|
162
|
+
- The agent host's service account may only manage `sandboxclaims` and exec into pods in one namespace.
|
|
163
|
+
- Host commands run with the host process's own `PATH`; the router checks the command text, not which `git` binary that `PATH` resolves to, so keep the host `PATH` free of directories the agent can write.
|
|
164
|
+
|
|
165
|
+
> [!WARNING]
|
|
166
|
+
> No host variable is forwarded by default. Name only what builds need, e.g. `AGENT_SANDBOX_FORWARD_ENV="MY_BUILD_FLAG,NPM_CONFIG_REGISTRY"`, and never put tokens or API keys in the list: anything forwarded is readable by every command in the sandbox.
|
|
167
|
+
|
|
168
|
+
## ๐งช Tests
|
|
169
|
+
|
|
170
|
+
- `tests/route_table.py` (fixed cases) and `tests/route_fuzz.py` (random commands run through real bash, asserting nothing is written outside the code dir) check host-command routing offline: `python tests/route_table.py`.
|
|
171
|
+
- `tests/core_unit.py` checks error handling, streaming output, `release`, the variable allowlist (empty by default: a host API key does not reach the sandbox; dropped names are unset) and the git config allowlists (host and repository) offline with a fake `kubectl`.
|
|
172
|
+
- `tests/e2e.sh` drives the `sbx` CLI against a real cluster: gVisor, variable forwarding, pull-back of files and commits, host routing, claim reuse and release, exit codes.
|
|
173
|
+
- Adapter repos carry their own acceptance suites (for Hermes: 26 + 15 end-to-end checks).
|
|
174
|
+
|
|
175
|
+
## ๐ What it touches
|
|
176
|
+
|
|
177
|
+
- **Runs** `kubectl` (the binary you point it at) as a subprocess, with the kubeconfig you name, against that kubeconfig's API server only.
|
|
178
|
+
- **Sends into the sandbox:** the host variables named in `AGENT_SANDBOX_FORWARD_ENV` (none by default), the files in `AGENT_SANDBOX_SYNC_PATHS` and `AGENT_SANDBOX_TASK_FILES`, the code directories, any seed directories an adapter passes (e.g. a read-only skills copy) and the allowlisted part of the host git config.
|
|
179
|
+
- **Writes on the host:** only inside the code directories, with files the sandbox created, changed or deleted there (in `.git` only history and index; never through symlinks that leave the code dirs).
|
|
180
|
+
- **No other network calls,** no telemetry, no stored credentials, no background process of its own.
|
|
181
|
+
|
|
182
|
+
## ๐ฃ๏ธ Status
|
|
183
|
+
|
|
184
|
+
Early. The core was extracted from the Hermes plugin, where it went through nine rounds of independent release QA. Linux x86_64 nodes, k3s v1.37.
|
|
185
|
+
|
|
186
|
+
## ๐ License
|
|
187
|
+
|
|
188
|
+
[Apache-2.0](LICENSE)
|