// Stay in touch?
Products
Encore CloudEncore Cloud
Encore.tsEncore.ts
Encore.goEncore.go
PricingPricing
Book a DemoBook a Demo
Use Cases
AI-Powered DevelopmentAI-Powered Development
Event-Driven SystemsEvent-Driven Systems
Distributed SystemsDistributed Systems
Case StudiesCase Studies
ShowcaseShowcase
Resources
DocsDocs
InstallInstall
Example AppsExample Apps
Demo videoDemo video
ArticlesArticles
GitHub ReleasesGitHub Releases
Systems Operational
Company
About UsAbout Us
Swag ShopSwag Shop
ContactContact
JobsJobs
PressPress
SecuritySecurity
TermsTerms
Privacy PolicyPrivacy Policy
Data Processing AgreementData Processing Agreement
Enterprise SLAEnterprise SLA
Encore
© 2026 EncoreAll rights reserved
© 2026 Encore All Rights Reserved
GitHubDiscordYouTube

Sandboxing an agent's code isn't the same as trusting it

A sandbox keeps an agent's code contained. It can't tell you the code is correct, which for backend code is the harder problem.

Jul 21, 2026
8 Min Read
Ivan Cernja
Jul 21, 2026

Sandboxing an agent's code isn't the same as trusting it

A sandbox keeps an agent's code contained. It can't tell you the code is correct, which for backend code is the harder problem.

Ivan Cernja
8 Min Read

When people talk about running an AI agent's code safely, they usually mean a sandbox. Daytona, e2b, and Fly's Sprites each give the agent a microVM or container that isolates code you have no reason to trust, so a script it generated cannot reach anything it shouldn't. That is containment, the right tool for running untrusted code, but also the half people reach for first and treat as the whole of the problem.

For agent-written backend code, a sandbox leaves a second question unanswered. Even when the code runs somewhere safe, how do you know it is correct before it ships? Backend code an agent writes usually compiles and passes a couple of tests and is still wrong in ways that only show up against real infrastructure, like a query that works on an empty table and falls apart on a full one. An agent works in a loop, changing the code and re-running whatever feedback it can get until nothing complains, so what it ships is whatever that feedback missed. Making it safe to ship is a matter of putting better feedback in that loop and giving the code a real place to run before production. The examples here are in Encore, since that is what we build, though the loop is not specific to it.

The loop has a few places to catch a problem before it reaches a user, and the rest of this post walks through each one.

A change an agent writes passes through several checks before production: types at compile time, a preview environment running real dependencies, the agent's own checks over MCP, and a human review of the result.

Agent's changecode and declared infrastructure
compile
Types
run
Preview environment
mcp
Agent checks itself
review
Human review
Productionyour AWS or GCP
A failure at any stage sends the change back to the agent, and only a change that clears all of them reaches production.

Catching mistakes before it runs

The cheapest feedback fires before the code runs at all, and you get more of it by narrowing what the agent can express. When an endpoint's request and response are ordinary types, returning the wrong shape is a compile error on the exact line, caught while the agent is editing rather than as a 400 some caller hits later. The same holds across services, where a call that passes a field the receiving service does not declare fails to compile.

In Encore that surface is the API definition and the infrastructure a service declares. A typed endpoint backed by a database looks like this:

import { api } from "encore.dev/api"; import { SQLDatabase } from "encore.dev/storage/sqldb"; const db = new SQLDatabase("todo", { migrations: "./migrations" }); interface Todo { id: number; title: string; done: boolean; } export const get = api( { expose: true, method: "GET", path: "/todo/:id" }, async ({ id }: { id: number }): Promise<Todo> => { const row = await db.queryRow<Todo>` SELECT id, title, done FROM todo_item WHERE id = ${id} `; if (!row) throw new Error("todo not found"); return row; }, );

Those request and response types are the contract, so an agent that returns the wrong shape from this handler sees a type error while it is still editing, with the line and the expected type, and fixes it in the same loop where the mistake was made instead of leaving it for staging where it becomes someone else's to debug days later. Infrastructure is declared the same way, in code, which takes a whole category of error off the table, because the agent never hand-writes an IAM policy or a connection string and so cannot get them wrong. The parser that reads those declarations also fails the build when they do not hold together, so a service pointed at a database with no migration directory stops with "The migration directory does not exist" before it deploys.

Running against real dependencies

