- Published on
AI Agent Sandbox Permissions: A Deno Sandbox Checklist
- Authors

- Name
- Mehdi Akiki
Reference · Through the layers
An agent needs to read a CSV and calculate a total. You put its code in a separate VM. Good. Now ask what that VM can reach.
Can it call the internet? Is there a real API key in its environment? Did you mount a directory containing files from another job? These questions matter even if the code never escapes the VM.
For this example, assume the agent handles a private customer file. It needs that file and a small output directory. It does not need your source repository, cloud credentials, or general internet access. That gives us a specific policy to check.
Read the defaults before setting the options
The word “sandbox” doesn't tell you the network policy. Deno Sandbox's security documentation says outbound access is unrestricted when allowNet is omitted. Supplying the option restricts destinations to the listed hosts.
That is easy to miss. A separate VM can still have a wide-open network path.
Here are four configurations to compare. The table describes the documented policy, not results collected from a running deployment:
allowNet value | Request to reports.example:443 | Request to reports.example:8443 | Request to another host |
|---|---|---|---|
| Option omitted | Allowed by the network policy | Allowed by the network policy | Allowed by the network policy |
[] | Blocked | Blocked | Blocked |
["reports.example"] | Allowed | Allowed | Blocked |
["reports.example:443"] | Allowed | Blocked | Blocked |
The example host is illustrative. An allowed operation can still fail because the service is unavailable, a certificate is wrong, or its own authorization rejects the request. “Allowed” here only describes this network rule.
For an offline calculation, the SDK configuration can make the intended restriction explicit:
import { Sandbox } from "@deno/sandbox";
await using sandbox = await Sandbox.create({
allowNet: [],
});
An empty allowNet list blocks outbound requests. File uploads, resource limits, and application permissions need their own settings. Use the exact SDK version your project pins and check this configuration in the environment you will actually run. The Deno hosting migration checklist also separates the runtime support period from the service migration.
For a real check, use a harmless endpoint you control on each relevant port, and a second controlled host that must be blocked. Use a fresh sandbox for each configuration, save the SDK version and requested rule, then record whether the call reached the endpoint. A failed HTTP response alone doesn't prove the sandbox denied the connection.
Also check whether package installation needs network access. Preparing dependencies before a customer job starts can avoid granting every job a package-registry route. Keep the dependency image and its update process in the audit; preinstallation moves that work to another place.
Use dummy secrets for the first check
Never test secret handling by asking the agent to print a production key. Give the harness a harmless canary value and check whether untrusted code can read it.
There are two separate questions. Can the program obtain the credential? Can it use an allowed service to do something it should not do?
Deno Sandbox supports secret substitution on requests to approved hosts. That can keep the actual key out of the process. It doesn't automatically restrict every action available through the external API. An allowed host may accept expensive requests or store arbitrary data.
Put limits where the real action happens. For a document conversion service, a trusted handler can accept one conversion operation, a bounded input, and the current tenant's job ID. It shouldn't forward an arbitrary URL, method, and headers just because the agent asked.
Keep the provider credential on that trusted side. The program receives the ability to request one operation, rather than a general key it can reuse elsewhere.
Write down a few things that must fail
I would put these checks in the job's CI verification, using dummy data:
| Attempt | Expected result |
|---|---|
| Read the input file assigned to this job | Allowed |
| Read another job's mounted data | Denied or absent |
| Contact an unlisted host | Blocked |
| Read a dummy credential kept on the trusted side | Credential unavailable |
| Keep running after the job limit | Stopped |
| Reopen a finished job using another user's account | Rejected by the application |
The last row is easy to forget. An environment ID helps find the VM. It doesn't establish who owns it. Every command and file endpoint should check the authenticated user against the application's job record.
Make the failed attempts visible in test output without copying customer data or secrets. The goal is to show which operation was denied, not to collect everything the program tried to read.
Close the paths around the VM
The output is still untrusted. A generated HTML report might contain active scripts. A file can be enormous. A preview URL can accidentally become public.
For the CSV job, accept a small structured result such as a total and a row count. Check its shape and size before showing or storing it. If users need downloadable files, serve them with the right content type and access checks.
Decide what survives the job. Temporary input should disappear when the environment is destroyed. A mounted volume may have a different lifetime, so give it a separate cleanup rule. Don't assume destroying the VM also removed every durable copy.
The earlier guide to sandboxing generated code covers the wider design. If the blocked path involves an outbound broker, rejecting a host before DNS resolution is a separate control to examine. This article's SDK settings do not verify that broker or prove the runtime cannot be escaped.
Start with that and test the denied operations before adding another permission. If a job later needs an external API, you will know which new path you are opening and which checks must change.