kennel

A best current practice

Decide what untrusted code is allowed to do — and prove it.

Project Kennel is the disciplined way to confine untrusted code: a threat catalogue you can cite, a design that answers it, and a working Linux runtime that proves the whole thing builds and audits — each derived from the last.

Hand-drawn ballpoint sketch of a kennel: a doghouse with a barred, arched doorway and engineering dimension lines.

Fig. 1 — the kennel

Why

The assumption the whole model rested on has quietly stopped being true.

For years, code under your account was code you wrote, installed, and were watching — so letting it inherit your full authority was fine. It isn't any more. A modern $HOME runs npm and pip trees, build plugins, language servers, and AI agents that write and run code on your behalf: pulled in wholesale, mostly unread, executing with access to your keys, your email, your browsing history, and all of your source.

The kernel has been able to contain this for years — SELinux, AppArmor, Landlock — but those are authored by the OS vendor, not by the person running the code, so in practice they get switched off rather than used. And because nothing separates you from a program under your UID, every shortcut you take is inherited by whatever you run next. The boundary that matters moved inside your account; the means to defend it stayed outside, and out of reach.

So you impose it from the outside instead. That is a kennel: a space you set up in advance, where the code runs freely but reaches only what you chose to put in with it.

The dog isn't the threat. It's a good boy that, left loose while you're out, will earnestly and enthusiastically wreck the house. The code is the same: not malicious, just eager.

The longer argument — why the existing mechanisms fail, why self-sandboxing misses the point, and where an organisation's own policy is meant to attach — is laid out in the case for Kennel.

What "Project" means

A threat catalogue, a design that answers it, and a Linux runtime that proves it out.

The word Project is deliberate. Confining untrusted code is mostly a thinking problem — deciding what to confine, and being able to show the working — and only then a sandbox. Kennel publishes that reasoning as three deliverables, each derived from the one before, so the controls in the code trace back to named risks and the risks trace back to a stated practice.

Deliverable 01

The threat catalogue

An explicit, stable-ID catalogue of the threats untrusted local code poses — real-world incident citations, MITRE ATT&CK and compliance mappings. The durable, portable contribution: a security team can cite it whether or not they run any of our code.

Deliverable 02

The design

The reasoning that answers the catalogue: the adversary model, the trust boundary, and the policy surface a control must satisfy. Substrate-neutral by intent — any team could build a fresh runtime to it. In time, the kind of practice a framework like ISO 27001 would test an organisation for.

Deliverable 03

The reference runtime

A working Linux implementation, composing existing kernel primitives, every control traceable to a threat. The worked proof that the design produces real, auditable software. The runtime is the example — not the point.

The order is the method, not a roadmap. The catalogue names the threats; the design is the reasoning that answers them; the runtime is what you get when you build controls that discharge each one. The traceability — control to threat to principle — is the deliverable. The catalogue and the design don't depend on running our code: cite the catalogue in your own posture, or build a fresh runtime to the design. The Linux runtime is one worked answer, not the only one. Start with the threat catalogue, read the design, or jump to the reference runtime.

When Microsoft shipped MXC in 2026 — "policy-driven, layered isolation and containment" — it confirmed the category is real. Kennel's contribution is the part underneath the tool: the catalogue and the design that say why a given control belongs, written down to be argued with.

Deliverable 03 · the reference runtime

A sandbox with teeth.

Most sandboxes advise; this one enforces. Default-deny throughout — it bounds the files a workload can see, brokers and logs its network, and runs only the binaries you name, on the real kernel and on your actual files. Each capability below traces back to a threat in the catalogue.

your account — one UID, and everything runs under it the boundary you set OUT OF REACH ~/.ssh · keys mail · history your other source the open network THE THREE MOVES walls — bound the files door — broker the network inside — only named tools THE KENNEL the workload, free inside ./project (rw) ~/.cache (ro) node · git broker · audit api.host:443 ✓
Fig. 2 — granted in, everything else out, one audited way through the door
01

A granted view, not a copy

The workload sees only the paths you grant — your actual repository, in place. No image to sync, no files shuffled in and out.

02

Allowlisted egress

Outbound traffic runs through a broker that checks it against a list of permitted destinations and records every connection. Anything not on the list simply can't be reached.

03

Only the binaries you name

Execution is limited to the programs the policy permits, so a compromised tool doesn't have the rest of your toolchain to reach for.

04

Hard to scout

Secrets and unrelated trees aren't just unreadable. Where the platform allows it, they aren't visible at all — there's nothing to map.

05

Policy as signed code

Policies are declarative, version-pinned, and signed. A template can't quietly weaken an invariant; resolution refuses it.

06

An audit trail you can keep

Every security-relevant decision is written to an append-only, structured log — meant for a SIEM, not for grepping after something has already gone wrong.

The runtime is Linux, composing Landlock, cgroup BPF, seccomp and namespaces. It isn't the portable part — the threat catalogue and the design are, and a different team could build a runtime to them on another substrate entirely. Other backends (macOS Seatbelt, FreeBSD jails, a microVM) are possible future work, not a current claim.

