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.
Files changed (26) hide show
  1. self_hosted_agent_sandbox-0.3.0/.github/workflows/publish.yml +52 -0
  2. self_hosted_agent_sandbox-0.3.0/.gitignore +5 -0
  3. self_hosted_agent_sandbox-0.3.0/CHANGELOG.md +42 -0
  4. self_hosted_agent_sandbox-0.3.0/LICENSE +202 -0
  5. self_hosted_agent_sandbox-0.3.0/PKG-INFO +188 -0
  6. self_hosted_agent_sandbox-0.3.0/README.md +176 -0
  7. self_hosted_agent_sandbox-0.3.0/README.zh-CN.md +176 -0
  8. self_hosted_agent_sandbox-0.3.0/SECURITY.md +5 -0
  9. self_hosted_agent_sandbox-0.3.0/cluster/cache-gc-install.sh +25 -0
  10. self_hosted_agent_sandbox-0.3.0/cluster/cache-gc.sh +33 -0
  11. self_hosted_agent_sandbox-0.3.0/cluster/example-pool.yaml +31 -0
  12. self_hosted_agent_sandbox-0.3.0/cluster/gvisor-node.sh +28 -0
  13. self_hosted_agent_sandbox-0.3.0/cluster/make-kubeconfig.sh +23 -0
  14. self_hosted_agent_sandbox-0.3.0/cluster/node-install.sh +57 -0
  15. self_hosted_agent_sandbox-0.3.0/cluster/rbac.yaml +33 -0
  16. self_hosted_agent_sandbox-0.3.0/cluster/render-pool.py +73 -0
  17. self_hosted_agent_sandbox-0.3.0/cluster/runtimeclass.yaml +4 -0
  18. self_hosted_agent_sandbox-0.3.0/docs/omniroute.md +55 -0
  19. self_hosted_agent_sandbox-0.3.0/pyproject.toml +23 -0
  20. self_hosted_agent_sandbox-0.3.0/src/self_hosted_agent_sandbox/__init__.py +5 -0
  21. self_hosted_agent_sandbox-0.3.0/src/self_hosted_agent_sandbox/cli.py +74 -0
  22. self_hosted_agent_sandbox-0.3.0/src/self_hosted_agent_sandbox/core.py +1019 -0
  23. self_hosted_agent_sandbox-0.3.0/tests/core_unit.py +229 -0
  24. self_hosted_agent_sandbox-0.3.0/tests/e2e.sh +29 -0
  25. self_hosted_agent_sandbox-0.3.0/tests/route_fuzz.py +48 -0
  26. 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,5 @@
1
+ __pycache__/
2
+ *.pyc
3
+ dist/
4
+ build/
5
+ *.egg-info/
@@ -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
+ ![License](https://img.shields.io/badge/license-Apache--2.0-blue) ![k3s](https://img.shields.io/badge/k3s-v1.37-FFC61C) ![gVisor](https://img.shields.io/badge/isolation-gVisor-4285F4) ![agent-sandbox](https://img.shields.io/badge/kubernetes--sigs%2Fagent--sandbox-v1.0.9-326CE5) ![Self-hosted](https://img.shields.io/badge/self--hosted-multi--node-2E8B57) ![Python](https://img.shields.io/badge/python-3.10%2B%20ยท%20no%20deps-3776AB) ![Status](https://img.shields.io/badge/status-early-orange)
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)