Quarkus Insights #267: Sandboxing agents with Quarkus, for Quarkus

This summary was generated using AI, reviewed by humans - watch the video for the full story.

Quarkus Insights #267: Sandboxing agents with Quarkus, for Quarkus

The promise of coding agents is that you hand over a task, walk away, and come back to finished work. In practice, many developers spend their day pressing "yes" on permission prompts. In episode 267, Sanne Grinovero, one of the earliest Quarkus contributors, joins hosts Eric Deandrea and Holly Cummins to present isx, a tool he built on Quarkus that gives each agent its own disposable machine so it can run with full autonomy while your keys, files, and production credentials stay out of reach.

Quarkus News

Holly noted that Quarkus 4 Beta 3 is out and that CR1 is expected this week. She also reminded everyone that Quarkus:Dev is coming in early February, with the call for papers opening soon.

Three Questions About Quarkus and AI

Holly explained that questions about Quarkus and AI tend to fall into three groups:

  1. Embedding AI in applications: The answer is LangChain4j with the Quarkus integration.

  2. Using AI to build applications better: This covers the AI development experience, including the Quarkus MCP server and work by Eric and others to expose only the documentation that matches the extensions configured in your application.

  3. How the Quarkus team itself uses AI: isx is one answer to that question, and it is also a Quarkus use case because the tool is built on Quarkus.

Sanne introduced himself as a contributor from the early days, when the group was prototyping ideas such as running Hibernate in native images. Today he splits his time between Quarkus and other projects, and acts as a link between Quarkus and the OpenJDK engineers so that each side benefits from what the other is building. He stressed that isx is a hobby project rather than an official direction of the Quarkus team, although several team members, including Holly, already use it daily.

The Problem: Approval Fatigue

Sanne described the typical first experience with an agent. You ask it to fix a failing test, and it asks whether it can run the test, then whether it can add a dependency, then whether it can run the test again, and then whether it can commit. Before long you are the approver of every step, and Eric and Holly agreed that for small tasks you often think you would have been quicker doing it yourself, even before considering token cost.

To make the point, Sanne ran a game, written by his agent, in which the hosts had to approve or reject a stream of commands as they flew by. A harmless Maven invocation, a pull from an unknown script, a force push, and a database deletion all arrive in the same format and with no context, so it is easy to approve the wrong one.

Two things make this worse:

  • Permanent approvals are broader than they look. The "yes, and don’t ask again" button records a pattern, not a single action. Approving a test run also approves whatever code the agent wrote, since a test is just code that can do anything, including running a script that reads your keys or wipes your backups.

  • Auto mode is not a boundary. A second model reviewing each action is a good idea, and Sanne cited research showing roughly five correct decisions out of six. That sounds good, but it still means a mistake in one of every six operations, which adds up over a long session. Furthermore, the model can be asked to turn its own safeguards off, so they behave more like guidelines.

Some tasks cannot be automated away anyway. Building the JDK needs system packages, which means root access, and each missing dependency stops the agent to wait for you. Sanne’s example took six approvals and 36 minutes. This is precisely the boring environment setup he most wants an agent to handle, especially when he moves between projects and has to rebuild his local setup after months away.

isx: A Machine per Agent

isx is built on Incus system containers. Unlike an application container from Podman or Docker, which is optimized to run a single process, a system container boots a whole operating system. That means the agent can install packages, run PostgreSQL as a separate process, and start its own services, all inside a boundary with its own file system, user identity, and network. Nothing in there can reach your home directory, your keys, or your documents.

The design follows a few principles:

  • No approvals: Inside the boundary the agent can do whatever it wants, and what it is allowed to do is defined deterministically in a template that the agent cannot change.

  • Disposable machines: Sanne treats a machine as tainted after an agent has worked in it, so he destroys it after each task and creates a new one. This only works because creation and teardown are very fast.

  • No secrets: There is nothing worth stealing inside the machine.

  • Its own identity: The agent uses its own GitHub account, clearly marked as an AI bot, rather than Sanne’s. GitHub’s terms allow one such AI account, which must be identifiable as one, and it makes it obvious when a pull request was sent without supervision.

Why Not Worktrees?

Git worktrees let you check out several branches in separate directories, but they share everything else. Sanne described a race condition hunt in which one agent was collecting diagnostics while another published a different snapshot to the shared Maven repository. The failure vanished, and the first agent drew a false conclusion from a poisoned result without realizing it. Port conflicts and shared dependencies cause similar trouble. isx applies the worktree idea to the whole machine, so there are no port conflicts and no shared state.

Speed and Resource Use

Branching a machine from the Quarkus template takes about 400 milliseconds on Sanne’s laptop, and his target is under a second. Disk storage is copy on write, so every machine shares the blocks of its parent template and only changed blocks consume space. Because the machines read the same blocks, the operating system also shares their read caches.

