AI Security 6 min read

MXC Sandbox: Where Your Agent’s Code Should Run

I tried to install the MXC sandbox v1.0.0 on Linux and it failed on node-pty. Here is what it takes to run LLM-written code at a small startup.

Abstract editorial illustration of a small amber box inside a larger teal frame with a thin line stopped at the frame edge, flat vector shapes on a navy background.

MXC is a free, MIT-licensed library from Microsoft that runs a command inside a sandbox and hands you the output, so you can run code an AI model wrote without giving it your whole machine. The project’s own README calls it a “sandboxed code execution system” for “untrusted code (model output, plugins, and tools)”. It reached v1.0.0 on October 7, eight days after v0.9.0, with stable Rust, .NET and Node entrypoints and a promise to follow semver.

In this post I go over what the MXC sandbox does, what happened when I installed it on plain Linux, what the Linux backend needs before it works, and what I would run at Trellis. My verdict in one line: it is the right kind of answer for a small team that lets an LLM run code, and the wrong thing to adopt this week unless you read the Linux prerequisites before you read the release notes.

Here is why I care. Trellis generates small scripts that reshape seller data, things like “take this 40,000-row catalogue export and flag every listing with fewer than five images”. Today a human looks at that script before it runs. Mettons we remove the human. Then a generated script that is slightly wrong, or slightly hostile because of what is inside a product title, runs next to the same machine that holds our API keys. I made up that scenario, but not the question: where does the code the model wrote actually run? I said in an earlier post that your agent is a junior operator with root access, and generated code is where that sentence gets literal.

So I tried it. I ran npm install @microsoft/mxc-sdk@1.0.0 on a plain Linux box with Node 22, and it died in about ten seconds on a dependency called node-pty: gyp ERR! stack Error: not found: make.

What does the MXC sandbox actually do?

You describe the job to MXC as one typed request: the command, the read-only and read-write paths, the network policy, and a timeout.

Per the README table, Linux defaults to bubblewrap, macOS to seatbelt, Windows to a process container. There are heavier options, including microVMs and Hyperlight, and the README marks several of them experimental.

What v1.0.0 changed, according to the release notes, is mostly a promise. You can run to completion and capture stdout, spawn with pipes, or spawn with a terminal. The notes list breaking changes: the old request types are replaced, so if you started on 0.x you are rewriting some code.

Abstract editorial illustration of a small amber box inside a larger teal frame with a thin line stopped at the frame edge, flat vector shapes on a navy background.
The line stops at the gap in the frame. With deny-all network, the gap stays shut until a policy opens it.

What happened when I installed the MXC sandbox?

The SDK’s package.json asks for Node 24 or later in engines, and I had Node 22.23.3. npm printed an EBADENGINE warning, but a warning does not stop an install, so that was not the failure. The failure was node-pty, whose install script is node scripts/prebuild.js || node-gyp rebuild, which means “use a prebuilt binary if there is one, otherwise compile”. I opened the prebuilds folder inside the installed package. It holds four directories: darwin-arm64, darwin-x64, win32-arm64, win32-x64. No Linux. So on Linux, every install compiles node-pty from source, and on a machine without a compiler, like mine, that means failure. A Dockerfile with build-essential will be fine. A minimal base image will not, and you will find out in CI.

I tried again with npm install --ignore-scripts. That works. The node_modules folder came to 168 MB, and 100 MB of that is the SDK’s own bin directory, Windows executables included. Then I called getPlatformSupport() from the Node SDK and got isSupported: false, with a reason saying bubblewrap is missing and 0.5.0 or newer is required. A run() call copied from the README, with deny-all network and a read-only current directory, threw the same bubblewrap message. Clear errors, and no silent fallback to running the command unprotected. That is the whole extent of what I tested.

I did not run a single command inside the sandbox. This box has no bubblewrap and I did not install system packages on it. So I have nothing to say about how strong the isolation is, or how much overhead it adds. If you read that MXC “is secure” anywhere, that sentence has no test behind it from me.

What does the Linux backend need before it works?

The bubblewrap backend doc is the most useful file in the repo, and it is honest about the cost. You need bubblewrap 0.5.0 or newer, user namespaces enabled, and that is enough if your policy says the sandbox has no network at all. The doc says ruleless deny “uses --unshare-net alone and needs none of these additional network tools”.

The moment you want to allow some network, the list grows. The doc asks for slirp4netns, util-linux unshare with --map-current-user, nsenter, iptables and ip6tables plus their restore tools, a POSIX sh, and the nf_conntrack kernel module already loaded, because an unprivileged process cannot load it on demand. iptables must use the nf_tables backend. And IPv6 is off: the doc says a destination there “is unreachable even when a rule allows it” and points to issue #955.

So “deny everything” is one flag. “Allow only my own API” is a small ops project. That gap is the most useful thing I learned this week, and it shapes what I would do first.

Is MXC v1.0.0 safe to depend on?

On the API, probably yes, since a stable entrypoint and a semver promise are what you want before building on a library. On isolation, I do not know, and neither do you, from reading a README.

Microsoft wrote it, and I went looking for an independent write-up testing escapes and found none. The README has a warning I respect: audit mode “turns off all sandbox security”, so never point it at untrusted code. Telemetry is, per the README, Windows-only and off unless consent is given.

Then there are the adoption numbers, which I will label carefully. The npm API says @microsoft/mxc-sdk had 370,706 downloads from October 2 to October 8. The crates.io API says the Rust crate has 68 downloads in total. I think another package depends on the npm one, so I would not read 370,706 as 370,706 teams, but I could not establish who it is. The repo sits at about 2,400 stars, and a star is a click, not a code review.

What I would do at Trellis

With the MXC sandbox I would start boring. Generated scripts run on a Linux box we control, with the network denied, the working directory read-only except one scratch folder, and a timeout of thirty seconds. This is roughly the request the README shows, and it is the one configuration that needs only bubblewrap and no iptables project:

const result = await run({
  filesystem: { readonlyPaths: ['/srv/jobs/input'] },
  network: { egress: { default: 'deny' } },
  timeoutMs: 30_000,
  command: 'python3 /srv/jobs/script.py',
});

Feed the script its data through that read-only folder, take the output from stdout, and never put a key in its environment. If a script truly needs the internet, I would rather have a separate service fetch the data first than build an allow-list on day one. Anything touching seller credentials gets a real VM and not a namespace trick, and I will say that is a judgment, not a measurement.

I have not tested a hosted sandbox service this week, so this is opinion and not a comparison. For a team of five with one Linux server, I think a library you run yourself, under an MIT license you can read, beats a vendor account that holds your seller data in someone else’s cloud.

Who should try it: teams already running generated code in a plain subprocess with the same user as the app. That is worse than anything above, and moving off it is cheap. Who should wait: anyone who wants a published escape test before trusting it with customer data. Me, I’d also wait on the Node 24 requirement if the rest of the stack is still on 22.

Next I’m putting bubblewrap and build-essential on a test box, running the deny-all example above, and opening a socket from inside the script to see whether it really fails. If it connects, I will write that down too.

Dominic Plouffe

CTO of Trellis, with exits at FuseTalk, 360pi and DeadHeat. I write about making agents and big data work together safely, and I'm building yaaf, an open-source platform for running teams of AI agents that actually finish the job: yaaf.dplouffe.ca