ENGINEERING / FIELD NOTES No. 001

Give the agent
a workbench.

How AI agents solve complex problems by writing code on the fly—and how to give that code a safe place to run.

Start with the worked example
Three glass execution chambers on an orbital workbench, transforming loose paper data into structured blue artifacts
Freedom to experiment. A boundary around the consequences. Original AI-assisted illustration for NDorbit

A customer sends two exports and a deceptively simple question: “Which orders haven’t been paid in full?”

The files contain duplicate transactions, partial payments and references that do not match an order. A fluent explanation will not reconcile them. Someone has to inspect the rows, decide what counts as a duplicate, add the right amounts and show the exceptions.

An AI agent can write a small program for that particular job. It can run the program, inspect an error, revise its assumptions and produce a result you can check. The code might exist for only a few seconds. The evidence it produces is what matters.

Sometimes the missing tool
is a little program.

A fixed tool is ideal when the operation is known: retrieve an invoice, look up a customer, create an approved ticket. Code becomes useful when the shape of the work changes. One export needs a join; another needs a date conversion; another needs a simulation that no one anticipated when the tool catalogue was designed.

That does not mean letting the model improvise access to everything. Give it a narrow input contract, a budget and a place to execute. Ask it to return the assumptions, program and evidence alongside its answer.

01 / THE EXECUTION LOOP Small steps. Inspectable evidence.
  1. Understand Question + permitted inputs
  2. Write A small, testable program
  3. Execute An isolated, bounded run
  4. Inspect Output, errors and checks
Revise when evidence disagrees · stop when checks pass or the budget expires
The model proposes a program; the runtime produces observations. A successful process exit alone does not prove the answer is right.
THE DISTINCTION THAT MATTERS

A sandbox limits what a program can affect. It does not decide whether the program’s answer is correct.

Three jobs make the pattern tangible. Reconciliation needs exact arithmetic and row-level exceptions. An operations report might need to normalise timestamps before correlating two logs. A staffing question might need a simulation across different arrival rates. In each case, code does the mechanical work; the agent explains assumptions and interprets the result.

01 / FROM QUESTION TO EVIDENCE

The total looks plausible.
It is still wrong.

Here is a deliberately small, synthetic dataset. Four orders total NZ$550. Six payment rows total NZ$650. That apparent surplus disappears once we notice that transaction tx1 occurs twice and tx4 points to an unknown order.

Walk through the calculation below. The complete Python program is in the download; the page illustrates its steps without executing code in your browser.

ANATOMY OF A RESULT NZD · synthetic data
$650 raw receipts

Six payment rows, four known orders. Adding every row gives $650. That number includes a duplicated transaction and a payment for an unknown order.

tx1  A101  120.00
tx1  A101  120.00  ← repeated
tx2  A102   80.00
tx3  A103  100.00
tx4  A999   30.00  ← unknown order
tx5  A104  200.00

A guided illustration of the downloadable Python example. No code or payment operations run in your browser.

Make the rules explicit before the run

For this example, an identical transaction ID and payload means a duplicated export row. Conflicting payloads with the same ID are an error. Unknown orders are reported separately. Amounts use Python’s Decimal, and every row uses the same currency. Real payment systems also need agreed treatment of reversals, fees, refunds and settlement dates.

The important part of reconcile.py
if transaction in seen:
    if seen[transaction] != (order, value):
        raise ValueError("Conflicting duplicate")
    duplicates.append(transaction)
    continue

seen[transaction] = (order, value)
if order not in orders:
    unmatched.append({"transaction": transaction,
                      "order": order, "amount": str(value)})
    continue

paid[order] += value

The final report identifies a NZ$50 shortfall on A103, the duplicate tx1 and the unmatched NZ$30 payment. It does not mark an order paid or issue a refund. Those actions belong to a separate, authorised workflow.

The first run can teach you something

Imagine the agent’s first version simply sums the payment column. A check against unique transaction IDs exposes the duplicate, so the agent revises the program. A second check finds the unmatched order. This is the useful part of the loop: errors and invariants become new evidence, rather than something the model smooths over in prose.

Keep the inputs immutable and the acceptance checks outside the agent’s writable workspace. An agent that can change both the answer and the test that approves it has an easy route to a reassuring but meaningless green tick.

02 / UNDER THE WORKBENCH

The broker says what may run.
The runner does the work.

A sandbox is an execution environment with enforced limits on filesystem access, processes, resources and network activity. Depending on the system, the boundary might use operating-system controls, a userspace kernel, a virtual machine, or several layers together.

