Flow execution sandbox — tiered roadmap #14
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
agentic_calling.md(in hero_router) and Story 1 (#11) both rely on spawningpython3to execute flow code. Today that subprocess runs unsandboxed — it has whatever filesystem, network, and capability access the hero_logic server process has. That's fine for hand-authored flows by trusted developers and obviously not fine once the router agent (peragentic_calling.md) can ask an LLM to generate a Python script and execute it on the spot.This issue tracks the sandbox roadmap. Threat model first; tiers second; non-goals last.
Threat model
Two consumers, two trust levels:
WorkflowVersion.python_sourceof the running Playagentic_calling.md)Threats we want to mitigate, in priority order:
~/.ssh, writes outside its workdir. Mitigation: filesystem namespace./bin/sh -c …to escalate. Mitigation: privilege drop, no SUID binaries on PATH,seccompallowlist (Linux).Tier 0 — Subprocess hardening (free, ship with #11)
No new dependencies. Land as part of the executor in #11.
PYTHONPATH,PATH=/usr/bin:/bin,HERO_FLOW_SPAN_SOCK,HERO_*flow inputs.~/.hero/var/plays/{play_sid}/work(created per play, deleted on Play GC).prlimit/setrlimit:RLIMIT_AS(e.g. 512 MiB),RLIMIT_CPU(e.g. 60s wall ≈ 120s cpu),RLIMIT_NOFILE(256),RLIMIT_NPROC(32).kill -TERM, thenkill -KILL5s later.Defends against (A) and the "obvious" parts of (D)/(E). Does nothing for network or filesystem isolation.
Tier 1 — FS + network sandbox (1–2 weeks, blocks router agent execution)
Per-platform sandbox shim invoked by the executor. Same Tier 0 hardening on top.
bwrap(bubblewrap). Bind-mount read-only:/usr,/lib*,/etc/resolv.conf(only if network allowed),~/.hero/var/router/python,~/.hero/var/flows/sdk, the flow.pyfile. Tmpfs/tmp. Bind-mount the per-play workdir read-write. No/proc(or a minimal one). UDS allowlist via bind-mount: only~/hero/var/sockets/*/rpc.sock+ the per-play span socket. Network:--unshare-netby default; allowlist override for flows that opt in.sandbox-execwith a generated profile.(version 1) (deny default) (allow process*) (allow file-read* (subpath …)) (allow file-write* (subpath …)) (allow network-outbound (path-literal "…/rpc.sock") …) (deny network-outbound). macOS sandbox-exec is undocumented/deprecated but still functional.Defends against (A)(C)(D)(E). RPC-level authz still needed for (B).
This is the prerequisite for shipping the router agent execution flow described in
agentic_calling.md— once the LLM is generating scripts, Tier 0 is no longer enough.Tier 2 — Per-play container (1–2 months, defer until needed)
Rootless
podmanper play. Strong boundary, ~1s cold start. A small Alpine + python3 + bundled SDK image, built once per hero_logic release. Bind-mount generated clients + workdir + sockets.Adds: easy CPU/mem cgroup limits, image immutability, tear-down via
podman rm. Tradeoff: cold-start cost, podman dependency, image build pipeline.Defer until Tier 1 measurably falls short — the router agent's threat model probably stays inside Tier 1 indefinitely.
Tier 3 — microVM (months, speculative)
Firecracker / cloud-hypervisor. Strongest isolation, ~200 ms start, big infra. Don't plan around this until concrete pressure exists (e.g. multi-tenant hero_router serving untrusted users).
Non-goals
Done when
executor.sandbox = "bwrap" | "sandbox-exec" | "none"config flag, withnoneonly valid in dev mode + warned at startup.hero_logic/docs/sandbox.mdand is linked fromagentic_calling.mdso the router-agent design and the executor design stay in sync.Refs
herolib_openrpc_authorizeTaking ownership. Plan:
executor.sandbox = ...config flag, defaulting tobwrapon Linux andsandbox-execon macOS,noneonly in dev mode with a startup warning.hero_logic/docs/sandbox.mdlands with Tier 0 as a permanent reference and gets cross-linked fromhero_router/docs/agentic_calling.md§8.Not blocking #11/#12/#13 on Tier 1 — those are human-authored flows where Tier 0 is enough. Tier 1 is the prerequisite for the router-agent execution path in
agentic_calling.md, not for hero_logic stories themselves.