On Linux, memory use is essentially the processes you run inside, as with any container, which beats a VM where memory must be assigned up front. ARM and x86 are both supported on Linux, and macOS support is ARM only. Windows is not supported yet, and Sanne would welcome contributions, although he would also need to work out how to run CI for it.

Templates

A template has a name, a description, and a parent. Sanne prefers composition to inheritance, so templates also have a tools section that pulls in building blocks such as Podman, Maven, and Claude Code, optionally with parameters. A template can pre-install skills, add an agent note that works like an addendum to the project instructions, and list the Git repositories to clone. Most templates are very short.

Repositories are cloned from the remote at template creation time, but any object that already exists in your local clone is fetched from there instead, which saves time. This is done conservatively: nothing flows in or out of your local repository unless the same information is available on the remote.

Sanne also noted that a few JDK builds ship their own certificates, which requires deploying custom certificates into the machine. A helper handles this case, although it gets hairy for people building their own JDK.

Keeping Credentials Out

Inside the machine you will not find your real tokens. You will find isx tokens that mean nothing outside it, because each is registered only against that local instance. A proxy runs on the host, and the machine’s traffic goes through it. For a given domain, the proxy terminates TLS, swaps in the real credential, and re-encrypts the request to the upstream API. From the agent’s point of view, it is talking to the Anthropic API or to GitHub over an ordinary encrypted connection. A custom DNS setup routes those domains to the proxy.

Sanne pointed to a recent report of around 2,500 secrets leaked in public repositories because tools did not redact them as users expected. Those were mistakes and misunderstandings, not malicious agents, but they illustrate why he prefers an environment with nothing to steal.

The proxy is configurable through plugins defined as YAML files that name the domains to intercept and the header to replace. Contributors have added support for Jira, for example, and Eric asked whether this works for tools such as Tableau. Sanne said it should be straightforward. You can also hold several subscriptions and swap which one a machine uses on the fly without the agent noticing, with one limitation: Claude uses a different protocol when going through Vertex than through the regular API, so switching between those two needs an agent restart.

Getting Work Out of the Sandbox

isx installs a custom Git protocol as an extension of your local Git. When you create a machine, a remote with that machine’s name is registered in your repository, and when you destroy the machine, the remote is removed. From your host you can fetch and pull from the agent’s repository like any other remote, then review the result in your IDE.

Many projects do not accept agent-authored pull requests, and Sanne does not send them directly from the sandbox either. He fetches the branch, inspects it, signs the commit himself, and then opens the pull request, which is his way of saying he actually looked at it.

The same mechanism helps with untrusted contributions. To test a pull request from a stranger, Sanne branches a machine from his template, checks out the PR inside, runs the tests, and destroys the machine. Anything malicious has zero blast radius.

On Linux, graphical applications running in the machine can display on the host. Sanne’s presentation was running in a Firefox instance inside an isx machine, and his agent edited the slides while he watched. macOS does not offer the same remote windowing, so front-end work there still needs to run on the host.

Coordinator Agents

A newer feature lets one isx instance control others through MCP. A coordinator can create sub-machines, delegate tasks to Claude Code, Codex, or other agents, check whether they are finished, collect results, and destroy them when they are no longer useful. Sanne introduced this because he found himself with around 100 agents running in parallel and could no longer tell which were still needed.

Security is layered in two places:

  1. A global MCP configuration file lists which templates may be coordinated at all, so templates holding sensitive information can never be controlled by an agent.

  2. The coordinator’s own template names the templates it may control, and can pull in a Git repository of instructions and skills.

Sanne maintains isx itself this way. A coordinator spins up a machine to take a first look at each incoming pull request, validates assumptions, resolves conflicts, and loops until the build is green. It then tells him which pull requests are waiting for his approval, so he no longer watches GitHub directly. He describes this as a large reduction in cognitive overload, and encouraged others to try the mode on larger tasks that need many pull requests coordinated.

How Quarkus Fits In

isx ships as two binaries tuned for different jobs:

  • The CLI and terminal UI: Built with Quarkus CLI, Aesh, and TamboUI, bound to native libraries through Panama, and talking to Incus over its REST client. It is tuned for startup and now launches in about two milliseconds on the simplest path, a level Sanne said Quarkus itself had never reached, so he had to learn new tricks and plans to push the stable ones back upstream.

  • The proxy: A long-running daemon built on Vert.x, with different CPU and garbage collection settings.

