2026-10-11 16:37 UTC

Brig's maintainers claim its default macOS and Linux microVM execution confines coding agents' host-filesystem and credential access to configured shares and delivered secrets, reducing host exposure without preventing misuse or exfiltration of resources explicitly provided.

state: seedheat: lowuncertainty: mediumknownscott: lowagent-sandboxing micro-vms coding-agents agentic-securityBrig

What is this?

The case describes Brig as a coding-agent sandbox whose maintainers claim default microVM execution on macOS and Linux, with host access limited to configured filesystem shares and delivered credentials. None of the supplied web snippets identifies Brig, its maintainers, or a Brig release or documentation update, so those specifics remain unverified. The snippets do establish related approaches: mvm documents explicit sharing and secrets decisions, while comparisons distinguish hardware microVM isolation from shared-kernel containers. These are contextual parallels, not evidence that Brig implements its claimed boundary or prevents misuse or exfiltration of resources supplied to an agent.

Why it matters to Scott

The containment position is already held in Scott’s SiloOS and Sandboxed Execution pages and implemented in his Bubblewrap boundary for Codex workers; Brig’s claimed host isolation does not establish SiloOS-style mediation of supplied secrets or control of their downstream use. No supplied radar hit tracks Brig itself, but with its implementation unverified and no demonstrated advantage over Scott’s existing boundary, this remains another example of an established pattern rather than a reason to change what he builds or argues.
ip:framework.siloosip:concept.sandboxed-executiondev:technology.bubblewrapradar:coop-coding-agent-vm-isolationradar:bubbleclaude-absent-by-default-sandboxradar:concept.agent-sandboxing
queries asked of Scott's wikis
  • coding-agent harness execution isolation trust boundaries
  • microVM versus container sandbox security tradeoffs
  • agent filesystem permissions explicit project mounts
  • agent credential delivery secrets brokering least privilege
  • sandbox network egress exfiltration authorized resources

Measured heat

now 0 pts/hpeak 0 pts/hcomments 0/hpeers p14momentum: steady2 platformsage 621h
points/hour across evidence · reading as of 2026-10-12 02:59:37.977291+11:00 · deterministic, not a model opinion

How the heat travelled

09-15 19:33 (minted)⭐ origin echo-reconstructedBrig documents default microVM isolation on macOS and Linux, explicit project mounts and credential delivery, an optional weaker container r
Brig maintainers on github (echo) · attributed from hn.story.49716895 · published time unknown
—
09-15 18:41first on hacker news · published · lag ?Running Coding Agents in a Micro VM Sandbox with Brig
gtzi
—
09-15 18:41amplified on hacker newshn.story.49716895
gtzi
peak 2 · 0 comments · 16% of case engagement
09-22 15:16amplified on hacker news 👑hn.story.49802729
spirosoik
peak 9 · 1 comments · 83% of case engagement
09-15 19:20our radar first saw it · lag ?discovery anchor: hn.story.49716895—
pace: p48 vs 1032 stories at the 336h mark (now 621h old) — ahead of acs-local-skill-risk-catalog (1.1x), behind agentgit-accountless-agent-handoffs (0.9x)

Evidence (3) — ⭐ canonical anchor

sourceobjectauthorscorecomments
🟧 hnRunning Coding Agents in a Micro VM Sandbox with Brig
Retrieved article excerpt

Open article · Retrieved 2026-09-15T19:22:04.861944+00:00

# What Brig protects, and what it does not

An agent runs code you did not write. You run it in a sandbox because that
code executes against a machine that holds everything you have. Brig narrows
what "everything" means. On the host, the agent can reach its guest home and
the credentials you gave it. It can also reach any project you name on the
run line. No other host directory is mounted.

No host credential source is read on the run path: the only store a run
opens is Brig's own. See [secrets.md](https://github.com/brig-sh/brig/blob/main/docs/secrets.md). What the agent can reach
over the network is a separate question, with a much weaker answer, covered
below.

If you have found a flaw in one of these boundaries, [SECURITY.md](https://github.com/brig-sh/brig/blob/main/SECURITY.md)
is how to report it privately. The section below on
[things brig does not claim](https://github.com/brig-sh/brig/blob/main/docs/security.md#things-brig-does-not-claim) is the line between a
vulnerability and a known limitation.

## What the agent can reach

The guest can access:

- its guest home, read-write
- the project you name on the run line, read-write at `/work/<name>`
- the credentials you deliver to it
- the internet, on the default `shared` network
- any hostmount volume a profile declares

The guest cannot access:

- any other host directory
- your keychain
- your SSH agent
- your secret manager
- an environment variable a profile's `deny` list refuses

Mounting a project or delivering a credential changes both lists. The agent
can change the real files at those two mounts, not a copy of them. Anything
it can read there it can also send over the network. A credential you deliver
is usable by the agent for as long as it holds it. The agent can misuse it
the same way a person holding it can.

Every hostmount in a shipped profile already lives inside the guest home.
That means no shipped hostmount exposes anything today, a property of the
shipped profiles rather than a guarantee about a profile you write yourself.

A `deny` list only ever covers an environment variable. A `files:` binding
reaches the guest by a different channel, and the list does not see it.

What "the internet" means in practice is in
[Things brig does not claim](https://github.com/brig-sh/brig/blob/main/docs/security.md#things-brig-does-not-claim) below. So is
whether the guest can reach a service bound on the host.

## The boundary

The sandbox is a microVM on both macOS and Linux. On macOS it is booted by
[hull](https://github.com/brig-sh/hull) over Virtualization.framework, which
`brew install --cask brig` brings along. On Linux Brig drives `nerdctl` and
hands the container to the urunc shim (`io.containerd.urunc.v2`), which is the
default rather than the direction. That gives the guest a kernel of its own
there too. `BRIG_CONTAINERD_RUNTIME=runc` asks for a plain container instead, which
shares the host kernel. That is the weaker of the two, and it is something you
have to choose rather than something you get.

Which of them you got is the `ISOLATION` row of the execution envelope, printed
before every boot and by `brig info`:

```
ISOLATION    microVM (hull, hvi backend)
ISOLATION    microVM (hull, vz backend)
ISOLATION    microVM (nerdctl over containerd, io.containerd.urunc.v2)
ISOLATION    container (docker over containerd, runc: the guest shares the host kernel)
```

The row reports what this run resolved: the binary in hand, the backend it
settled on, and the shim it will name. That is not the same as what the
paragraph above promises. Brig may not recognise a shim but could still use it
to boot a sandbox, and Brig cannot establish the isolation that sandbox gets from a
shim name alone. So the row says it cannot tell, instead of claiming the
stronger boundary.

Inside Brig, the guest has your guest home mounted as its home, read-write. Name
a project on the run line and that project is a second host directory, also
mounted read-write, at `/work/<name>`. The agent can change those files too.

Each hostmount volume in the profile is an additional share. Every hostmount volume in
a shipped profile lives inside the guest home already, so nothing extra is exposed
today. That is a property of the shipped profiles, not a guarantee. A
hostmount your own profile declares outside a tmpfs cover is a host path the
guest can see.

Beyond those, the guest does not have your keychain, your SSH agent, your
secret manager, or any other directory on the host. That inaccessibility is
the isolation boundary for everything else. It is also the reason credentials
have to be forwarded in explicitly: the guest cannot fetch them for itself.

## Credentials

**A run reads no host credential source.** Nothing on the
`brig run`, `exec` or `shell` path reaches a keychain item Brig did not write.
Nothing on that path reaches a credential file outside the guest home, or a
host command that produces one. Two host reads do happen, and no setting
turns either off. Brig runs `git config --get` in the directory you invoked it from, for `user.name`,
`user.email` and `github.user`, to resolve the commit identity forwarded into
the guest. If `github.user` comes back empty, it then reads the `user:` line
from the stanza for your git host in gh's `hosts.yml`, under `$GH_CONFIG_DIR`
or `~/.config/gh`. That line names the login that pairs with the forwarded
token. That file usually carries gh's own OAuth token as well. Brig takes the login and
nothing else. `BRIG_GIT_IDENTITY=0`, `BRIG_GIT_CONFIG=0` and `BRIG_GIT_USER`
change what Brig does with the answers, not whether it asks. Your host login
enters Brig's own store once, when you type `brig secret import <profile>`,
and every run afterwards reads only that store:

```
brig run claude-code               # log in inside the sandbox, or:
brig secret import claude-code     # carry the host login in, once
```

A credential reaches the guest by one of two channels, and the profile picks
per secret. `files:` writes it into the guest at the path the agent already
reads. `env:` binds it as an environment variable, for credentials whose
consumer offers no file interface. For an `env.<name>` binding, or the
deprecated `forward:` spelling of one, Brig still reads the named variable
from its own environment. Whatever populates that environment remains a
usable backend for those.

On Linux the store is a Secret Service keyring on your session bus,
gnome-keyring or KWallet ([secrets.md](https://github.com/brig-sh/brig/blob/main/docs/secrets.md#linux)). A host with no
keyring has no store. A profile whose secrets are *optional* degrades there
rather than failing: the run boots and the agent asks for a login. That is
what `claude-code` does. A **required** secret does fail outright,
because there is nowhere on that host to read it from.

Values are re-read on every exec, so a rotated credential is picked up without
restarting the sandbox. Nothing is written into the guest home from the host
for this.

### What file delivery buys, and what it costs

A credential delivered as a file stays out of `/proc/<pid>/environ` and is not
inherited by processes the agent spawns. It can also be rewritten under a
running agent, so a rotated secret can reach a live session, which no
environment variable can. The bytes land on a memory-backed mount covering
the agent's whole config directory, verified to be `tmpfs` with no swap
before anything is written. So there is no path from the credential to your
disk to check.

Weigh these costs before you rely on file delivery. None of them is small
enough to leave implied.

- **Brig stores and hands over a refresh token.** There is no way to give
  Claude Code a working `.credentials.json` without `refreshToken` and
  `refreshTokenExpiresAt`. A file carrying only an access token is *worse*
  than an environment variable, because the agent attempts a refresh, fails,
  and prompts. So a compromised agent inside the sandbox can mint access
  tokens indefinitely, and keeps doing so after the host's own token has
  expired. The compensating argument is real and belongs beside it: the guest
  refreshes for itself, so a long session stops breaking every few hours. It
  is a trade, taken deliberately.
- **Brig's copy is less protected than the item it came from.** The host's
  Claude item is ACL-scoped to the application that wrote it. That is why it
  raises a dialog the first time something else reads it. The copy Brig
  writes carries the default ACL, the same one every secret in
  [the secret store](https://github.com/brig-sh/brig/blob/main/docs/security.md#the-secret-store) carries. The same consequence
  applies: keeping the stored copy low-value is the only real mitigation, and
  a refresh token is not low-value. When you are not using it, delete it:
  `brig secret delete claude-credentials`.
- **The denylist stays env-scoped.** A `files:` binding bypasses the deny check
  entirely, and no name check can fix that. A profile can deliver a metered
  API key inside a `settings.json`, and nothing sees it. That is defensible
  rather than a hole. `deny` exists to catch **accident**, an ambient
  variable swept into the guest because it happened to be in your shell. A file
  binding takes an explicit stored secret and an explicit binding written by
  the profile author. Nobody file-binds an `ANTHROPIC_API_KEY` by mistake. The
  two names on `claude-code`'s denylist are env-shaped by the agent's own
  design, so the guard still covers the channel the risk uses.
- **The stored copy does not rotate.** A credential renewed on the host does
  not update Brig's copy. One *revoked* on the host stays valid in Brig's
  store until you re-import or delete it. Brig warns before boot when the
  stored copy has expired, and names the command that refreshes it. It cannot
  see a revocation at all.

A `0600` file is also still readable by anything running as the agent's uid
inside the sandbox. Files narrow the exposure. They do not draw a boundary.
See [what is still exposed](https://github.com/brig-sh/brig/blob/main/docs/security.md#what-is-still-exposed) below.

Brig applies these rules when it resolves a secret:

- Unset or empty is skipped, so it cannot shadow a value baked into the image.
- A `scheme://` value read from the environment is refused as an unresolved
  secret-manager reference. direnv and friends leave those in the environment
  readily. Forwarded verbatim, it yields "Invalid username or token" in
  the guest, which looks exactly like a broken sandbox. A `value:` literal or
  a value from Brig's own secret store skips this check. Brig put it there
  on purpose, not left behind by a tool that never resolved it. Refusing it
  rejects a perfectly good credential for merely looking like one it is not.
  `BRIG_ALLOW_REFS=1` forwards an ambient reference anyway.
- A variable on the profile's `deny` list is refused, with the reason.

`brig info <agent>` reports the guest's environment, by name, and fails the
same way a run does if a declared secret cannot be resolved. It never
prints a value: a secret-sourced variable comes back annotated, for example
`GH_TOKEN(secret)`, never with the value itself. A credential delivered as a
file is not an environment variable and does not appear in that list at all.

### What reaches host disk

The credential file is the part that does not. It lands on a `tmpfs` mount
covering `~/.claude`, checked to be `tmpfs` with no swap before anything is
written. So `~/.claude/.credentials.json` and the temp file the agent
renames onto it never touch your disk. `brig stop` takes that mount with the sandbox,
which is why an in-sandbox login on this profile does not outlive a stop.

The rest of `~/.claude` is not on that mount. Seven paths under it are
hostmounted, so they live in the guest home on host disk and persist across
boots:

- `settings.json` and `CLAUDE.md`, your permission allowlist and your
  user-level memory, written by hand or by the agent on your instruc
gtzi20
🟧 echo.github ⭐Brig documents default microVM isolation on macOS and Linux, explicit project mounts and credential delivery, an optional weaker container rBrig maintainers——
🟧 hnShow HN: Brig – A MicroVM sandbox for AI coding agents on Mac and Linuxspirosoik91

Interpretation history

Decision trace