magus v0.4.3 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
5 min read

The sandbox

The sandbox confines magus's subprocesses and in-process spells to the workspace plus a curated allowlist of toolchain, cache and system paths, and replaces the child-process environment with a minimum allowlist so secret-bearing variables do not leak to a compromised third-party spell. The sandbox model describes it in full.

It is designed to neutralize the class of supply-chain attacks that have appeared on npm and the VS Code marketplace, where a compromised package or extension reads credentials from disk and exfiltrates them.

What sandbox blocks

  • Reads outside the workspace, its private temp dir, the system trees, the PATH directories and the tool caches and installs the spells declare. This denies ~/.aws/credentials, ~/.vault-token, ~/.ssh/id_rsa, ~/.npmrc, ~/.config/op, ~/.docker/config.json, ~/.kube/config, and the rest of the usual supply-chain targets.
  • Writes outside the workspace, its private temp dir and the tool caches. System paths are read-only, and the shared /tmp is not granted at all.
  • Execs outside the workspace, the private temp dir, the system trees, the PATH directories and the toolchain installs.
  • Inheritance of secret-bearing env vars in child processes: AWS_*, GITHUB_TOKEN, VAULT_*, OP_SESSION_*, NPM_TOKEN, ANTHROPIC_API_KEY, etc. By default only HOME, PATH, USER, locale vars and terminal vars pass through.
  • Writes through magus's bindings to files other tools run code from later: .git/hooks, .git/config, magus.yaml, mise.toml, .envrc, .claude/ and the rest the sandbox model lists.

Without kernel landlock, only what goes through magus's own bindings is checked; see MGS2005.

What sandbox does NOT block

  • Network egress. A compromised spell with no token in its env can still curl attacker.example.
  • In-memory secret theft from magus itself. If magus is holding a secret in memory at the moment a spell runs, landlock cannot help.
  • A child writing a control file. Landlock cannot deny a path inside a grant, so only magus's own bindings refuse those writes.

Turning it on

In magus.yaml:

sandbox:
  mode: best-effort

Or per-invocation:

MAGUS_SANDBOX=best-effort magus run build
magus --sandbox=required run build

best-effort uses kernel landlock where the host has it and falls back to binding-level checks (MGS2005) where it does not. required refuses that fallback, and a kernel below landlock ABI 3, and stops with MGS2012 instead. off is the default. A nested magus inherits its parent's mode and may only strengthen it (MGS2010).

Extending the allowlist

sandbox:
  mode: best-effort
  allow:
    - path: ~/.terraform.d/plugins
      mode: rx
    - path: ~/.cache/bazel
      mode: rw
  env:
    passthrough:
      - GOPATH
      - GOCACHE
      - CARGO_HOME
      - "MISE_*"

mode is ro (the default), rw, rx or rwx; exec is never implied. A passthrough pattern ending in * is a prefix match, and its prefix must be at least three characters ending in _: MISE_* matches everything that starts with MISE_. A bad entry is an error (MGS2004).

Enforcement mechanism

Two layers run together:

  1. Kernel level. On Linux 5.13 or newer, magus starts each child through a launcher: magus re-executed, which applies the policy's landlock ruleset to itself and then execs the command. The child and everything it starts stay confined. magus itself is never confined. No root required.
  2. Binding level. magus's own fs, archive, crypto and http bindings, and Buzz's own os and io, check the policy before touching a path, and the exec binding checks the binary it starts. This gives a friendly MGS2001/MGS2002/MGS2007 error and is the only enforcement on macOS, Windows, or older Linux kernels.

