Metadata-Version: 2.4
Name: hermes-sandbox-setup
Version: 0.0.1.dev0
Summary: Secure Docker sandbox container pool for Hermes AI agent — zero-latency sandbox execution
Author: Robin Schultka
License-Expression: MIT
Project-URL: Homepage, https://github.com/whiskybeer/hermes-sandbox-setup
Project-URL: Repository, https://github.com/whiskybeer/hermes-sandbox-setup
Project-URL: Documentation, https://github.com/whiskybeer/hermes-sandbox-setup#readme
Keywords: hermes,sandbox,docker,container-pool,ai-security,owasp
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Unix Shell
Classifier: Topic :: Security
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# Hermes Sandbox Setup

Secure Docker sandbox container pool for the Hermes AI agent.
Zero-latency sandbox execution with full OWASP Top 10 security coverage.

> **⚠️ PyPI Publication:** This package is ready for PyPI. Pending token setup.
> See [PyPI placeholder section](#pypi) below.

---

## What It Does

Hermes runs directly on the host with full `terminal` + `file` access. This means any prompt injection (from web scraping, file reads, or API responses) can execute `rm -rf /` on your production VM.

This repo provides **execution-layer caging** — a warm container pool that routes dangerous operations into isolated Docker containers at near-zero latency.

### Architecture

```
┌───────────────────────────────────────┐
│  Hermes Agent (host)                  │
│  - reads your project files           │
│  - thinks about the problem           │
│  - decides: "I need to run a command" │
├───────────────────────────────────────┤
│  Sandbox Pool (3 warm containers)     │
│  ┌───┐ ┌───┐ ┌───┐                   │
│  │ 1 │ │ 2 │ │ 3 │  → docker exec     │
│  └───┘ └───┘ └───┘    (~5ms-160ms)   │
│                                       │
│  If pool empty → cold start           │
│  docker run --rm    (~500-800ms)      │
└───────────────────────────────────────┘
```

---

## Quick Start

### 1. Prerequisites

```bash
# Docker installed and user in docker group
docker --version
groups | grep docker

# If not: sudo usermod -aG docker $USER && newgrp docker
```

### 2. One-Command Setup

```bash
bash ~/.hermes/scripts/sandbox-setup.sh --pool-size 3 --memory 128m
```

Or with all options:

```bash
bash ~/.hermes/scripts/sandbox-setup.sh \
  --pool-size 3          \   # warm containers (default: 3)
  --memory 128m          \   # max RAM per container (default: 128m)
  --memory-reserve 16m   \   # soft reservation (default: 16m)
  --cpus 1               \   # CPU cores per container (default: 1)
  --network none         \   # network mode (default: none — for K8s use "host")
  --read-only true       \   # rootfs read-only (default: true)
  --image debian:bookworm-slim \  # container image
  --no-systemd           \   # skip systemd service install
```

### 3. Run Commands in Sandbox

```bash
# Via warm pool (~5-160ms)
~/.hermes/scripts/sandbox-daemon.sh exec "kubectl get pods"
~/.hermes/scripts/sandbox-daemon.sh exec "curl https://untrusted-site.com"

# Via cold start (~500-800ms, auto fallback)
~/.hermes/scripts/sandbox-exec.sh "pip install some-package"

# Check pool status
~/.hermes/scripts/sandbox-daemon.sh status
```

### 4. Systemd Auto-Start

```bash
systemctl --user enable --now hermes-sandbox-pool
```

---

## Security Coverage

### OWASP Top 10 (Web) — 9/10 passed

| Category | Status | Implementation |
|----------|--------|----------------|
| A01 Access Control | ✅ | `os.path.realpath()` path allowlist, restricted Hermes profiles |
| A02 Crypto | ✅ | Only MD5 for cache keying (not security-critical) |
| A03 Injection | ✅ | 16KB command limit + container isolation |
| A04 Design | ✅ | Zero-Trust WAF documented, defense in depth |
| A05 Misconfig | ✅ | Resolved absolute paths in config, visudo validation |
| A06 Components | ✅ | Zero external deps, Debian stable |
| A07 Auth | ✅ | nginx deny-by-default, `listen [IP_ADDRESS]:80` |
| A08 Integrity | ✅ | Hardcoded binary, `.deb` from git, no `curl|bash` |
| A09 Logging | ✅ | Full audit trail with duration, RC, output lines |
| A10 SSRF | ✅ | `--network none` in pool, no HTTP proxies |

### OWASP Top 10 (LLM) — 1 critical finding fixed

**LLM06 — Excessive Agency:** Before the audit, Hermes had one profile with full host access. Now:

| Profile | Toolsets | Use Case |
|---------|----------|----------|
| `default` | Full access (terminal, file, all) | Trusted tasks, known prompts |
| `research` | `web, browser, vision, skills, search, memory, cronjob` | Web scraping, reading — **no RCE risk** |
| `sandbox` | All tools + verbose DEBUG logging | Debugging, untrusted terminal |

```
hermes --profile research   # Read-only session
hermes --profile sandbox    # Debug session with audit
```

See:
- [`SECURITY_AUDIT.md`](SECURITY_AUDIT.md) — Full OWASP Web Top 10 report
- [`SECURITY_AUDIT_LLM.md`](SECURITY_AUDIT_LLM.md) — Full OWASP LLM Top 10 report
- [`LLM06_FIX.md`](LLM06_FIX.md) — Restricted profiles documentation
- [`FINAL_REPORT.md`](FINAL_REPORT.md) — Post-audit summary with all metrics

---

## Performance

| Metric | Value |
|--------|-------|
| Cold `docker run --rm` | ~521ms |
| Warm pool exec (avg) | ~162-455ms |
| Pool idle RAM per container | ~380-432 KB |
| Pool idle RAM total (3 containers) | ~1.5 MB |
| Audit log entry size | ~100 bytes |
| Per-container memory limit | 128m (configurable) |
| Per-container CPU limit | 1 vCPU |

### Recovery from failures

| Failure | Behavior |
|---------|----------|
| All containers busy | ✅ Transparent cold-start fallback (~600ms) |
| Docker daemon restart | ✅ CID files vanish → cold-start kicks in |
| Container killed externally | ✅ Next call selects different container |
| Parallel execs on same CID | ✅ Atomic `mv` claim — no race condition |
| Command > 16KB | ✅ Rejected safely (RC=64) |

---

## Configuration

### sandbox.yaml

```yaml
pool:
  size: 3
  memory_limit: "128m"
  memory_reserve: "16m"
  cpus: "1"
  network: "none"
  read_only: true
  image: "debian:bookworm-slim"

waf_mode: "read-only"     # drops write/delete/exec at socket layer
allowed_file_paths:
  - "/home/hermes"
tool_overrides: {}
```

### Logging

```yaml
# In ~/.hermes/config.yaml (per-profile)
logging:
  level: DEBUG         # DEBUG | INFO | WARNING | ERROR
  backup_count: 3
  max_size_mb: 5
  file: sandbox-log.log  # Optional: separate log file per profile
```

Audit log location: `~/.hermes/logs/sandbox-audit.log`

```
2026-06-08T05:02:59Z POOL HIT=true rc=0 dur=123ms out=2L cid=89a93f85b752 cmd="echo test123 && cat /etc/hostname"
2026-06-08T05:07:08Z REJECTED 20000B cmd="AAAA..."
2026-06-08T05:10:38Z COLD DONE rc=0 dur=600ms out=1L
2026-06-08T05:25:21Z POOL-STOP containers=3
```

---

## Go Rewrite — Effort Estimate

The sandbox scripts are **not worth rewriting in Go.** Here's why:

### Current state: 7 KB of bash

- `sandbox-daemon.sh`: 6,107 bytes
- `sandbox-exec.sh`: 1,080 bytes

### What Go would (not) buy

| Aspect | Bash | Go | Delta |
|--------|------|----|-------|
| **Exec latency** | ~162ms | ~160ms | **0%** — Docker is the bottleneck |
| **Startup** | ~5ms | ~1ms | Negligible |
| **Binary size** | 7 KB | ~6 MB | **857x larger** |
| **Concurrency** | Background `&` + `mv` atomic claim | Native goroutines | Cleaner code, same result |
| **Signal handling** | `trap` (flaky with bg) | `os.Signal` | Better |
| **Error handling** | `$?` checks (easy to miss) | Native error returns | Better |
| **Cross-platform** | Linux only | Linux/macOS/Windows | Better |
| **Maintenance (you)** | Simple bash — any sysadmin can edit | Needs Go toolchain | **Worse** |
| **Distribution** | Copy one `.sh` file | Build for every arch | **Worse** |
| **Auditability** | Plain text, grep-friendly | Compiled binary — needs source | **Worse** |

### Effort estimate

| Level | Hours | What you get |
|-------|-------|-------------|
| Minimal (CLI wrapper) | 4-6h | Same functionality, 300 lines Go, 6 MB binary |
| Full (with Docker SDK, health checks, structured logging) | 12-16h | Proper signal handling, JSON logging, channel-based pool |
| **Complete** (replacing all bash + Makefile + systemd integration) | **20-30h** | A proper Go daemon with everything above |

### Verdict

**Not recommended for you.** The scripts work. They're 7 KB, trivially editable, and the bottleneck is Docker (not bash). A Go rewrite would make maintenance harder for you (Go toolchain, compilation, cross-compilation, CI) for zero user-facing improvement.

### Where Go IS worth it

If you want a rewrite, focus on **ToolRecall's MCP daemon** (the Python multiplexer). That's where concurrency, JSON-RPC routing, and long-running process management matter. The sandbox wrapper is fine as bash.

---

## Files

```
├── usr/local/bin/
│   ├── sandbox-daemon.sh    # Pool manager (start/stop/exec/status)
│   ├── sandbox-exec.sh      # Single cold exec (legacy)
│   └── hermes-docker-enable # Hardcoded docker group wrapper
├── etc/
│   ├── sandbox.yaml         # Pool configuration template
│   ├── sandbox-audit.log    # Execution audit trail
│   └── sudoers.d/
│       └── hermes-docker    # NOPASSWD rule for group enable
│   ├── systemd/system/
│   │   └── hermes-sandbox-pool.service
│   └── nginx/
│       └── nginx-toolrecall.conf  # MCP proxy (deny-by-default)
├── debian-package/          # .deb build source
│   └── DEBIAN/
│       ├── control
│       └── postinst
├── SECURITY_AUDIT.md        # OWASP Web Top 10
├── SECURITY_AUDIT_LLM.md    # OWASP LLM Top 10
├── LLM06_FIX.md             # Excessive Agency fix docs
├── FINAL_REPORT.md          # Full metrics and post-audit summary
├── Makefile
└── README.md
```

---

## PyPI

<a name="pypi"></a>

This package will be available on PyPI as:

```
pip install hermes-sandbox-setup
# or
uv pip install hermes-sandbox-setup
```

Then:

```bash
hermes-sandbox-setup --pool-size 3 --memory 128m
```

> **Status:** PyPI token pending. Package structure is ready at `/home/hermes/hermes-sandbox-setup/`.
> Run `git push` once the remote is configured.

---

## License

MIT