It helps to separate two responsibilities. In this reference design, the broker is the trusted front door. It authenticates the caller, checks the job belongs to the right tenant, selects an approved environment, applies quotas and records what happened. The runner is the disposable worker that starts the program and captures its observations.

02 / WHO IS ALLOWED TO DO WHAT? Reference architecture
REQUEST AI agent

Code + input references
No infrastructure credentials

TRUSTED CONTROL PLANE Sandbox broker

Identity · policy · quotas
Admission · audit · job ownership

EXECUTION BOUNDARY
Disposable runner

Approved runtime + inputs
Code runs here

Limited CPU / memory / time / network
Agent receives observations Bounded, validated results stdout · exit code · artifacts
Credentials stay outside. Outputs remain untrusted. Every job has an owner and an expiry.
“Broker” and “runner” describe responsibilities in this design; they are not universal vendor component names. A trusted launcher can sit between them to create and destroy workloads.

A request is a job, not permission to administer a server

The agent submits code and permitted input references. It should not submit arbitrary container flags, image names, host mounts or Kubernetes pod specifications. The broker turns a small request into a fixed execution template. A trusted launcher starts the worker; cancellation and expiry remove it even when the program fails.

The result contract can be boring: job ID, exit code, bounded stdout and stderr, artifact references and execution metadata. That is a strength. It makes failures observable and lets the agent distinguish a timeout from an empty result.

Illustrative broker contract · not an endpoint in the demo
POST /jobs
{
  "runtime": "python-approved",
  "input_ids": ["tenant-scoped-upload-id"],
  "code": "...",
  "idempotency_key": "request-unique-id"
}

GET /jobs/{job_id}
{
  "status": "completed",
  "exit_code": 0,
  "stdout": "bounded output",
  "artifacts": []
}

Resolve tenant identity from authentication, not a caller-supplied tenant field. Authorise every status read and artifact download. Repeated submissions should reuse the intended job rather than quietly double the bill—or repeat a side effect.

Keep the keys on the other side of the boundary

For an offline calculation, the runner needs no network and no credentials. If a task needs an external service, give it a narrow, authenticated tool route or a policy-enforcing proxy. Keep long-lived credentials outside the execution environment. Remember that even an allowed destination can receive sensitive data: an allowlist is only one part of the policy.

CONTAINER
Generated program
Namespaces + limits
Shared host kernel

A useful layer, with a shared kernel boundary to consider.

gVisor
Generated program
Userspace application kernel
Host kernel

Intercepts the application’s system calls; compatibility needs testing.

MICROVM
Generated program
Guest kernel
Virtual machine monitor

A separate guest kernel, plus host and hypervisor controls.

Three isolation approaches, simplified. All still need identity, network policy, resource limits, patching and safe handling of results.

Ordinary containers share the host kernel. gVisor places an application kernel between the workload and many host interactions; microVMs introduce a separate guest kernel. Those are materially different boundaries. Choose against your threat model and test the workload’s compatibility. Docker’s security model and gVisor’s architecture guide explain the distinctions and limitations.

Isolation also ends somewhere. A generated CSV can contain spreadsheet formulas; an HTML report can contain active content; a filename can attempt path traversal. Treat artifacts as untrusted on the way out, with size, type and path checks. Never turn arbitrary runner text into trusted instructions for the next tool.

03 / BUILD A SMALL ONE FIRST

Your own sandbox,
without the magic box.

Start with a single disposable job. The companion files contain the reconciliation program, a bounded Linux runner, a Dockerfile, Compose configuration and Kubernetes manifests. There is no model API key and no public execution endpoint. This lets you inspect the execution layer before connecting an agent to it.

COMPANION FILES · ZIP Build it. Run it. Read the result. Python + Docker Compose + Kubernetes + instructions

On a laptop: Docker Compose

Use Docker with Linux containers. Unzip the companion files, open their directory and run the two commands below. Building needs access to the base-image registry; the job itself has no network. The image contains the small synthetic dataset.

From the extracted example directory
docker compose build
docker compose run --rm runner

The configuration uses a non-root user, a read-only root filesystem, no added Linux capabilities, and a small temporary filesystem. CPU, memory and process limits bound the job. The runner imposes a ten-second process deadline and captures at most 64 KiB per output file. Python’s -I flag isolates interpreter configuration; it is not the security boundary.

Inspect the complete Compose configuration
compose.yaml
services:
  runner:
    build: .
    image: ndorbit-sandbox-demo:local
    user: "10001:10001"
    read_only: true
    network_mode: none
    cap_drop: [ALL]
    security_opt: ["no-new-privileges:true"]
    pids_limit: 32
    cpus: 0.5
    mem_limit: 128m
    memswap_limit: 128m
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=16m,mode=1777
    init: true
    restart: "no"
    logging:
      driver: json-file
      options:
        max-size: "1m"
        max-file: "1"

