Tooling

NixPak launcher

The Rust binary that assembles the bubblewrap sandbox Dolomite runs its untrusted-input apps in, coding agents included. It reads its whole configuration from the environment, resolves the bind mounts that decide what the container can reach, and reaps its helper processes in the one order that does not deadlock.

Trust boundaryA boundary defends only what it fully encloses. Every crossing is an assumption someone has to justify.
When
2025
Role
Go-to-Rust rewrite of the launcher
Stack
  • Rust
  • bubblewrap
  • Nix

What it does

  • The container gets its own PID and mount namespaces and a /proc that shows only its own process tree; what it can reach is exactly what is bind-mounted in, and nothing else.
  • The boundary is namespace isolation, not the user ID. Same UID, different namespace, so from inside there is no route to the host's process table and no way to read a file that was never mounted.
  • Ported from Go to Rust against a 490-line description of the old binary's exact runtime behaviour, kept as the correctness contract after the Go source was deleted.
  • It has no config file of its own. Environment variables go in and a running container comes out, with the dbus, Wayland, and pasta helpers started and torn down in the order that avoids a teardown hang.

NixPak is the bubblewrap-based sandbox Dolomite runs its untrusted-input apps in: browsers, chat clients, and the AI coding agents that need real access without real trust. The launcher is the native binary that stands between a Nix-generated wrapper script and bwrap, turning a handful of environment variables into a running container whose reachable world is precisely the paths mounted into it.

I rewrote that launcher from Go to Rust. The Go source had already been deleted and replaced by a 490-line account of its exact runtime behaviour, kept as the contract the port had to match: the child-reaping order that deadlocks if reversed, the instance-id hashing, the proxy handshakes. The sandbox it builds is one half of Dolomite's containment; rungate, the typed command gate, is the other.

The full write-up is in Rewriting the NixPak launcher in Rust.

← All work