dergraf.org ↗NotesProjectsgithub ↗

Canister: An Invitation to Break It and Tell Me Where It Doesn't Fit

· 6 min read

A few months ago I posted a small note on LinkedIn about a holiday project: Canister, an unprivileged Linux sandbox for running code you don’t fully trust – AI-generated scripts, npm install, build steps from a repo you just cloned.

A lot has changed since then, and I mostly stayed quiet about it. A couple of the notes here even slipped out as drafts before I meant to publish them – if you read one early and it changed under you, that’s why. This post is the opposite of an accident. It’s an invitation.

Where it stands

When I wrote the first post, Canister was an experiment held together by enthusiasm. Since then the pieces I was unsure about have actually landed:

  • An L7 egress proxy with a DLP layer that scans outbound traffic for credential leaks – because a network allow-list can’t tell the difference between a legitimate API call and your AWS key base64’d into a header. It’s a second line of defense with real limits, not a hard boundary; if you’re leaning on it, read the limitations section of the DLP docs first.
  • Per-destination egress contracts: one [[host]] block per upstream declares the methods, content types, paths, and body size a destination is allowed to receive. A refused request comes back with a copy-pasteable patch in the response body, so loosening a policy is a paste, not a guessing game.
  • Project manifests: drop a canister.toml at your project root, define named sandboxes, and run them with can up <name>. The choice is remembered instead of retyped.
  • A test suite that boots real Fedora and Ubuntu VMs in CI to verify SELinux and AppArmor enforcement – because you can’t test a security policy in a container that shares the host kernel.

None of these primitives are novel on their own. The bet was always that the composition – namespaces, seccomp, a USER_NOTIF supervisor, and an L7 DLP proxy behind one CLI and a few readable TOML files – could be genuinely useful. I think it’s finally crossed that line. I’ve started reaching for can run in my own daily workflow instead of treating it as a thing I’m building.

The honest gap

Here’s the problem with that: I built this in and for my own world. Elixir, Nix, Linux, the way I install toolchains and lay out projects. The recipe model is shaped by my habits. I genuinely don’t know whether it fits the reflexes of a developer who lives in a different ecosystem – and that’s exactly the feedback I can’t generate on my own.

So I’m asking for two specific kinds of help.

If you live outside the Elixir world

There are recipes for Node, Python, and Go, and for the package managers around them – npm, pnpm, yarn, pip, poetry, uv. Take a real project, not a toy, and run its install or test step inside Canister:

$ can run -r node -- npm install
$ can run -r python -r uv -- uv run pytest

Then tell me where it’s wrong. Is the recipe too tight, so legitimate work breaks? Too loose, so it isn’t really protecting anything? Does it miss a registry, a cache directory, a socket your toolchain expects? can run --monitor shows you what would be blocked without enforcing, which is the fastest way to see where a recipe and your ecosystem disagree. I want those disagreements.

If you break things for a living

This is the feedback I want most. Canister is an unprivileged sandbox that stacks user namespaces, mount isolation, a PID namespace, seccomp BPF, a USER_NOTIF supervisor for argument-level syscall filtering, and an L7 proxy doing TLS-terminating DLP. That’s a lot of surface, and a lot of places I could be wrong.

So: try to break out. Read a path that shouldn’t be mounted. Get a secret past the DLP scanner through an encoding chain I didn’t anticipate. Escape the namespaces, signal a host process, find the syscall I forgot to deny. The design philosophy is fail-closed – if it isn’t, I want to know before someone relying on it does. File an issue, or just tell me how you got out.

And either way: how does it feel?

Security and ecosystem coverage aside, I want to know if the tool is pleasant. Is can run / can up natural, or fighting you? Are the refusal messages actually actionable when something gets blocked? Did monitor mode help you build a working recipe, or did you give up halfway? Ergonomics are where good security tools quietly die, and I’d rather hear it’s annoying than watch people stop using it.

New: a canister.toml composer

Composing recipes by hand means knowing which ones exist and how they layer. To make that easier, there’s now a guided builder at canister.dergraf.org.

Answer a handful of questions – what language, how your toolchain is installed, which editor, where your code lives – and it composes the matching recipes and emits a working canister.toml. Suggested companions are pre-checked and explain themselves on hover; there’s an advanced mode if you’d rather edit the recipe list directly. The manifest is optional: you can drop the file at your project root and can up, or just copy the equivalent can run line and skip the file entirely.

Come kick the tires

The code is at github.com/dergraf/canister, Apache-2.0 licensed. The architecture, configuration reference, and DLP internals are documented in docs/. The composer is at canister.dergraf.org.

If you try it – whether you’re porting a recipe to your ecosystem, hunting for a breakout, or just forming an opinion on whether the ergonomics are any good – I’d genuinely like to hear from you. The best place is GitHub issues: a broken recipe, a false positive, a syscall I forgot to deny, a successful escape, or just “this felt awkward” – all of it is useful, and all of it is welcome.