Codes

  • MGS2001: path read denied.
  • MGS2002: path write denied.
  • MGS2003: env vars stripped from child.
  • MGS2004: a sandbox.allow or passthrough entry failed to resolve.
  • MGS2005: landlock unavailable; binding-level checks only.
  • MGS2006: likely PATH-shim manager (mise/asdf/direnv) stripped.
  • MGS2007: exec denied.
  • MGS2008: server socket withheld from sandboxed children.
  • MGS2010: a nested or forwarded run asked for a weaker sandbox mode.
  • MGS2012: the sandbox is required and the kernel cannot confine the run's children.
  • MGS3009: machine budget exhausted.
  • MGS3010: redundant gate deferred.
  • MGS3011: target exceeded its declared timeout.
  • MGS3012: invocation stalled with its project locks held.
  • MGS3013: every build slot held by a step that is itself waiting.
  • MGS3014: gate superseded by a later gate on the same tree.
  • MGS3016: a server call against a workspace that failed to load.
  • MGS3017: a server call against a workspace still loading.
  • MGS3018: a job forked with a directory as a write path.
  • MGS3019: the merge queue's status is required from another integration than its credential's.
  • MGS3020: a --preflight target failed, so the invoked target never started.
  • MGS3021: a --preflight target outside the invoked target's closure.
  • MGS3023: a pipe whose writers loop back into the run reading it.
  • MGS3024: a hook glue call that names no agent host.
  • MGS3026: a merge queue hook flag holding shell syntax rather than a command and its arguments.
  • MGS3027: the merge queue refused a validation run the base's own queue workflow did not start.
  • MGS3028: the merge queue's plan disagrees with what apply reads itself.
  • MGS3029: a run --stdin that reads a malformed plan, or names a shard, target or count the plan does not have.
  • MGS3030: a magus stage upstream of this run in a pipe exited non-zero.
  • MGS3031: a job forked with a declaration claim no footprint can grade.

MGS3015 was retired in 2026-09. It refused a run when every holder of the isolation gate looked stalled, and it read that from a record the gate did not own, so it could not fire for a simple step and did fire for healthy composite ones. The shape it was built for is prevented rather than detected; a hang that escapes that prevention is caught by MGS3012. The number is not reused.

sandboxdiagnosticserror codesMGS2xxxsecuritypermissionslandlocksupply chain
Last updated (c5971189)
Earlier changes on this page (7)

Full history ↗ · Blame source ↗

Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. See workspace.

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Ward

A coded diagnostic that inspects a resolved op and nudges or blocks an anti-pattern before it runs. See wards.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Sandbox

The restricted filesystem and environment a target runs in, so builds stay reproducible and side-effect-free. See sandbox.

Server

The background process a person starts with magus server start. It serves MCP, the console, background jobs and the warm knowledge graph, and adopts nested magus calls into one pool. See server.

Slot

One unit of the pool's capacity. A target acquires the slots it needs to run (most take one) and releases them when it finishes; the pool tracks capacity (total slots), running (acquired), and queued (blocked). See server.

Health

The at-a-glance server state derived from the pool: healthy when the pool is reporting, degraded when it reports an error, down when there is no pool. The dashboard color-codes each state. See server.

Session

An agent host's conversation, by the id the host delivers to its hooks. magus never mints one: a record with no session is unattributed, and the OS user it carries says whose account ran it.

Invocation

One magus process's recorded facts - the targets it finished, their outcomes, the lease it acted as, and the session it ran in when a host delivered one - kept in a repo-scoped store every worktree shares. magus session lists them; the store prunes itself by last-fact age.

Window

The terminal a command runs in. It keys fire-once notices for a caller no host gave a session, and is never recorded as a session.

Job

The unit of delegated work, and one row of the job store: what an orchestrating agent handed out, with its goal, the checkpoint it was cut against, the paths it may write or must not touch, and the one check it runs. A job's holder is either a session, for work an orchestrator handed out, or the server, for its own maintenance. The store records; the agent guard is what reads those facts back when grading a write. See doctrine.

A job is not a run. magus run build web is a run, and no job exists for it. A job causes runs: its check executes as one, and a server job records the invocation of its last one. Jobs are listed with magus ls jobs and in the console's Jobs view; runs are listed in the Runs view.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate.

Conventions

This page uses none of the site's convention markers. The full set is on the conventions page.