Get started · on Linux

From download to a confined agent in three commands.

Project Kennel is pre-release: the reference runtime is built and runs the full vertical unprivileged on stock Linux (kernel 6.17, Landlock ABI ≥ 6), but interfaces and guarantees may still change. The install is offline — prebuilt binaries, no toolchain, no network — and adds one setuid-root helper for the three host-global operations a user namespace cannot reach.

1 · install (root, once)

# unpack a release tarball for your arch, then:
sudo ./install.sh --provision-users

Installs the binaries under /usr/libexec/kennel, the per-user systemd units, the AppArmor userns grant, and the maintainer trust key. --provision-users allocates a subkennel line for every member of the users group.

2 · enable the daemon (each user)

export PATH=$PATH:/usr/libexec/kennel
systemctl --user enable --now kenneld.socket

kenneld is an ordinary user process, socket-activated on first use. No root daemon, no sudo at runtime.

3 · run something inside a kennel

# confine an AI coding agent to this repo, a named toolchain, and a few registries:
kennel run ai-coding-strict -- claude

# a detachable interactive shell (Ctrl-\ d detaches; reattach later):
kennel run interactive -- /bin/bash
kennel attach interactive

A policy is declarative, signed, version-pinned code — not behaviour, but kernel-level constraint. The shipped ai-coding-strict template, in part:

# exec: only these binaries may run — nothing else, even if present
[exec]
allow = ["/usr/bin/node", "/usr/bin/git", "/usr/bin/npm", "/usr/bin/python3", ]

# egress: named destinations through the audited broker; all else denied
[[net.proxy.allow]]
name = "registry.npmjs.org"
ports = [443]
threats.exposed = ["T1.9"]   # each grant traces to a threat in the catalogue

# filesystem, identity, audit, ssh … inherited from base-confined@v1, then narrowed
THE CLI

A small surface

kennel run <policy> -- <cmd> start a workload confined by a policy (interactive runs are detachable) kennel attach <name> reconnect a terminal to a running kennel kennel review <policy> re-pin a workspace trust manifest after edits kennel stop / list stop a kennel; list the running ones kennel policy compile / sign / lint resolve + sign a policy; check the template corpus
STATUS

Built vs. roadmap

Runs today

  • The full unprivileged spawn vertical
  • Egress proxy + cgroup-BPF allowlist + audit
  • AF_UNIX shim · SSH re-origination bastion
  • Masked workspace manifest · pty escape filter
  • Signed templates · policy compiler · lockfile

Designed, not yet built

  • D-Bus & X11 facades
  • Binder cross-instance relay (MCP topology)
  • fs.scrub / fs.home.sanitise
  • kennel diff

Full instructions, the policy language, and the man pages are in the documentation. Builds are on the releases page; the source is on GitHub.

How

Not a new sandbox — a careful arrangement of the mechanisms your OS already ships, kept small enough to read end to end.

Kennel doesn't reimplement the kernel or put a runtime in front of it. It composes the enforcement the platform already provides and wires it to one declarative policy. Everything starts from deny: filesystem access is a specific grant rather than your whole home, outbound traffic is brokered and logged, and execution is limited to named binaries. A small privileged helper does the few steps that genuinely need root, so the workload itself never runs with elevated rights.

Because Kennel governs what a workload can reach rather than the host kernel's attack surface, it sits comfortably underneath stronger isolation when a job calls for it. Run a kennel around a gVisor sandbox or a microVM and you add host-kernel-exploit containment without giving up the egress control, the recon-resistance, or the audit trail. Different layers doing different jobs — stacked only when it's worth it.

Where it fits

Between two options that don't quite work.

The default

Give it everything

Hand the agent or the package your full account and trust that it — and everything it pulled in — behaves. Convenient, and the way most of it runs today.

one bad actor reaches your keys
Heavyweight

Put it in a VM

Strong isolation, but a guest kernel to maintain, an image to keep current, and files moved in and out — enough friction that you stop using it for everyday work.

isolation you won't leave on
Project Kennel

Contain it in place

Per-workload grants on your real files at native speed, brokered egress, and a full audit trail — light enough to leave on. Put a VM underneath only when you need to.

containment you'll actually run

Security

Made to be distrusted, and read.

  • [+]

    Auditable by construction

    A small trusted core, held to a high bar for review. Apache-2.0, with the BPF components under GPL-2.0. You can read every line.

  • [+]

    Signed, pinned policy

    Templates are signature-verified before any field is read, and a policy can't negotiate below the framework's invariants.

  • [+]

    Structured audit

    Append-only JSONL for every decision — policy loads, denied connects, signature failures — ready to ship to a SIEM.

  • [+]

    Coordinated disclosure

    Found a specific, exploitable flaw? Report it privately at security@projectkennel.org. Acknowledged within 72 hours.