Building it exposed problems that more conventional applications do not. Early on, Sanne found nasty bugs in the Quarkus CLI, including one where it picked up configuration files from the current directory, which was a particularly circular problem when using the Quarkus CLI to work on Quarkus. The proxy also took real effort to migrate from Vert.x 4 to 5 because of its low-level code, and it now runs on Quarkus 4. Sanne’s hope is that side projects that push the boundaries find improvements the framework can use. Given the complexity of the effort, Eric suggested swapping in Sanne to the saying that the Quarkus team does the hard work so users do not have to.

Two further changes stood out:

  • Replacing Jackson with jjq: Sanne was only using Jackson to parse YAML configuration, so he switched to jjq, a fast parser built by Ståle Pedersen from the Quarkus performance team.

  • Profile-guided optimization: After learning more about GraalVM PGO at a recent summit, Sanne enabled it for all releases. The proxy binary shrank by 58 percent, individual use cases improved by between 19 and 80 percent, and the CLI starts even faster.

Linux and macOS Architecture

On Linux, the CLI talks to the Incus daemon, which controls the storage pool, instances, and network. A Git helper also talks to the daemon so that fetches reach the right container. A switch lets you use VMs instead of containers when you want a stronger boundary, though Sanne was candid that no sandbox will contain a determined, malicious AI. isx is meant to prevent well-intentioned mistakes, not to replace air gapping.

On macOS the same stack runs inside a single VM using Apple’s virtualization framework, with the memory you assign shared across all agents inside it. Nested VMs are not possible there, but the VM already provides the isolation. Getting there involved a custom Linux kernel, a custom Fedora distribution, a custom Alpine appliance, and WebSocket forwarders, and working around a connection leak that sometimes hangs and needs a recovery process. Sanne said none of this would have been feasible without an agent.

Caching Without Trusting the Cache

Because every download passes through the proxy, caching is an obvious optimization, but Sanne dislikes caches that can serve stale or corrupted artifacts without any way to evict them. His approach is to look carefully at each protocol:

  • Maven Central: Snapshots are never cached. For released artifacts, the proxy asks Maven Central for the SHA checksum with an HTTP HEAD request and serves the local copy only if it matches, otherwise the entry is evicted and downloaded again. Two optional timeouts skip the check for very recent requests. As an exception for working on trains and planes, a verified local copy can be served if Maven Central is unreachable.

  • Other registries: Container registries and npm and Gradle plugin repositories get similar treatment, each cached only under conservative assumptions.

Demo

In a short terminal UI demo, Sanne created a new machine from the Quarkus template by pressing a single key. It was ready in about 400 milliseconds, with Claude Code running in around two more seconds. He opened a second one, jumped between them, and showed both appearing as remotes in his local Quarkus checkout. Templates can copy or overlay files into the machine, and the UI integrates with the Herder tool for switching between agent sessions. The agents run with permission bypass on by default, because the sandbox is the safety boundary.

Audience Questions

  • Competition: Asked about Docker Sandboxes, Sanne said he started isx without knowing about them and was glad to see Docker endorse the same system-level approach. He has not compared them closely. His focus is Git integration, and he needs a system container so that Quarkus development with Testcontainers works inside the machine.

  • Switching models: isx does not route between models, because he considers that feature creep. Tools such as OpenRouter already do it, and within Claude you can switch models or use the advisor feature to ask a stronger model for a second opinion.

  • Subscriptions: You can set a different account for each machine and swap it with a command.

Key Takeaways

  1. Approvals do not scale: Repeatedly clicking "yes" trains you to approve everything, and a permanent approval often covers far more than you intended.

  2. Auto mode is a soft guardrail: A reviewing model improves matters but is not a security boundary.

  3. Isolate the whole machine: System containers give each agent its own file system, network, identity, and services, which avoids the shared state problems of worktrees.

  4. Disposable by design: Machines that start in about 400 milliseconds can be destroyed after every task.

  5. Agents never see real credentials: A TLS-terminating proxy injects secrets in transit, so a leaked token is worthless outside its machine.

  6. Review on your own terms: A custom Git remote lets you fetch the agent’s work, inspect it, and sign it yourself before opening a pull request.

  7. Coordinators reduce overload: One agent can create, supervise, and clean up many others, within an explicit allow list of templates.

  8. Verify caches, do not trust them: Checksum validation makes dependency caching fast without risking stale artifacts.

  9. Side projects improve Quarkus: Building isx uncovered CLI bugs, Vert.x 5 migration lessons, and startup tricks that can flow back into the framework.

Conclusion

isx shows that the way to make agents useful is not to supervise them more closely but to make it safe to stop supervising them. Putting the boundary around the whole machine, keeping real secrets on the host, and reviewing results through Git lets you hand off a task and return to a tested result. Next week, Quarkus Insights returns with a look at secure agents with LangChain4j.

Watch the full episode on the Quarkus YouTube channel and try isx from the incus-spawn repository on GitHub.