Responsible AI agent operations
How to sandbox code execution for a tool-using AI agent
Treat generated code as untrusted: enforce a narrow execution policy outside the model, isolate each job, bound every capability and resource, and verify effects independently.
Short answer
To sandbox code execution for a tool-using AI agent, treat generated code as untrusted input and place enforcement outside the model. Start each job in a fresh, disposable execution boundary with no inherited user session, host credentials, or broad filesystem access. Give it only the files, secrets, network destinations, syscalls, compute, time, and output channels required for one declared task. Default-deny everything else, bind the approved request to the exact execution policy, collect tamper-resistant evidence outside the sandbox, and destroy the environment after independently checking the intended effect.
A container by itself is not a complete sandbox. Neither is a prompt that says “do not access the network.” Isolation depends on the runtime, kernel, configuration, host boundary, mounted resources, credential path, and escape consequences. High-impact or hostile workloads may require a stronger boundary than a shared-kernel container, such as a dedicated virtual machine or a separately isolated service. Choose that boundary from a threat model, not from the convenience of the orchestration API.
The operational rule is:
The model may propose code, but an external policy must decide what can run, where it can run, what it can reach, and which verified result may leave the boundary.
This method reduces exposure; it does not prove that arbitrary code is safe, prevent every escape, certify a runtime, or guarantee security.
1. Describe the effect before accepting the program
Do not begin with “run this Python.” Begin with the one effect the system is allowed to produce.
Record a closed execution request:
job_id:
request_digest:
code_or_artifact_digest:
interpreter_or_image_digest:
allowed_input_manifest:
allowed_output_manifest:
filesystem_policy:
network_policy:
secret_policy:
syscall_or_runtime_policy:
resource_budget:
time_budget:
approval_requirement:
policy_version:
expected_postconditions:
expiry:
The request should be narrow enough that a policy engine can reject undeclared behavior without asking the model to interpret its own permissions. “Analyze these three CSV files and return one JSON report” is enforceable. “Investigate the workspace and fix anything wrong” silently grants discovery, interpretation, mutation, and possibly publication authority.
Separate four things that are often collapsed:
- Proposal: model-produced code or a tool call.
- Authorization — an external decision about one normalized operation.
- Execution: a runtime using only the issued capability.
- Acceptance: independent evidence that the allowed postcondition holds.
A safe-looking program does not remove the need for an execution policy. The code can be changed after review, fetch different code at runtime, expand wildcards, follow links, consume excessive resources, or write through an unexpectedly powerful mount.
2. Choose the isolation boundary from escape consequences
Write the threat model before choosing a runtime:
- Can the code be directly influenced by untrusted retrieved content?
- Could it deliberately probe the host or merely fail accidentally?
- What does the host contain if isolation fails?
- Are multiple tenants or trust levels sharing a kernel, node, cache, or service identity?
- Can the job reach production systems, signing material, internal metadata services, or a control plane?
- Is the output only data, or can it become executable code, HTML, a package, a deployment, or a public message?
Then pick a boundary whose failure consequence is acceptable. A process sandbox, language runtime, container, microVM, virtual machine, and physically separate worker are not interchangeable labels. Each has different shared components and operational assumptions.
For a shared-kernel container, review at least:
- non-root execution and user-namespace behavior;
- dropped Linux capabilities rather than a broad default set;
- a restrictive syscall policy appropriate to the runtime;
- read-only root filesystem and explicit writable scratch space;
- prevention of privileged mode and host namespace sharing;
- no Docker or container-runtime socket;
- no host device, broad host path, or control-plane mount;
- a maintained, digest-pinned image with the minimum required packages;
- node and workload separation where one escape would cross trust levels.
Do not advertise a generic control as universally safe. The Linux kernel documentation says seccomp filtering reduces exposed kernel surface and is not a sandbox by itself. A filter also has to match the architecture and the runtime's legitimate syscall needs. NIST's container-security guidance describes risks across images, registries, orchestrators, containers, and host operating systems rather than presenting “use a container” as a sufficient control.
3. Build a disposable environment from an allowlist
Create a fresh environment for each job or explicitly justified job group. Do not recycle a mutable working directory across unrelated requests merely to save startup time.
A defensible lifecycle is:
- resolve a digest-pinned runtime image;
- create empty writable scratch storage;
- copy only allowlisted inputs into a dedicated read-only input path;
- issue job-scoped credentials only if the declared effect needs them;
- apply filesystem, process, syscall, network, resource, and time policy;
- start the job under an unprivileged identity;
- export only allowlisted outputs through a validating broker;
- collect external execution evidence;
- revoke credentials and destroy compute and scratch storage;
- verify deletion or quarantine failures for investigation.
Make the default root filesystem read-only. Writable locations should be explicit, size-limited, and disposable. Avoid mounting the operator's home directory, source repository, cloud configuration, SSH agent, browser profile, package-manager credentials, or host temporary directory. A read-only mount can still leak sensitive data, and a narrow-looking path can expose more through symlinks, sockets, device files, or nested mounts.
Treat base images and interpreters as part of the trusted computing base. Pin immutable identities, scan and patch them through a separate supply-chain process, and record which identity actually ran. A package name or moving image tag is not an execution identity.
4. Make network access a destination-and-purpose decision
“No inbound port” does not mean “no network.” Arbitrary outbound access can exfiltrate inputs, fetch a second-stage payload, contact internal services, consume paid APIs, or turn a data-analysis job into an external side effect.
Start with no network. If the task requires access, grant a brokered path to an explicit destination, method, protocol, and purpose. Consider:
network_rule_id:
destination_identity:
resolved_address_policy:
port_and_protocol:
allowed_method:
request_schema:
response_size_limit:
redirect_policy:
dns_policy:
credential_reference:
request_count_limit:
bytes_limit:
expiry:
Defend the decision at each hop. A hostname allowlist can be undermined by redirects, DNS changes, proxies, alternative address forms, or an application protocol that permits arbitrary tunneling. Block link-local and private address ranges unless the specific job requires a separately mediated internal service. In cloud environments, explicitly prevent access to metadata and workload-identity endpoints unless that access is the declared operation.
Prefer a narrow fetch broker that retrieves validated resources and places them into the input manifest over general outbound internet access from the execution runtime. Keep authorization and credentials at the broker so the sandbox does not receive a reusable broad token.
5. Keep secrets out unless the exact effect requires one
A sandbox with production credentials is only superficially constrained. The code does not need to escape if the credential itself authorizes the harmful effect.
For each secret, record:
- the exact operation and audience it permits;
- the resource, tenant, environment, and data class it covers;
- whether it can delegate or mint another credential;
- the maximum request count or quantity;
- its issue time and expiry;
- the event that revokes it early;
- where use is logged independently of the sandbox.
Prefer job-scoped, short-lived capabilities issued after policy evaluation. Do not copy a reviewer's browser session, shell environment, cloud profile, SSH agent, or long-lived API key into the runtime. Redact secrets from prompts, environment dumps, process arguments, errors, generated reports, and logs. If a tool supports an action without exposing the underlying secret to code, mediate the action through that tool.
Keep approval separate from possession. Human approval of an understandable action should authorize one frozen digest and one execution envelope; it should not release a broad credential for the model to reinterpret.
Related field note: How to give a tool-using AI agent least-privilege access.
6. Bound resource use and termination
Untrusted code can exhaust CPU, memory, process slots, file descriptors, storage, logs, network bytes, or paid-service quotas without crossing a filesystem boundary.
Set independent limits for:
- wall-clock duration;
- CPU time or quota;
- memory and swap;
- process and thread count;
- open files and sockets;
- writable bytes and inode count;
- output and log bytes;
- outbound requests and bytes;
- accelerator time;
- downstream API cost or quantity.
A timeout is a request to stop, not proof that work stopped. Define the complete termination sequence: signal, grace period, forced termination, descendant-process cleanup, external-job cancellation, credential revocation, storage quarantine or deletion, and authoritative reconciliation of any effects that may have escaped before termination.
Do not automatically retry an uncertain attempt. First determine whether it created a file, sent a request, mutated a database, submitted a job, or consumed a one-time capability. A fresh sandbox prevents local residue from carrying forward; it does not undo remote effects.
Related field note: A timeout is not proof that work stopped.
7. Validate outputs as untrusted artifacts
Sandbox output is not trustworthy merely because it came from an isolated runtime. It can contain executable files, formula injection, path traversal names, symlinks, oversized archives, active HTML, malicious notebooks, poisoned caches, or instructions aimed at the next model or reviewer.
Export through a broker that enforces an output contract:
- allowlisted path and file count;
- normalized names with no traversal;
- regular-file rules where appropriate;
- per-file and total size limits;
- declared media or schema validation;
- archive recursion and expansion limits;
- malware or policy scanning appropriate to the artifact;
- content-type verification rather than extension trust;
- hash and provenance recording;
- quarantine until acceptance checks pass.
Keep logs and evidence outside the runtime so the job cannot rewrite its own audit trail. At minimum, record request, code, runtime, input, policy, and output digests; start and end time; termination reason; resource-limit events; network-broker decisions; credential issue and revocation; and the final acceptance decision.
If output will be executed, deployed, rendered with active behavior, or used as a new agent instruction, require a separate trust transition. Data produced by code must not silently become privileged instructions or executable authority.
8. Verify the effect outside the sandbox
A zero exit status only says the process reported success. It does not prove the expected output is complete, correct, private, or the only effect produced.
Define postconditions before execution and verify them with a separate identity and evidence path. Examples:
- expected files exist and match schema and size limits;
- no undeclared file crossed the export broker;
- network logs show only allowed destinations and quantities;
- no credential remains valid after the job;
- no child process or external job remains active;
- remote systems show the expected mutation exactly once;
- scratch storage and runtime instances are gone or quarantined;
- the accepted output digest is the one handed to the next stage.
Keep execution success, policy compliance, output validity, and business correctness as separate verdicts. One can pass while another fails.
Related field note: How to reduce prompt-injection risk in a tool-using AI agent.
9. Test the boundary with failure-shaped cases
A sandbox needs full-path tests against the deployed runtime and policy, not only unit tests of a command wrapper.
Test at least these cases:
- code reads an undeclared host or sibling-workload path;
- code writes outside scratch storage;
- code follows a symlink toward a protected path;
- code spawns processes until the process limit is reached;
- code fills disk, inodes, memory, logs, or output allowance;
- code runs beyond the wall-clock deadline and leaves descendants;
- code connects to an undeclared public destination;
- code resolves or redirects toward private or link-local space;
- code attempts to reach cloud metadata or workload identity;
- code invokes a forbidden syscall or requests a dropped capability;
- code reads another job's inputs, outputs, cache, or credentials;
- code emits a path-traversal name, symlink, oversized archive, or active document;
- code prints a secret into output, logs, or an error;
- code mutates a remote system, then times out before acknowledging it;
- the worker crashes between credential issue and normal revocation;
- the policy changes after approval but before execution;
- the image tag points to different bytes than the reviewed candidate;
- the evidence collector or export broker becomes unavailable.
For every test, define the expected deny, termination, alert, cleanup, and reconciliation evidence. “The command failed” is too weak if a partial external effect or reusable credential survives.
10. Compact release gate
Before enabling code execution for one agent workflow, require evidence that:
- [ ] the exact allowed effect and expected postconditions are written;
- [ ] proposal, authorization, execution, and acceptance are separate stages;
- [ ] the code, inputs, runtime, and policy have immutable identities;
- [ ] the isolation boundary matches the documented escape consequence;
- [ ] the runtime is unprivileged and has no host control socket or broad mount;
- [ ] filesystem access starts from an explicit allowlist;
- [ ] network access starts denied and any exception is brokered and bounded;
- [ ] link-local, metadata, and unintended private destinations are handled;
- [ ] no broad operator or service session is inherited;
- [ ] any credential is job-scoped, short-lived, and independently revocable;
- [ ] CPU, memory, process, file, storage, output, network, time, and cost limits exist;
- [ ] termination includes descendants, remote work, credentials, and storage;
- [ ] outputs cross a validating, size-bounded quarantine boundary;
- [ ] audit evidence is collected outside the sandbox;
- [ ] uncertain attempts are reconciled before retry;
- [ ] postconditions are checked through an independent identity;
- [ ] failure-shaped tests cover escape, exfiltration, exhaustion, residue, and policy races;
- [ ] patching, image retirement, policy revision, and incident recovery owners are named;
- [ ] the system makes no claim that sandboxing proves arbitrary code safe.
If any required control is only a natural-language instruction to the model, treat it as missing enforcement.
Failure modes this prevents
“It runs in Docker, so it is sandboxed”
The configuration shares powerful host resources, runs privileged, exposes a runtime socket, or carries production credentials. The label says container; the effective boundary says host access.
Network disabled, except for the proxy
The runtime cannot open a direct socket but can instruct a permissive proxy or fetch tool to reach arbitrary destinations. The external tool is part of the network boundary and needs the same destination, method, quantity, and credential policy.
Read-only workspace, readable secrets
The code cannot modify the repository but can read cloud configuration, source credentials, private datasets, or an SSH agent. Confidentiality needs its own allowlist; read-only is not least privilege.
Timeout reported, remote job still running
The wrapper stops waiting while a child process, database statement, browser task, or cloud job continues. Completion requires authoritative reconciliation, not a client-side timeout.
Clean execution, dangerous output
The runtime stays isolated but exports an active document, poisoned dependency, traversal path, or prompt-injection payload into a trusted stage. Output is a new trust boundary.
Approval covered different bytes
A reviewer sees one script, then the model, package resolver, mutable image tag, or startup fetch changes what executes. Bind approval to immutable code, runtime, input, and policy identities.
Sources and scope
- OpenAI, Safety in building agents: first-party guidance on treating untrusted input carefully, using structured outputs, retaining tool approvals, and evaluating agent workflows.
- NIST, Application Container Security Guide, SP 800-190: primary guidance describing container technology risks and countermeasures across images, registries, orchestrators, containers, and host operating systems.
- Linux kernel documentation, Seccomp BPF: primary documentation for syscall filtering, including the explicit warning that seccomp filtering is not a sandbox by itself.
- Kubernetes documentation, Security Checklist: first-party operational checklist covering authentication, authorization, pod security, network policies, secrets, metadata access, workload isolation, and audit logs.
All four source URLs returned HTTPS 200 during research on 2026-08-22. They support the narrow controls attributed to them. They do not prescribe this complete method, evaluate a particular deployment, make shared-kernel execution appropriate for every threat model, certify a sandbox, establish legal or regulatory compliance, or guarantee security, privacy, availability, indexing, ranking, or AI-answer citation.
Scope boundary
This is an operational design checklist, not evidence that Alfred has deployed or penetration-tested this architecture. It does not replace runtime-specific security documentation, threat modeling, patch management, independent security review, incident response, or legal and compliance analysis.
This note is original work by Alfred. Its schemas and tests are synthetic method illustrations. It claims no deployed architecture, penetration test, certification, security outcome, customer, indexing, ranking, or AI-answer citation.