Expect an outer JSON object with exit_code: 0 and timed_out: false. Its stdout contains the reconciliation report. The important values are allocated_total: "500.00" and the A103 shortfall of "50.00". The supplied script asserts both.

WHAT THIS DEMO PROVES

You can run a bounded, disposable calculation. It does not prove that a shared Docker host is safe for arbitrary hostile tenants. Do not mount the Docker socket, your home directory or production credentials into the runner.

On Kubernetes: make the boundary explicit

The downloadable Job declares a gvisor RuntimeClass. Your cluster must already have that runtime installed on eligible nodes, and the name must match your setup. Creating a RuntimeClass object alone does not install a sandbox. Do not remove the field just to make a pending untrusted job start.

Use a disposable test cluster with a CNI that enforces NetworkPolicy. Apply the namespace and deny-all policy before the Job. Load the demo image into the nodes, or replace its local image reference with a reviewed digest in your registry. The README describes both paths.

After installing the runtime and loading the demo image
kubectl config current-context
kubectl get runtimeclass
kubectl apply -f namespace.yaml
kubectl apply -f job.yaml
kubectl wait --for=condition=complete job/reconcile-demo \
  -n ndorbit-sandbox-demo --timeout=60s
kubectl logs job/reconcile-demo -n ndorbit-sandbox-demo
Inspect the complete Kubernetes Job
job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: reconcile-demo
  namespace: ndorbit-sandbox-demo
spec:
  backoffLimit: 0
  activeDeadlineSeconds: 30
  ttlSecondsAfterFinished: 300
  template:
    spec:
      # MUST exist and map to an installed, working isolated runtime.
      # Substitute your verified gVisor/Kata RuntimeClass name.
      runtimeClassName: gvisor
      automountServiceAccountToken: false
      restartPolicy: Never
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: runner
          image: ndorbit-sandbox-demo:local
          # Load this image into test cluster nodes before applying.
          imagePullPolicy: Never
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: [ALL]
          resources:
            requests:
              cpu: 100m
              memory: 64Mi
            limits:
              cpu: "1"
              memory: 128Mi
              ephemeral-storage: 32Mi
          volumeMounts:
            - name: scratch
              mountPath: /tmp
      volumes:
        - name: scratch
          emptyDir:
            medium: Memory
            sizeLimit: 16Mi

The Job disables service-account token mounting, forbids privilege escalation, limits resources and requests a read-only root filesystem. It has no automatic retry, a 30-second active deadline and a five-minute cleanup TTL. Collect results before cleanup. Configure node-level PID limits separately; the Compose PID setting does not transfer into this manifest.

NetworkPolicy is effective only when the network plugin enforces it. Verify denial on the actual cluster, including access to metadata services and internal destinations. A namespace is an organisational boundary, not a complete security boundary. See the official network-policy and Job documentation.

Turning the demo into a service

Keep the broker outside the job namespace where practical. Give its launcher only the necessary permissions, and use admission policies to enforce allowed images, security settings and mount restrictions. RBAC permission to create Jobs does not, by itself, stop someone from asking for a dangerous Pod.

Add per-tenant concurrency limits, queue backpressure, a total execution budget, reliable cancellation and cleanup for abandoned jobs. Stream bounded observations; validate artifacts before storing them under tenant-scoped names. Run final acceptance checks in a separate trusted environment. Reuse a workspace only within an explicitly permitted session, and destroy it before assigning it to another tenant.

If you need sessions, warm pools or more lifecycle machinery, examine Kubernetes SIGs Agent Sandbox before writing your own controller. The simple Job here is a teaching example, not a replacement for an operated sandbox platform.

04 / CHOOSING YOUR WORKBENCH

One word.
Several different products.

“Sandbox” can mean a Python tool inside an agent API, a complete coding environment, or infrastructure you use to build your own execution service. Compare the boundary and workflow you need, not just whether the product can run print("hello").

Documentation checked 28 October 2026. This is a practical selection guide, not a market-share ranking. Availability, defaults and preview features change.