Types rule out the mistakes that are about shape, but the rest only appear when the code runs against the systems it talks to, where a query can be valid SQL and still return the wrong rows, or a migration can apply cleanly in review and then lock a table under real load. Catching those means running against a real database and a real queue rather than mocks. The gap shows up with something like an upsert:

INSERT INTO todo_item (id, title, done) VALUES ($1, $2, false) ON CONFLICT (id) DO UPDATE SET title = EXCLUDED.title RETURNING id, title, done;

Against real Postgres this leans on a unique constraint and on EXCLUDED to update the row on conflict. A SQLite stub or an in-memory fake handles ON CONFLICT ... DO UPDATE differently, or not at all, so the agent's code passes its test and is still wrong once it reaches production. Encore runs real Postgres locally through Docker, so the schema the agent builds against and the query it runs are the ones production will use, after the same migrations.

That run cannot happen in production, so each change needs somewhere real but disposable to go. Encore brings up a preview environment for every pull request, with its own database and infrastructure, when the PR opens, and tears it down when the PR is merged or closed. It can seed that database from a copy of your main one so the data is realistic, and the agent's change runs there end to end before anyone thinks about promoting it. The change's tests run in that environment too, against the real database instead of a mock.

A sandbox contains code you cannot trust, while a preview environment runs code you expect to work and confirms that it does, including against a copy of production data. A system that needs both would run them at different layers, and Encore is the verification side, not a sandbox for untrusted code.

Letting the agent check its own work

Running the code only helps if something checks the result, and the agent can do a lot of that itself once it can see the running system instead of guessing at it. Encore exposes the running app to an agent through an MCP server.

The schema is the most useful piece, since an agent adding a field to a response can read the real tables and columns and write a correct query the first time instead of guessing at column names, and because Encore runs real Postgres locally, the schema it reads is the one the code runs against after the latest migration. It can also call its own endpoints and read the traces that come back, seeing the queries that ran and the response that came out rather than guessing.

An agent reading an Encore app's traces back over the MCP server.

All of this replaces the agent's assumptions with things it can look up, so what it verifies is how the system behaves rather than what the model expected.

What a person still reviews

A person still reviews each change, but the review is of the result rather than the code. A change that ran in its own environment against real dependencies and passed its checks comes with evidence behind it, so the reviewer sees what it provisioned and how it behaved instead of reading line by line through whatever the agent generated. A change that failed somewhere never reaches review, and since the environment was disposable, the failed attempt costs nothing to drop.

One thing the loop does not catch is whether the business logic is right. The type system says nothing about that, and the agent can always reach for an escape hatch, an as any or a @ts-ignore that quiets a check it could not satisfy. What keeps that in check is that a suppression is a visible line in the diff, so it shows up in review and you can lint it to zero in CI.

None of this rests on the agent being more careful or the model being smarter. The code earns its way to production by running somewhere real and surviving real checks, the same bar you would hold code a person wrote to. As more of the backend is written by agents, the bottleneck moves from writing the code to making sure it is safe to run, and a loop with real feedback and a real place to run is what keeps that from slowing everything down.

Every Encore app gets a preview environment per pull request and an MCP server an agent can read, so the loop is there once you build on it. The quick start is the way in, and the docs cover how Encore provisions infrastructure from your code.

Contents
Catching mistakes before it runs
Running against real dependencies
Letting the agent check its own work
What a person still reviews
Encore

The backend platform for humans and agents

Build backends in Go or TypeScript and run them on your own AWS or GCP, with the guardrails your team and AI agents need to ship safely.

Get started
Encore

This blog is presented by Encore, the backend framework for building robust type-safe distributed systems with declarative infrastructure.

Like this article?
Get future ones straight to your mailbox.

You can unsubscribe at any time.

Related Articles

Development
07/15/26 / 7 Min Read
Development
07/15/26 / 7 Min Read
We compiled our TypeScript parser to WASM
How we compiled the Rust parser that reads infrastructure out of Encore code so it runs in the browser, and how we use it to reproduce the parser errors people report.
Ivan Cernja
AI
07/07/26 / 7 Min Read
AI
07/07/26 / 7 Min Read
Automating cloud infrastructure with Cursor
Now a Cursor agent can provision your cloud infrastructure from the same code it writes, on your own AWS or GCP.
Ivan Cernja
Development
06/25/26 / 6 Min Read
Development
06/25/26 / 6 Min Read
We put a Redis server inside our runtime
How we run an in-memory Redis inside the runtime for local development and tests, and how we keep it behaving the same as the Redis you run in production.
Ivan Cernja