Problem
The default sandbox profile grants ~/.cache read-write to every harness session. ~/.cache is shared across all applications and can contain sensitive cached data:
~/.cache/gh/ — GitHub CLI OAuth token cache, API response cache
~/.cache/copilot/ — Copilot package cache
~/.cache/claude-cli-nodejs/ — Claude CLI staging
- Any other app's cached data
A compromised or buggy harness running inside the sandbox can read this data (cross-app leakage) or poison cached files for other apps.
Proposed approach
Replace the broad ~/.cache rw grant with a per-workdir persistent cache dir at ~/.cache/omac/<workdir-hash>/. Redirect specific tool caches into it via env vars:
func cacheEnvVars(cacheDir string) map[string]string {
return map[string]string{
"GOCACHE": filepath.Join(cacheDir, "go-build"),
"NPM_CONFIG_CACHE": filepath.Join(cacheDir, "npm"),
"PIP_CACHE_DIR": filepath.Join(cacheDir, "pip"),
"CARGO_HOME": filepath.Join(cacheDir, "cargo"),
}
}
XDG_CACHE_HOME is not set — it would redirect ALL XDG-respecting tools, breaking those that need existing cache data from other apps (e.g. gh hardcodes ~/.cache/gh/).
The cache dir is created by omac at launch, granted --allow (rw), and persists across sessions for warm rebuilds. Tools without env var overrides keep their default paths — users add specific cache dirs to their profile if needed.
Implementation sketch
createCacheDir(workdir) — creates ~/.cache/omac/<sha256(workdir)[:6]>/ (0o700)
- Grant
--allow <cacheDir> in start.go + serve.go
- Set
GOCACHE, NPM_CONFIG_CACHE, PIP_CACHE_DIR, CARGO_HOME in the sandbox env
- Also strip
~/.cache, ~/Library/Caches, ~/go, ~/.rustup, ~/.cargo, ~/.nvm, ~/.bun/bin from DefaultProfile (binary resolution now handled by shebang detection + resolveInnerBinaryPath)
Pros
- Security: No cross-app cache leakage. Only omac's own subtree is exposed, not other apps' cached data (OAuth tokens, API responses, etc.)
- Usability: Tools work out of the box — no config needed. Caches persist across sessions for fast rebuilds.
- Isolation: Per-workdir, so different projects don't cross-contaminate.
- Explicit: Only 4 well-known tool caches are redirected. Other tools keep their default paths — users add specific cache dirs to their profile when needed.
Cons
- Cold start: First session for a workdir has empty caches (no reuse from system caches). Subsequent sessions are warm.
- Incomplete coverage: Only Go, npm, pip, cargo are redirected. Tools without env var overrides (e.g.
gh, docker, custom tools) need manual profile entries.
- Disk usage: Per-workdir cache dirs accumulate. No automatic cleanup (could add GC later).
- Poisoning: A malicious tool could plant poisoned cache files. Blast radius is limited to omac sessions for that workdir — next session reads its own poisoned cache, not the system's. Much better than poisoning
~/.cache/gh/.
Alternatives considered
- TMPDIR redirect only: Redirect all caches to ephemeral
$TMPDIR. More secure (ephemeral) but cold rebuild every session. Bad usability.
- XDG_CACHE_HOME redirect: Set
XDG_CACHE_HOME to the per-workdir dir. Catches all XDG-respecting tools automatically, but breaks tools that hardcode ~/.cache/<tool>/ and need existing data. Too broad.
- Status quo: Keep
~/.cache rw in default profile. Simple but insecure (cross-app leakage).
Context
Explored during PR #24 (codex+copilot harness support). Reverted to keep that PR focused on harness support. The SandboxDirs field (per-harness config/state dirs) stays — it is harness-related. This issue covers the non-harness cache isolation.
Problem
The default sandbox profile grants
~/.cacheread-write to every harness session.~/.cacheis shared across all applications and can contain sensitive cached data:~/.cache/gh/— GitHub CLI OAuth token cache, API response cache~/.cache/copilot/— Copilot package cache~/.cache/claude-cli-nodejs/— Claude CLI stagingA compromised or buggy harness running inside the sandbox can read this data (cross-app leakage) or poison cached files for other apps.
Proposed approach
Replace the broad
~/.cacherw grant with a per-workdir persistent cache dir at~/.cache/omac/<workdir-hash>/. Redirect specific tool caches into it via env vars:XDG_CACHE_HOMEis not set — it would redirect ALL XDG-respecting tools, breaking those that need existing cache data from other apps (e.g.ghhardcodes~/.cache/gh/).The cache dir is created by omac at launch, granted
--allow(rw), and persists across sessions for warm rebuilds. Tools without env var overrides keep their default paths — users add specific cache dirs to their profile if needed.Implementation sketch
createCacheDir(workdir)— creates~/.cache/omac/<sha256(workdir)[:6]>/(0o700)--allow <cacheDir>in start.go + serve.goGOCACHE,NPM_CONFIG_CACHE,PIP_CACHE_DIR,CARGO_HOMEin the sandbox env~/.cache,~/Library/Caches,~/go,~/.rustup,~/.cargo,~/.nvm,~/.bun/binfromDefaultProfile(binary resolution now handled by shebang detection +resolveInnerBinaryPath)Pros
Cons
gh,docker, custom tools) need manual profile entries.~/.cache/gh/.Alternatives considered
$TMPDIR. More secure (ephemeral) but cold rebuild every session. Bad usability.XDG_CACHE_HOMEto the per-workdir dir. Catches all XDG-respecting tools automatically, but breaks tools that hardcode~/.cache/<tool>/and need existing data. Too broad.~/.cacherw in default profile. Simple but insecure (cross-app leakage).Context
Explored during PR #24 (codex+copilot harness support). Reverted to keep that PR focused on harness support. The
SandboxDirsfield (per-harness config/state dirs) stays — it is harness-related. This issue covers the non-harness cache isolation.