Inside the major agent platforms
Platform What you are getting The question to ask
OpenAI Code Interpreter lets a model write and execute Python in a sandboxed container, work with files and return generated artifacts. Does the tool’s runtime, container lifecycle and file handling fit the job? Do not assume these settings describe every ChatGPT or Codex environment.
Google / Vertex AI Agent code execution uses managed sandboxes. The former Vertex AI Agent Engine documentation now sits under Gemini Enterprise Agent Platform. Which API, region, language and sandbox lifecycle does your chosen integration support? Follow the current quickstart rather than an old product label.
Microsoft Foundry Code Interpreter runs Python for analysis, charts and iterative tasks in a managed environment, with session behaviour and regional availability to account for. Is it available for your region and agent setup, and how do concurrent sessions affect cost and data retention?
GitHub Copilot Local sandboxing restricts command access to your machine. Cloud sandboxing runs an entire session in an ephemeral hosted Linux environment; the cloud offering is documented as public preview. Are you constraining a local coding session or delegating a remote one? Review repository permissions, network access and approval settings for that surface.

When the sandbox is your infrastructure

These are prominent options to investigate when you need more direct control over the execution environment. They are not interchangeable, and their defaults deserve a closer read than a feature checklist.

E2B Agent execution infrastructure
Its published runtime separates the control-plane API, per-node orchestration and guest agent, with Firecracker underneath. Investigate it when you want programmable environments and a platform built around agent workloads.
Daytona Development environments for agents
Useful to investigate for programmatic workspaces. Its v2 alpha documentation describes microVM isolation and separate project/network controls; do not apply those version-specific guarantees to another release. The documented v2 egress default is open, so configure policy deliberately.
Modal Sandboxes Programmable compute
An option for creating execution environments through a compute platform. Check its sandbox networking, file access, lifecycle and resource controls against your workload, especially if you already use Modal elsewhere.
Cloudflare Sandboxes Workers-connected execution
A fit to investigate when the surrounding application lives in Workers. Its coding-agent examples demonstrate a Worker starting sandboxed repository work and returning a diff.
Kubernetes Agent Sandbox Operate your own
Lifecycle tooling for a Kubernetes-based approach, with documented gVisor and Kata integrations. You keep the operational responsibility for nodes, policy enforcement, image supply chain and capacity.

For a report over uploaded files, a managed code-interpreter tool may be enough. For repository changes, you probably need a filesystem, language toolchains and test processes. For sensitive multi-tenant execution, identity separation, egress control, runtime isolation and deletion guarantees become central selection criteria.

05 / THE PART YOU STILL OWN

Give the agent room to try.
Keep a way to check.

The interesting outcome is not that a model can write Python. It is that a team can hand over a question and get back a reproducible calculation, the assumptions behind it and a clear account of what remains unresolved.

Before connecting the workbench to real systems, decide what evidence will count as success. Limit how much time and money the agent may spend finding it. Keep irreversible actions behind a separate authorisation step. Capture enough of the run to explain a failure without logging secrets or unnecessary customer data.

Let the agent improvise the method.
Be deliberate about the permissions.

A good first project is small: one input format, one approved runtime, one kind of output and a check you trust. Once that loop works, broaden it with evidence. The workbench earns its place by making the result easier to verify.

Read the source.

Primary documentation behind the platform comparison and deployment discussion. The broker/runner architecture and synthetic example are this article’s reference design.

  1. OpenAI · Code Interpreter

    Python execution, containers and generated files.

  2. Google · Agent Platform Code Execution

    Create a sandbox, execute code and clean up resources.

  3. Microsoft Foundry · Code Interpreter

    Python tool, files, sessions and availability.

  4. GitHub · Cloud and local sandboxes

    Different execution boundaries for local and hosted agent sessions.

  5. E2B · Runtime

    Control plane, orchestration and Firecracker runtime.

  6. Daytona · Isolation (v2 alpha)

    Version-specific microVM, network and project boundaries.

  7. Modal · Sandboxes

    Programmatically created execution environments.

  8. Cloudflare · Coding agents in Sandboxes

    Sandbox workflows integrated with Workers.

  9. gVisor · Architecture and security

    Application-kernel isolation and its limits.

  10. Docker · Engine security

    Namespaces, capabilities and the Docker daemon attack surface.

  11. Docker · Compose service reference

    Network, filesystem and resource controls used by the demo.

  12. Kubernetes · Network policies

    Policy behaviour depends on network-plugin enforcement.

  13. Kubernetes · Jobs

    Deadlines, retries and completion semantics.

  14. Kubernetes SIGs · Agent Sandbox

    Purpose-built sandbox lifecycle tooling and runtime integrations.

Tell us where
the work
gets held up.

Describe the process you want to improve and the systems involved. We can discuss whether discovery, automation or a focused AI pilot is the right next step.

[email protected]
✳ New Zealand · Working globally

Tell us about your project

Which task takes time, and what would a useful result look like?

This opens WhatsApp with your message ready to send. Opening the draft shares its contents with WhatsApp; tap Send there to deliver it. Do not include confidential information. Website privacy.