Skip to content

Sandboxes

View Markdown

A Sandbox is an isolated, long-running container for agents and untrusted code. You drive it with commands and file operations; it stops when idle and starts again on the next command.

examples/python/sandbox/main.py
"""Create a Sandbox, run commands in it, move files and delete it.
Run it with `python examples/python/sandbox/main.py`.
"""
import nodus
def main() -> None:
sb = nodus.Sandbox.create(
cpu=1,
memory="2Gi",
idle_timeout="5m",
max_cost=1,
)
try:
p = sb.exec("python", "-c", "print(6 * 7)")
print("stdout:", p.stdout.read().strip(), "exit:", p.wait())
with sb.open("/workspace/notes.txt", "w") as f:
f.write("written from the SDK\n")
print(sb.exec("cat", "/workspace/notes.txt").stdout.read(), end="")
finally:
sb.terminate()
if __name__ == "__main__":
main()
sb = nodus.Sandbox.create(
name="agent-1", # optional; creating the same name again reconnects
image="nodus/agent-tools", # the default: Python 3.12, Node 22, git
cpu=2, memory="4Gi",
timeout="24h", idle_timeout="5m", on_idle="stop",
network=nodus.Egress.open(), # outbound access; the default is nodus.Egress.deny()
max_cost=5,
)

create returns as soon as the Sandbox is admitted; commands wait for it to start. Egress is denied unless you open it. nodus.Sandbox.from_name("agent-1") reconnects and starts it again if you stopped it.

p = sb.exec("python", "-c", "print(6 * 7)")
print(p.stdout.read()) # everything the process wrote to stdout
assert p.wait() == 0 # the exit code
p = sb.exec("pip install requests && python app.py") # one string runs under /bin/sh -c
for chunk in p.stdout: # stream output as it arrives
print(chunk, end="")
p = sb.exec("bash", pty=True)
p.write("ls\n"); p.resize(40, 120); p.signal("SIGINT")

Every command is recorded as a Process, and its output is kept, so a reader that reconnects continues where it stopped. p.stdin.write(...) and p.stdin.write_eof() feed input; p.cancel() stops the process.

with sb.open("/workspace/notes.txt", "w") as f:
f.write("hi")
print(sb.files.read("/workspace/notes.txt"))
print(sb.files.list("/workspace"))
sb.stop() # saves the filesystem; compute billing stops, the saved files count as storage
sb.start()
image = sb.snapshot_filesystem() # Beta: an Image of the current filesystem
sb.terminate() # deletes it