An agent that can write a short program can often finish a task more reliably than an agent that must describe every intermediate operation in conversation. A program can filter records, compare files, calculate totals, run tests, and package an artifact. The improvement comes from giving the agent an executable interface to the work. It does not come from giving that program unrestricted access to the machine or to every connected service.
The central design question is therefore not whether an agent can execute code. It is which capabilities the generated code receives, how those capabilities are enforced, and what evidence survives after execution. A script that runs in a container with a production credential can still change production. A script that runs without credentials can still damage files mounted into its workspace. Isolation has to be described in terms of actual resources and authority.
Cloudflare's current Agents documentation distinguishes a Linux sandbox for shell tools and project files from Code Mode for programs that call developer-defined methods. It describes the sandbox as a container attached to an agent, running in its own virtual machine, with Internet access enabled explicitly. Those product facts do not imply that every arbitrary container has the same boundary. Cloudflare Agents Sandbox
The rest of this article is an original reference design for a bounded code-execution service. Its examples are hypothetical engineering exercises, not a claim that Apiary has deployed this architecture. The design applies whether the execution backend is a managed sandbox, a disposable virtual machine, or another environment whose guarantees have been independently checked.
Separate the work request from the execution authority
Consider a user who asks an agent to compare two exported product catalogs and produce a report of changed prices. The task needs read access to two inputs, temporary storage, a runtime, and permission to return a report. It does not inherently need access to the user's email, cloud administration account, source-control credentials, or unrelated files. Start by expressing that resource boundary before deciding which language the agent should use.
A task record can identify the two input objects, their hashes, the allowed output directory, a time budget, and a maximum output size. The agent receives those references and a documented execution tool. The service resolves the references into the sandbox; the generated program should not be able to substitute an arbitrary host path and ask the service to mount it. This prevents a convenient file-loading feature from becoming an unintended host-filesystem interface.
Keep the user's requested outcome distinct from any proposed plan. The agent may decide to use Python, JavaScript, or a command-line utility, but that choice should not expand the task's authority. If a package is missing, the system can use an approved image or a separately controlled dependency step. The program's preference for a tool is not, by itself, a reason to enable unrestricted network access.
Define completion in terms of the report, not the process exit alone. A process can exit successfully after reading the wrong input, generating an empty file, or silently skipping malformed rows. The task needs a receipt that identifies the inputs processed, the output produced, and the checks performed. That receipt gives the caller a basis for deciding whether the requested comparison actually happened.
Choose between shell execution and bounded method calls
Some tasks need a real operating-system toolchain. Compiling a project, running a test suite, converting media, or executing a scientific package may require a shell and a filesystem. Other tasks only need to compose a few application operations: fetch approved records, compute a result, and return a draft. Giving the second category a full shell can add risk and maintenance work without adding useful capability.
For bounded method calls, expose a small application API. A program might call readCatalog, compareRows, and saveDraft, with each method validating its arguments and enforcing the user's scope. The exact method names are illustrative. The important property is that the program cannot reach resources merely because the implementation process can reach them. Each method is a capability with its own contract.
For shell execution, choose a sandbox whose process, filesystem, network, and credential boundaries match the task. Document what survives between commands and what disappears when the environment is destroyed. A shell is powerful because it composes many tools; the same composability means a weak restriction on one command can be undermined by another available program. Enforcement must sit below the model's choice of command spelling.
Avoid presenting a list of approved command names as complete containment. A permitted interpreter can execute arbitrary source, and a permitted build tool can run project scripts. A command policy can still be useful for user experience and audit, but its limits should be understood. The underlying environment should remain safe when the permitted tool exercises its normal capabilities.
The decision can be revisited as the task evolves. Begin with the smallest interface that can perform the work. If the agent demonstrates a concrete need for a compiler or package, route that step into an appropriately configured sandbox. Do not make every task pay the complexity cost of the broadest execution environment simply because a few tasks need it.
Define the lifecycle before adding persistence
A useful execution service has explicit states: prepared, running, finished, failed, cancelled, and expired. These are operational states, not statements about whether the user's business goal was achieved. A finished process can still produce an invalid artifact. Keeping process state separate from task acceptance prevents a green execution badge from becoming an unsupported completion claim.
Preparation should verify input identity, establish the workspace, select the runtime image, and apply resource policy. Running should have an owner, a deadline, and a way to observe progress. Finishing should collect the output and validate its manifest. Cancellation should stop the active work and record whether any external operation had already happened. Expiration should remove or archive resources according to a documented retention policy.
Persistence is useful when a task spans several turns. The agent can install a permitted dependency once, keep an intermediate dataset, or resume a test investigation. But persistence also preserves mistakes, unexpected processes, and contaminated state. Decide which state deserves to survive: source files, generated artifacts, package caches, process memory, and credentials are different categories.
Do not assume a container's writable filesystem is a durable database. The environment may restart, be reclaimed, or be replaced during an upgrade. Store important task state and accepted artifacts in an explicitly durable service. Record which intermediate files can be regenerated. This makes recovery a planned operation rather than a hope that the same machine remains alive.
For a long catalog comparison, the durable state might contain input hashes, a completed-chunk list, and the latest checkpoint object. The sandbox can be rebuilt from those records. A checkpoint should describe completed work precisely enough to avoid repeating side effects, but it should not be treated as proof that an unfinished report is ready to deliver. Resume state and acceptance evidence serve different purposes.
Make filesystem access understandable
Use a workspace layout that makes ownership clear. Inputs can be mounted or copied into a read-only area, scratch files can live in a temporary area, and accepted outputs can be written beneath a designated export directory. The program should know which locations are writable. The service should enforce those distinctions independently of the prompt that describes them.
When accepting a requested output path, normalize it and verify the resolved target stays inside the allowed directory. Consider symbolic links and existing filesystem objects, not just string prefixes. A path such as reports/summary.txt is straightforward until reports is a link to another location. The validation must describe the actual object being accessed, not only the text supplied by the program.
Separate task workspaces even when they belong to the same user. A comparison job should not accidentally read another job's partially generated report. Shared caches can improve performance, but their contents should be treated as cache material with a defined trust and invalidation policy. A writable shared directory is a communication channel between jobs and should be evaluated as such.
Preserve original inputs for replay when policy permits it. If the program cleans malformed rows, retain the raw export and record the transformation. Otherwise, a later reviewer cannot distinguish an error in the original data from an error introduced during cleanup. The output manifest can refer to both the raw input hash and the normalized intermediate hash without exposing the raw data publicly.
For the catalog example, require the report to include counts of read, accepted, rejected, and compared rows. A malformed row should appear in a bounded error report rather than disappear silently. This is ordinary data-engineering discipline, but it becomes especially important when a conversational agent may otherwise summarize a partial result as a complete comparison.
Network access is a separate capability
A sandbox can isolate local files while still allowing outbound requests. That may be appropriate for fetching public documentation or downloading dependencies, but it creates another route for data to leave the task. Describe network policy independently from filesystem policy. “Runs in a sandbox” is not a complete answer to whether the program can contact an arbitrary destination.
Prefer a preparation phase that retrieves known dependencies before untrusted generated code runs. Pin the selected versions or image identity and record them in the execution receipt. If runtime network access is necessary, scope it to the destinations and operations the task requires. The enforcement mechanism should apply to subprocesses and alternative clients, not just to one helper function.
An allowlisted host is not automatically a safe recipient for arbitrary payloads. A legitimate service may expose endpoints that accept uploads or user-controlled content. Method restrictions can reduce some risks but do not eliminate all disclosure channels; even a read-looking request can place data in its URL. The application should control what information each operation is allowed to send.
Keep private-network reachability explicit. A sandbox running near internal services may be able to contact metadata endpoints, development databases, or administrative interfaces unless the environment prevents it. Test the intended boundary with harmless probes in a controlled environment. Do not infer it from the absence of a browser or from a claim that the program only needs public Internet access.
If a request is blocked, return a clear error with the destination category and policy reason, while avoiding credential disclosure. The agent should be able to choose an authorized alternative or report a genuine dependency. It should not receive instructions to evade the restriction by changing protocols, using another proxy, or hiding the same request inside an allowed command.
Keep credentials out of general-purpose execution
The safest credential is one the generated program never receives. When a task needs a narrow authenticated operation, a brokered method can perform that operation after validating the arguments and user scope. The program receives a result appropriate to its task, not a reusable token that can be repurposed for unrelated actions.
For example, saving a draft report into an approved project folder can be a brokered operation. The service can verify the destination, content size, and user identity. Giving the program a broad cloud-storage credential would allow many more operations than saving that one report. The narrower method also produces a clearer audit record because the action is expressed in application terms.
Some toolchains do require credentials inside execution. In that case, scope the credential to the task, limit its lifetime where the service supports that, and document how it is removed. Do not rely on redacting the terminal output as the primary protection. A credential can be used without ever being printed, and a program can transmit it through channels the log redactor does not inspect.
Account for inherited environment variables, mounted home directories, credential helpers, and configuration files. A clean-looking command line does not prove the process has no secrets. The environment construction step should enumerate the intended variables and mounts rather than inherit the host's entire working environment. This also improves reproducibility by removing hidden dependencies.
Credential handling needs a failure path. If execution crashes before cleanup, the service should still expire or revoke the task-scoped access through its normal lifecycle controls. The recovery record should identify whether cleanup completed. A successful report generation is not sufficient reason to ignore a leftover credential or an unexpectedly persistent execution session.
Outputs are data until they pass validation
Generated output can be wrong, oversized, malformed, or dangerous to display. A report might contain formulas that execute in a spreadsheet, HTML that runs script when opened, or a path that attempts to overwrite another artifact. The execution service should validate outputs according to their intended use rather than treating every file in the workspace as a safe deliverable.
For a plain JSON result, enforce a schema, size limit, and expected record count. For a document, verify the file type and render it in the appropriate viewer before claiming it is readable. For a source patch, inspect the diff and run the relevant checks. The output's acceptance criteria should be chosen before the agent optimizes its work around producing that output.
Use an export manifest that lists each accepted artifact, its media type, byte length, and hash. The manifest should not be supplied solely by the generated program; the service can calculate those fields from the collected files. The program may propose a title or explanation, but the system should independently establish what bytes were actually produced.
Avoid embedding raw command output into a public page without escaping. Logs can contain terminal control characters, markup, private paths, or secrets returned by a failing dependency. Store raw logs privately when needed for diagnosis, and produce a bounded, sanitized summary for the user. Preserve enough detail to explain failures without turning the receipt into another disclosure channel.
The catalog report should state whether prices were compared as strings, decimal values, or another explicit representation. It should explain how duplicate product identifiers and currency differences were handled. Those choices materially affect the result. A visually attractive table cannot compensate for an undefined comparison rule, and a successful process cannot certify an interpretation it never recorded.
Bound time, memory, and output independently
A timeout limits duration, but it does not necessarily limit memory growth, disk consumption, or the number of external operations attempted before the deadline. Give each resource its own budget. The correct values depend on the workload and execution platform; the general requirement is that a runaway task cannot consume resources without a defined stopping condition.
Track both the top-level process and its descendants. Stopping a shell while leaving a compiler, server, or background script alive can make a cancelled task continue consuming resources. Use the execution platform's supported process-lifecycle controls and verify their behavior with a harmless child process. A timeout message in a log is not evidence that the complete process tree stopped.
Output limits deserve similar care. Truncating the displayed log after execution does not prevent the process from generating enormous output internally. Bound collection where the platform permits it, and record whether output was truncated. If the missing portion could contain the reason a test failed, the receipt should point to the private retained log or state that the evidence is incomplete.
Limit repeated execution as well as individual runs. An agent can stay under a per-command timeout while retrying the same failing operation indefinitely. Give the task a total time or attempt budget, and distinguish a retry that changes a relevant condition from a blind repetition. A missing package, invalid input, or denied permission usually needs a different action, not another identical attempt.
Budget exhaustion should produce a useful partial receipt. Record completed steps, accepted artifacts, unresolved errors, and the next dependency. Do not rename an unfinished task as complete merely because the budget is used. A bounded system is valuable precisely because it can stop honestly and leave enough evidence for a later run to resume without repeating everything.
Model side effects as transactions with receipts
Many code tasks are reversible until they contact an external system. Computing a report in scratch storage is different from emailing it, changing a database record, or publishing it publicly. Keep those operations behind explicit application methods whose contracts describe the target, effect, and authorization. The generated program can prepare an action, but preparation should not accidentally perform it.
For a publication step, create a concrete draft with a content hash and destination. The final operation can verify that the authorized content and destination still match. If the draft changes materially, the old decision should not silently authorize the new version. This is a general transaction-design principle, not a requirement to interrupt the user for every low-impact step that was already clearly authorized.
Retries are particularly dangerous around external writes. A network timeout may occur after the destination accepted the operation. Repeating the request can create duplicate records or duplicate messages. Where supported, use an idempotency key tied to the logical operation. Otherwise, perform a readback or reconciliation step before retrying, and record when the outcome remains unknown.
For the catalog workflow, generating a report and uploading it are two distinct states. If the upload response is lost, the task should retain the local report hash and investigate whether the destination already contains it. It should not generate a different report and upload that under the same task identity. Stable operation identity makes recovery possible without guessing what happened.
Keep the receipt close to the artifact. A user should be able to see which input versions produced the report and which destination accepted it. The receipt should distinguish prepared, submitted, accepted, and verified states. These labels are useful only if they correspond to evidence. A program printing “uploaded successfully” is weaker than a destination identifier plus a successful readback.
Use structured execution results
A tool that returns only terminal text forces the model to infer status from prose. Prefer a structured result with process exit status, timestamps, timeout state, output truncation flags, and artifact references. The human-readable log remains useful, but it should not be the sole source of truth about whether the process finished or was terminated.
Separate standard output from standard error without assuming that one always means success and the other always means failure. Many tools print progress or warnings to standard error. The application should interpret the process status and task-specific result contract together. A warning can be material even when the exit code is zero, and a nonzero exit can still leave a useful partial artifact.
Include the command or script identity in the private receipt. For generated scripts, preserve the exact source or its hash according to retention policy. A later audit should not depend on reconstructing a program from the agent's conversational summary. If the source contains sensitive data, keep it private and expose only the necessary summary to the user.
Use explicit units for resource and output fields. Milliseconds, seconds, bytes, characters, and tokens are different measures. Ambiguous telemetry makes it difficult to compare runs or enforce budgets. A service that truncates at a character count should not label that limit as bytes, especially when outputs contain non-ASCII text.
The agent's final response can then be grounded in the structured receipt: two inputs processed, a stated number of rows compared, several malformed rows held for review, and a report saved at a verified destination. The response can still be concise. Good internal evidence reduces the need for vague caveats because the system knows exactly which parts succeeded.
Correct the protocol vocabulary
MCP means Model Context Protocol. It is not Machine Payments Protocol. Cloudflare's documentation separately lists MCP tools and MPP, the Machine Payments Protocol. Confusing those names changes the meaning of an architecture: a protocol for connecting model applications to tools and resources is not automatically a payment mechanism. Cloudflare MCP overview
This distinction matters when describing a sandbox interface. A service can expose an execution tool through MCP, but MCP itself does not establish the operating-system isolation of that execution. The server implementation and execution platform must provide the boundary. Likewise, a tool's name or schema does not prove that its backend enforces the declared path, network, or resource restrictions.
Treat protocol metadata as a description to validate. A tool advertised as read-only might still invoke a backend operation with side effects if implemented incorrectly. A client can use metadata to guide its behavior, but the server should enforce the operation's actual authorization. Security properties should remain true even when a caller supplies unexpected but syntactically valid arguments.
Version the tool contract. If an execution tool gains network access, new mounted resources, or a new publication method, that is a capability change that deserves review. A minor-looking schema update can materially expand what generated programs can do. Keep the previous contract and tests so callers and reviewers can understand the difference.
Avoid claiming that a protocol integration is currently available, in preview, or planned without checking the specific product documentation. Those labels change. For a durable article, explain the architectural boundary and link to current setup documentation for availability. That is more useful than embedding a speculative roadmap in a supposedly practical guide.
Test the boundary with harmless adversarial fixtures
A sandbox demonstration that prints a greeting proves very little. Build a test suite around the resources the service promises to protect. Use harmless fixture files and controlled endpoints so the tests can verify denied access without exposing real secrets or contacting unintended systems. The goal is to establish behavior, not to stage a dangerous exploit.
A filesystem fixture can include an allowed input, a forbidden sibling file, and a link pointing outside the workspace. The expected result is that the permitted input remains readable while the other accesses fail under the documented policy. The test should exercise the actual execution tool, including subprocesses, rather than only a path-validation helper in isolation.
A network fixture can distinguish an approved public endpoint, a disallowed endpoint, and a controlled private destination. Test the ordinary runtime clients available inside the environment. If only one helper is restricted while another program can reach the same destination, the boundary is weaker than the user-facing description. Record the platform version and policy configuration used for the test.
A process fixture can start a harmless child that writes a heartbeat to scratch storage. Cancel the parent task and verify that the heartbeat stops. A resource fixture can intentionally exceed a small test budget and confirm that the task ends with an accurate reason. These tests establish lifecycle behavior that is easy to overlook during a happy-path demonstration.
An output fixture can emit malformed JSON, an oversized file, and text containing markup. The service should reject or safely handle each according to its output policy. The final result should identify the failure accurately. A validation failure is a successful boundary test, even though the generated program did not produce an accepted artifact.
Recover without inventing success
Execution systems fail in several different places: before starting, during computation, while collecting output, and while delivering the final artifact. The recovery action depends on the failure point. A task that never started can usually be retried directly. A task that may have completed an external write needs reconciliation before repetition.
Persist a task identity before the execution begins. Record state transitions with timestamps and stable operation identifiers. If the controller restarts, it can inspect the existing execution rather than launching a second copy immediately. The exact mechanism depends on the backend, but the principle is to recover from observed state instead of assuming that a lost connection means nothing happened.
For long computations, checkpoints should be validated on resume. Confirm that the input hashes and runtime assumptions still match. A checkpoint from an earlier catalog version should not be reused for a newer export just because the filenames are the same. If compatibility cannot be established, restart the computation from preserved inputs and explain why the old checkpoint was not used.
If an artifact exists but its validation receipt is missing, validate the existing artifact before regenerating it. This avoids unnecessary work and preserves a clear lineage. If the artifact is incomplete, mark it as such and retain or remove it under the task's cleanup policy. Do not leave a partial file with the same name as a complete deliverable without an explicit state distinction.
Recovery should be visible in the final receipt when it affects confidence or reproducibility. The user does not need every retry detail, but they should know if the final report was reconstructed from a checkpoint, if some input rows were held, or if an external operation remains unverified. Honest recovery is part of a dependable execution interface.
Design the user experience around consequential choices
A useful interface shows what the agent is doing, why it needs a capability, and what result is ready to inspect. It should not overwhelm the user with every internal command. Routine computation inside an authorized workspace can proceed autonomously, while a meaningful expansion of access or an unapproved consequential action remains visible at the appropriate boundary.
When a decision is needed, make it concrete. Show the destination, the proposed artifact, and the effect of the action. Asking whether the agent may “continue” gives the user too little information. Asking whether a specific report may be published to a named destination makes the choice reviewable. Existing authorization should be respected rather than repeatedly requested without a changed circumstance.
Progress messages should describe outcomes and unresolved dependencies. “Compared the first input and found duplicate identifiers” is more useful than a stream of shell commands. If the agent needs a missing input or a rule for handling duplicates, ask early while continuing independent work that does not depend on the answer. The execution model should support that separation.
Keep private diagnostics separate from public deliverables. A report can include the comparison method and aggregate error counts without exposing local machine paths or internal credentials. The user may still need a detailed private receipt for debugging. Providing both artifacts deliberately is better than placing everything into one document and hoping the reader understands which parts are safe to share.
The final handoff should say what was produced, where it is, how it was checked, and what remains unresolved. If the task requested publication, include destination readback evidence. If the task requested only a draft, stop at the draft. The quality of the interface comes from matching the action to the user's actual intent, not from maximizing the number of operations performed.
A complete catalog-comparison walkthrough
The operator supplies two exports and asks for changed prices. The controller records their hashes, assigns a task identifier, and creates a workspace with read-only inputs and a writable report directory. The generated program parses both files, validates the expected columns, and records rejected rows. It compares products by the agreed identifier and keeps currency handling explicit.
The execution result reports a normal exit, bounded logs, and a proposed report file. The collector calculates the report hash and verifies the expected schema. A separate validator checks that every reported changed identifier exists in both accepted input sets and that the before-and-after values match the normalized inputs. New and removed products are reported separately rather than mislabeled as price changes.
The agent reads the validation result and produces a concise explanation. If duplicate identifiers remain ambiguous, the report separates them into a review section. It does not choose one duplicate arbitrarily to make the table look complete. The operator can inspect the report and provide a rule for those cases without rerunning the already verified comparisons.
If publication was authorized, the controller submits the exact accepted report to the specified destination through a bounded operation. It records the returned identifier and reads the destination back where supported. If delivery is uncertain, the task remains in a reconciliation state. The computation can be complete while delivery is still unresolved; the interface should make that distinction clear.
This workflow gains the benefits of executable code without treating code execution as unlimited authority. Each stage has a defined input, a bounded capability, and an observable result. That structure also makes the system easier to debug: a wrong comparison, a failed process, and a failed upload have different evidence and different repair paths.
Exercise: prove the same task can be replayed
Create two small synthetic catalogs with no personal or business-confidential data. Include an unchanged product, a changed price, a new product, a removed product, a duplicate identifier, and a malformed numeric field. Write down the expected classification of every row before running the agent. This gives the evaluator an answer that was not derived from the generated program's own output.
Run the comparison once, preserve the receipt, and then destroy the disposable execution environment through its normal lifecycle. Recreate the environment from the recorded runtime identity and input objects. Run the same script again. The accepted comparison data should match even if timestamps and execution identifiers differ. Compare the meaningful report fields rather than requiring irrelevant runtime metadata to be identical.
Next, change one input value while retaining the original filename. The input hash should change, and the old receipt should no longer describe the new run. A system that identifies inputs only by filename may incorrectly reuse an old artifact. This small exercise tests whether the provenance design protects against a realistic operator mistake rather than only against a dramatic security incident.
Finally, simulate a lost delivery response using a controlled destination. Let the destination accept the report, then make the caller see an uncertain result. The recovery procedure should discover the existing artifact or preserve the uncertainty; it should not blindly create another copy. Record which destination feature made reconciliation possible. If the destination has no suitable lookup or idempotency support, document that limitation before using it for consequential publication.
These exercises connect the sandbox boundary to the user's actual outcome. They test correct computation, repeatability, input identity, and delivery recovery together. Passing a shell-isolation check is useful, but the complete service is only dependable when it can also explain which report was produced from which inputs and whether that exact report reached its intended destination.
Sources and further reading
- Cloudflare Agents Sandbox
- Cloudflare Model Context Protocol overview
- OpenAI sandboxing documentation
- OpenAI agent approvals and security
AI-assisted editorial disclosure: this is a substantial rewrite of a local research draft, including correction of its MCP/MPP confusion and unsupported blanket isolation claims. Primary documentation was checked on October 30, 2026. The architecture, catalog example, test fixtures, and operational recommendations are original analysis rather than a claim of a deployed implementation.