LocalRuntime runs agent Python in your process. Same permissions as
your interpreter: filesystem, network, env vars, subprocesses. Use
it only for code you'd run yourself.
For untrusted agents, or agents you haven't audited yet, use an isolated runtime:
DockerRuntime— a fresh container per session.ModalRuntime— a remote Modal container.E2BRuntime— a remote E2B sandbox.DaytonaRuntime— a remote Daytona sandbox.- Custom
Runtime— SSH,kubectl exec, Firecracker, gVisor, anything.
DockerRuntime(
image="rlmflow:local",
network="none", # no outbound traffic
cpus=1.0, # CPU quota
memory="512m", # OOM cap
user="1000:1000", # non-root
extra_args=[
"--read-only", # read-only rootfs
"--security-opt", "no-new-privileges",
],
mounts={"./workspace": "/workspace"},
)Mount only what the agent needs. A hostile agent inside the container can still fill its writable volumes, burn CPU up to the quota, and call any tool you injected.
Independent of the runtime:
max_depth— recursion limit.max_iters— LLM calls per agent.max_budget— total tokens across the subtree.max_output_length— truncate oversized stdout.max_concurrency— opt into threaded parallel children when set.
runtime.register_tool(fn) and runtime.register_tools([...]) expose
callables to the agent REPL. Tools marked @tool(proxy=True) execute on the
host when called from a remote sandbox; local tools are shipped into the sandbox
when possible. Working-directory-aware tools run relative to
runtime.working_directory.
Tools you register are part of the trust boundary. The container can be sealed off, but any injected tool runs on the host with host privileges. Keep that surface small and validate arguments.
Override Flow.run_exec(agent, repl, code) to gate, classify, or rewrite code
before it touches the runtime. Return the same (suspended, payload) shape as
the backend to short-circuit execution with a rejection string:
import rflow
class ReviewingFlow(rflow.Flow):
def run_exec(self, agent, repl, code: str):
if "rm -rf" in code and input(f"run? {code}\n> ") != "y":
return False, "rejected by reviewer"
return super().run_exec(agent, repl, code)Wrap the runtime or backend if you want approval at the transport layer.
Subclass Runtime.open(...) to return a backend that gates start(...) and
resume(...) before delegating to the underlying backend.