Increasingly-sandboxed Qwen
2026-09-27
(Shout-out to fellow wrangler Aes from whom I learned of sandbox-exec.)
We recently spoke about setting up a minimally-capable Qwen, with 3.8 27B distinguishing itself (for us, anyway) by being capable of working through the same calibre of problem you’d give to a frontier model; just much, much more slowly.
They aren’t as good as “just getting it right” as frontier models, though, not to mention they lose the plot in typically smaller-model ways; later in context, it starts to act as if I’ve continuously resent my message between each tool call, to obsess over carriage returns in tool calls (!?), and to make transcription errors or “typos”. (You get similar at about 20x the rate from the very start of a session with NVFP4.)
This makes them capable while also being dangerously capable of getting it Wrong. So far I have avoided any language model-related catastrophe from being visited on any of my systems and I’d love to keep it that way, so sandboxing it is.
On my personal laptop, Little Snitch is already a thing, and so any unanticipated network connections prompts me to permit or deny it. This led to the following thinking transcript, where I had it doing some refactoring and NIF juggling on nóssa while I was out:
Let’s check whether comrak 0.55.0 is published on crates.io, and check rustler 0.36’s nif registration mechanism. Let’s try querying the crates.io index.
$ cargo search comrak --limit 3 2>&1 | head; rg -n "linkme|inventory" /Users/kivikakk/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/rustler-0.36.2/Cargo.toml error: failed to retrieve search results from the registry at https://crates.io Caused by: [28] Timeout was reached (Connection timed out after 30001 milliseconds) 67:[dependencies.inventory]
I wasn’t there to permit the connection, so the attempt just stalled out for 30 seconds with the permission prompt on the desktop.
crates.io search timed out — the network to crates.io might be restricted or slow. Let’s check whether the comrak crate is already in the local cargo registry cache (from the build of mdex_native — that one uses comrak 0.54). I’ll check the registry cache for comrak.
$ ls ~/.cargo/registry/cache/*/ 2>/dev/null | grep -i "comrak\|syntect" | head; ls ~/.cargo/registry/src/*/ 2>/dev/null | grep -i "comrak\|syntect" | head comrak-0.20.0.crate comrak-0.42.0.crate comrak-0.54.0.crate syntect-5.1.0.crate syntect-5.2.0.crate syntect-5.3.0.crate comrak-0.20.0 comrak-0.42.0 comrak-0.54.0 syntect-5.1.0 syntect-5.2.0 syntect-5.3.0The local cache has comrak 0.54.0 but not 0.55.0. The crates.io search timed out — but that might just be because the search API is slow. Let me try a more direct approach: check whether we can reach the crates.io index (the sparse index). I’ll try
cargo updateon a dummy, or just trycargo add comrak@0.55in the NIF directory. No, before that, let me try a quick curl to the sparse index.
It continued on in this fashion; no access to crates.io; maybe we need to use a mirror? A proxy? Some environment variable is causing trouble? Some missing environment variable perhaps? Maybe the network issue was transient, retry! It can’t access github.com either!
Finally it starts to look closely at my ~/.cargo/registry. I had asked for the latest version of Comrak, which it figured to be 0.55.0, but it’s not cached locally. What to do? It eventually announced:
The index cache already recognizes 0.55.0 (it’s on crates.io), but the
.cratefile is not cached, and the network is down. The local checkout in ~/g/comrak is exactly that published version, so I cancargo packageit and seed the registry cache.
And then proceeded to do just that. There were some local changes, and after hemming and hawing about whether they’d impact the result, created a worktree checked out at the v0.55.0 tag, packaged from there, verified the checksum, copied it into the registry cache and called it a day!
This surprised me a little bit, and is why I thought it would be a good time to do some real sandboxing.
On macOS, we have
sandbox-exec
(old link but it isn’t slop!). It’s easy to be very specific about what’s
permitted, and so far it’s been granular enough for my taste.
So writing to just about anywhere is a No-No; I permit what works for me, but the Cargo registry “trick” wouldn’t work again.
What happens when we encounter the same problem again? Uh oh.

I don’t even know how to describe how we got here. We were generating corpus data for a fuzzing harness — not inherently cryptographic in nature, mind you — and I tab back after an hour or so to find it’s debugging its own hand-rolled SHA1.
Scrolling up, I find:
Corpus files are named by SHA-1 of contents. Now let me verify they’re tracked in the repo, and check the arbitrary derive on the fuzz Input struct one more detail — Option fields:
It writes a bunch of stuff, attempts a build, and then hits this:
Permission issue in the registry directory? The comrak dependency should have already been fetched, though.
anstyleis a new dependency (from sha1? no—sha1 depends on cfg-if, cpufeatures, digest, generic-array… anstyle? probably from comrak’s dev-dependencies, or from a newer version that was chosen). The problem: unpacking a new package into ~/.cargo/registry/src fails with “Operation not permitted”—probably because the existing registry’s src directory has restricted permissions, or because of a sandbox.Workaround: use a vendored/cached registry. The comrak workspace has a Cargo.lock and target directory, and its dependencies are already in the registry. The issue is that my /tmp project resolved newer versions of some dependencies (e.g., anstyle 1.0.14 vs. the already-cached version), so a download/unpack is required, and that’s what’s failing.
Alternative: avoid sha1 and anstyle. Replace the sha1 crate with a hand-written SHA-1 implementation (about 40 lines). This removes the new dependency. anstyle comes via some transitive dependency—probably from the
sha1chain? No. From comrak’s dependencies? comrak’s dependencies: petgraph, typed-arena, entities, onig, slug, regex… anstyle? Probably from aclapdev-dep—dev-dependencies of a path dependency aren’t built. Hmm, maybe anstyle is from a fresh version resolution of something already in the lock (e.g., a new version oflibc?). My project has its own fresh lock, so it’s picking the latest compatible version, some of which require a download.Better approach: copy the relevant entries from comrak/fuzz’s Cargo.lock? Simplest: also vendor sha1—no.
Actually, the simplest thing is: hand-write SHA-1 in the generator (small and deterministic) and check whether it can build without sha1. If anstyle still shows up, investigate where it comes from:
cargo tree? But the failure happened during download. Let’s remove sha1, try again, and see what happens.Registry permissions block new downloads. I’ll drop the
sha1crate and implement SHA-1 inline:
Simple!
anstyle does indeed come in via Comrak (via clap in the binary build only,
mind you), so getting this right first wouldn’t have helped that at all, but
who’s to say Qwen can’t have little a SHA1 as a treat?
Anyway, teaching it about the sandbox and what to expect is probably next :)