Sandbox security, workspace, and harness primitives for AI coding agents
Project description
HarnessBox
Sandbox security, workspace, and harness primitives for AI coding agents.
HarnessBox gives you a single Sandbox class that works across cloud providers (E2B, Docker, Daytona, EC2), configures any agent harness (Claude Code, Codex, Gemini CLI, OpenCode), enforces security policies, and optionally clones a git repo into the workspace with one parameter.
from harnessbox import Sandbox, SecurityPolicy, GitWorkspace
sandbox = Sandbox(
client="e2b",
api_key="your-e2b-key",
security_policy=SecurityPolicy(deny_network=True),
harness="claude-code",
workspace=GitWorkspace(
remote="https://github.com/user/repo.git",
commit_on_exit=True,
),
files={"/workspace/CLAUDE.md": "You are a helpful coding assistant."},
)
await sandbox.setup()
async for line in sandbox.run_prompt("Fix the failing tests"):
print(line)
await sandbox.end() # commits + pushes changes back
Zero runtime dependencies. Stdlib only. Provider SDKs are optional extras.
Install
# From source (until PyPI name is finalized)
pip install -e packages/harnessbox
# With E2B provider
pip install -e "packages/harnessbox[e2b]"
What It Does
┌─────────────────────────────────────────────────────┐
│ YOUR APPLICATION │
│ │
│ from harnessbox import Sandbox, SecurityPolicy, │
│ GitWorkspace │
└──────────────────────┬────────────────────────────────┘
│
┌────────────▼────────────┐
│ HarnessBox │
│ │
│ SecurityPolicy │ ← deny rules, credential guards,
│ HarnessTypeConfig │ PreToolUse hooks
│ GitWorkspace │ ← clone repo, commit on exit,
│ Sandbox │ snapshots, diff, events
└────────────┬────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌─────────┐ ┌───────────┐ ┌──────────┐
│ E2B │ │ Docker │ │ Daytona │ ... any SandboxProvider
└─────────┘ └───────────┘ └──────────┘
Examples
Basic Sandbox (No Workspace)
from harnessbox import Sandbox, SecurityPolicy
sandbox = Sandbox(
client="e2b",
api_key="...",
harness="claude-code",
security_policy=SecurityPolicy(
denied_tools=["WebFetch", "WebSearch", "Agent"],
deny_network=True,
),
files={"/workspace/CLAUDE.md": "Analyze the code in /workspace."},
)
await sandbox.setup()
async for line in sandbox.run_prompt("What does this codebase do?"):
print(line)
await sandbox.kill()
Sandbox with Git Workspace
from harnessbox import Sandbox, GitWorkspace
sandbox = Sandbox(
client="e2b",
api_key="...",
harness="claude-code",
workspace=GitWorkspace(
remote="https://github.com/user/my-project.git",
branch="main",
commit_on_exit=True,
auth_token="ghp_...", # for private repos
),
)
await sandbox.setup()
# Repo cloned into /workspace. Agent has full git access.
async for line in sandbox.run_prompt("Add error handling to the API routes"):
print(line)
await sandbox.end()
# Changes committed and pushed to origin/main
Workspace Snapshots and Diff
sandbox = Sandbox(
client="e2b",
api_key="...",
workspace=GitWorkspace(remote="https://github.com/user/repo.git"),
)
await sandbox.setup()
# Checkpoint before a risky change
await sandbox.workspace.snapshot(sandbox.provider, "/workspace", "before-refactor")
async for line in sandbox.run_prompt("Refactor the auth module"):
print(line)
# See what changed
diff = await sandbox.workspace.diff(sandbox.provider, "/workspace")
print(diff)
# Undo if it went wrong
await sandbox.workspace.restore(sandbox.provider, "/workspace", "before-refactor")
await sandbox.kill()
Push Failure Recovery
sandbox = Sandbox(
client="e2b",
api_key="...",
workspace=GitWorkspace(
remote="https://github.com/user/repo.git",
commit_on_exit=True,
),
)
await sandbox.setup()
async for _ in sandbox.run_prompt("Make some changes"):
pass
await sandbox.end()
# If push failed, the committed files are still accessible
if sandbox.unpushed_files:
print("Push failed. Recovered files:")
for path, content in sandbox.unpushed_files.items():
print(f" {path}: {len(content)} bytes")
Workspace Events
def on_push_failure(error, branch):
send_slack_alert(f"Push to {branch} failed: {error}")
sandbox = Sandbox(
client="e2b",
api_key="...",
workspace=GitWorkspace(
remote="https://github.com/user/repo.git",
commit_on_exit=True,
on_clone_start=lambda **kw: print(f"Cloning {kw['remote']}..."),
on_clone_complete=lambda **kw: print(f"Clone {'OK' if kw['success'] else 'FAILED'}"),
on_push_failure=on_push_failure,
),
)
Setup Script
Run a shell command after files and workspace are injected, before the agent launches. Useful for installing dependencies, building assets, or configuring the environment.
sandbox = Sandbox(
client="e2b",
api_key="...",
harness="claude-code",
workspace=GitWorkspace(remote="https://github.com/user/repo.git"),
setup_script="cd /workspace && npm install && npm run build",
)
await sandbox.setup()
# 1. Sandbox created, files injected
# 2. Repo cloned
# 3. "npm install && npm run build" runs
# 4. Agent starts with deps installed and assets built
The script runs in the workspace root. If it exits non-zero, setup() raises RuntimeError with the stderr output and the sandbox does not transition to ACTIVE.
Custom Harness Type
from harnessbox import Sandbox, HarnessTypeConfig, register_harness_type
register_harness_type(HarnessTypeConfig(
name="my-agent",
config_dir=".myagent",
settings_file=None,
hooks_dir=None,
system_prompt_file="SYSTEM.md",
default_dirs=("/workspace",),
cli_command="myagent",
cli_oneshot_template="myagent run {prompt}",
cli_interactive_template="myagent",
))
sandbox = Sandbox(client="e2b", api_key="...", harness="my-agent")
Security
HarnessBox generates Claude Code settings.json deny rules and a PreToolUse hook guard that protect credentials inside sandboxes:
| Threat | Defense |
|---|---|
printenv / env / os.environ |
Bash deny rules + hook guard |
Read .env, .aws/credentials |
Read deny rules |
WebFetch exfiltration |
Tool deny rules |
| Agent spawning sub-agents | Agent deny rules |
/proc/self/environ |
Bash deny rules + hook guard |
| IMDS credential theft (169.254.169.254) | Hook guard regex |
| Git credential helper leak | git config credential.* deny + Read .git/config deny |
from harnessbox import SecurityPolicy
policy = SecurityPolicy(
denied_tools=["WebFetch", "WebSearch", "Agent"],
denied_bash_patterns=["rm -rf /"],
deny_network=True,
include_credential_guards=True, # on by default
)
Built-in Harness Types
| Harness | Config Dir | System Prompt | CLI |
|---|---|---|---|
claude-code |
.claude |
CLAUDE.md |
claude --dangerously-skip-permissions ... |
codex |
.codex |
AGENTS.md |
codex --model o4-mini -q {prompt} |
gemini-cli |
.gemini |
GEMINI.md |
gemini -p {prompt} |
opencode |
.opencode |
AGENTS.md |
opencode -p {prompt} |
Comparison
| HarnessBox | Cloudflare Artifacts | Turso AgentFS | Letta MemFS | |
|---|---|---|---|---|
| Focus | Harness + security + workspace | Managed git repos | SQLite filesystem | Git-tracked memory |
| Providers | E2B, Docker, Daytona, EC2 | Cloudflare only | Turso/libSQL | Letta platform |
| Security | Deny rules + hooks + credential guards | Token-scoped auth | N/A | N/A |
| Git | Clone any remote into sandbox | Managed git protocol | N/A | Local git tracking |
| Versioning | Full git (branch, diff, snapshot) | Full git (fork, clone) | SQL queries | Git commits |
| Lock-in | None | Cloudflare Workers | Turso | Letta API |
| Dependencies | Zero (stdlib only) | Cloudflare SDK | Turso SDK | Letta SDK |
API Reference
Sandbox
Sandbox(
client: SandboxProvider | str, # "e2b", "docker", or provider instance
*,
security_policy: SecurityPolicy | None = None,
harness: str = "claude-code",
env_vars: dict[str, str] | None = None,
dirs: list[str] | None = None,
files: dict[str, str] | None = None,
timeout: int = 300,
api_key: str | None = None,
template: str | None = None,
workspace: Workspace | None = None,
setup_script: str | None = None, # shell command to run before agent launch
)
Lifecycle: setup() → run_prompt() / start_interactive() → end() or kill()
GitWorkspace
GitWorkspace(
remote: str, # HTTPS git remote URL
*,
branch: str = "main",
commit_on_exit: bool = False, # auto-commit + push on end()
commit_message: str | None = None, # default: "harnessbox: auto-commit {timestamp}"
clone_depth: int | None = None, # None = full clone
auth_token: str | None = None, # HTTPS token (never stored as env var)
on_clone_start: Callable | None = None,
on_clone_complete: Callable | None = None,
on_commit: Callable | None = None,
on_push_success: Callable | None = None,
on_push_failure: Callable | None = None,
)
Methods (called via provider):
inject(provider, workspace_root)— clone repoextract(provider, workspace_root)— commit + push (ifcommit_on_exit)snapshot(provider, workspace_root, name)— create named checkpointrestore(provider, workspace_root, name)— revert to checkpointdiff(provider, workspace_root)— unified diff since clone or last snapshot
SecurityPolicy
SecurityPolicy(
denied_tools: list[str] = [],
denied_bash_patterns: list[str] = [],
deny_network: bool = False,
include_credential_guards: bool = True,
)
Project Structure
packages/harnessbox/
harnessbox/
__init__.py # public API
sandbox.py # Sandbox class
workspace.py # Workspace protocol, GitWorkspace, MountWorkspace
providers.py # SandboxProvider protocol
harness.py # HarnessTypeConfig registry
security.py # SecurityPolicy, deny rules
hooks.py # PreToolUse hook guard
lifecycle.py # SessionState machine
_setup.py # manifest builder
_providers/
e2b.py # E2B provider
docker.py # stub
daytona.py # stub
ec2.py # stub
tests/ # 211 tests
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file harnessbox-0.1.1.tar.gz.
File metadata
- Download URL: harnessbox-0.1.1.tar.gz
- Upload date:
- Size: 26.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d17df7fac7464972e2061097f8401639fa449a04eae834feebd7951bca691f7b
|
|
| MD5 |
a86d001afc014a34821bb0c61b30a8f7
|
|
| BLAKE2b-256 |
d59cbc92c970143ab80843ecbb0ae0de48098d32b3a476db477a26c1ec30c26b
|
Provenance
The following attestation bundles were made for harnessbox-0.1.1.tar.gz:
Publisher:
publish.yml on Nikhil-Kadapala/HarnessBox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
harnessbox-0.1.1.tar.gz -
Subject digest:
d17df7fac7464972e2061097f8401639fa449a04eae834feebd7951bca691f7b - Sigstore transparency entry: 1341414737
- Sigstore integration time:
-
Permalink:
Nikhil-Kadapala/HarnessBox@5ffa9e1c2999bb915504f579c2ad769ddbc6fa70 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Nikhil-Kadapala
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5ffa9e1c2999bb915504f579c2ad769ddbc6fa70 -
Trigger Event:
release
-
Statement type:
File details
Details for the file harnessbox-0.1.1-py3-none-any.whl.
File metadata
- Download URL: harnessbox-0.1.1-py3-none-any.whl
- Upload date:
- Size: 23.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83567831264379ab154c7ed8ba72d7171c7861bd8bd38bbd113a2dbee0e500ca
|
|
| MD5 |
2df77894e547fcee4815a2361d1e91d9
|
|
| BLAKE2b-256 |
7fb4b8e7e149d1fd664dd122157e19f902ada199f131f11b5da0dcf02689d916
|
Provenance
The following attestation bundles were made for harnessbox-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Nikhil-Kadapala/HarnessBox
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
harnessbox-0.1.1-py3-none-any.whl -
Subject digest:
83567831264379ab154c7ed8ba72d7171c7861bd8bd38bbd113a2dbee0e500ca - Sigstore transparency entry: 1341414780
- Sigstore integration time:
-
Permalink:
Nikhil-Kadapala/HarnessBox@5ffa9e1c2999bb915504f579c2ad769ddbc6fa70 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Nikhil-Kadapala
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5ffa9e1c2999bb915504f579c2ad769ddbc6fa70 -
Trigger Event:
release
-
Statement type: