brig docs

Guides

Agent profiles

On this page

A profile is a YAML file that tells Brig which image to boot, where the guest home is, and which credentials the guest receives.

The command and its <ref> argument say "agent": brig agent ls, brig agent edit. The file format, the profile directory and BRIG_PROFILE_DIR say "profile".

A profile is optional. Brig boots an agent from an OCI image, and any Linux CLI that runs in one works. A profile saves you from typing the image, the guest home and the credential variables on every run.

Minimal profile #

name: mine
image: ghcr.io/brig-sh/claude-code-stock:root
guestHome: /root
binary: claude
mem: 4096
cpus: 4

The default kind: agent requires these six fields. All other fields are optional.

Create a profile #

Start from the closest existing profile:

brig agent new mine --from claude-code   # writes ~/.config/brig/mine.yaml
brig agent edit mine                     # change the image, and what it forwards
brig run mine

The argument of new is a name. Brig writes the file to your profile directory and refuses a name that contains /.

new also writes the name into the name: field. Brig identifies a profile by that field, not by the file name. The rest of the file is the profile you copied, with a header comment that explains every field.

brig agent edit opens the profile's file in $VISUAL, then $EDITOR, then vi. It works only on a profile that has a file. For a built-in profile, it prints the commands that create a file, and it creates nothing.

To import a file that you wrote by hand, use brig agent import:

brig agent import mytool.yaml

Fields #

Field Required Purpose
name yes Profile name
image yes Guest image to boot
guestHome yes Where the guest home is mounted
kind no agent, shell or gui
binary yes, for kind: agent Agent CLI inside the guest
mem yes Guest memory
cpus yes Guest CPU count
desc no One-line description
secrets no Secrets the profile wants from the store
env no Variables the guest sees
forward no Deprecated spelling of env
files no Credential files the guest sees
volumes no Mounts inside guestHome
deny no Variables never bound
statePaths no Deprecated, superseded by volumes
staleCredentialFiles no Old credential paths to warn about
headless no Agent supports a non-interactive run
guiTitle no Window title for a kind: gui profile
network no Network posture
hypervisor no macOS backend
runtimeBin no Runtime binary to drive
rootfsType no How the guest root reaches the microVM
genericBoot no Boot a plain OCI image
hostConfigDir no Host agent configuration directory
projectPaths no Subdirectories to seed with --skills
onboarding no First-run state file to seed
reserved no Protects a guest home from session names
unpublished no No published image
policy no Policies attached inline

Brig refuses a misspelled field, such as forwards: in place of forward:.

name #

The profile name. It becomes the guest home directory and the sandbox name.

name: mine

Limits:

  • Lowercase letters, digits, dot, dash and underscore only.

image #

The guest image to boot.

image: ghcr.io/brig-sh/claude-code-stock:root

Limits:

  • Brig cannot check the signature of an image outside ghcr.io/brig-sh. It warns on every boot and still boots. See Security.

guestHome #

The absolute path where the guest home is mounted. The state of the agent lands here, so the guest home is the unit that persists.

guestHome: /root

kind #

Sets what brig run does when the guest is up. Default: agent.

kind: shell
Value What brig run does Trailing arguments
agent Execs binary: Passed to binary:
shell Opens a login shell Run as one command
gui Boots the sandbox with a graphical console Refused

brig sh always execs bash -l, or bash -lc for a command. It never execs binary:.

For kind: gui, brig run starts the sandbox and attaches to nothing. guiTitle names the window.

Limits:

  • A kind: shell or kind: gui profile cannot carry policy:, because it has no agent process for an egress rule. Brig refuses the profile at parse time.
  • Only the vz hypervisor backend shows a console. Brig refuses to boot a kind: gui profile on hvi or qemu.
  • The older shell: and gui: booleans still parse. Brig converts them to kind: when it reads the file. See Migration.

binary #

The agent CLI inside the guest, which brig run execs.

binary: claude

Limits:

  • For kind: shell, Brig does not require or read the field. The built-in ubuntu profile sets binary: bash only as documentation.

mem #

Guest memory size.

mem: 4096

Limits:

  • Must be greater than zero.

cpus #

Guest CPU count.

cpus: 4

Limits:

  • Must be greater than zero.

desc #

One line that brig agent ls shows.

desc: our internal agent

secrets #

The names of the secrets that this profile wants from Brig's secret store. secrets: is the list of requirements, and env and files are the bindings.

secrets:
  - gh_token
env:
  - name: GH_TOKEN
    ref: secrets.gh_token

A bare string is the short form of a required secret that you create by hand. secrets: [gh_token] means {name: gh_token, required: true}.

Object form #

secrets:
  - name: claude-credentials
    required: false                     # warn and boot, instead of refusing
    expiryField: expiresAt              # found at any depth; drives the stale warning
    sources:                            # where `brig secret import` looks
      - from: keychain
        service: Claude Code-credentials
      - from: file
        path: ~/.claude/.credentials.json
        hint: run `claude` on the host once to log in
Key What it does
name The name of the secret in the store
required Decides whether the run stops. Absent means required. required: false warns and boots
sources Where brig secret import looks. Also decides which command the error names
field How to read the value of the secret from what a source yields. If absent, Brig stores the value verbatim
expiryField How to read the expiry of the secret from what a source yields

The built-in claude-code profile uses required: false, because the agent can log in inside the sandbox.

field: and expiryField: belong to the secret, not to a source. Every source for one secret must yield the same document shape.

Leave field: out for a secret that is a file. The host keychain blob has the format of the agent's credentials file, so Brig stores it whole and loses no field.

Sources #

Brig tries the sources in order, and the first that exists wins. One secret can name the macOS keychain and the Linux file, so one profile works on both platforms.

A secret with no sources: is one that you create by hand.

Each from: value takes one locator:

from: Locator What it reads
keychain service: A macOS keychain generic-password item. macOS only. The Linux store is a Secret Service keyring, and no source reads from it. See Secrets
file path: A host file, verbatim. Brig expands a leading ~ when it reads the file, so a profile holds no home directory of one host
env var: A host environment variable, copied once at import

A source with the wrong locator is a parse error, for example a keychain source with a path:.

Warning from: env and ref: env.<name> behave differently. Brig reads a ref: on every run. A from: env source copies the value into the store when you run brig secret import, and never reads it again. For a value that expires, use a refs: chain. For this reason, the built-in claude-code profile uses no from: env source.

On macOS, the keychain source answers first, so Brig never reaches the path: ~/.claude/.credentials.json source there. That path is the documented Linux location, not an observed one. On a Linux host with a keyring, import reads that file and stores it in the Secret Service backend.

Limits:

  • A credential whose two host locations differ in shape needs two secrets.
  • A files: binding names one secret, and one path: takes one binding. One portable profile cannot express an agent whose credential file differs per platform. Claude's two locations share a shape.

Missing secrets #

A missing required secret fails the run before Brig creates a sandbox. The error names the secret, the sandbox and the command that creates the secret:

$ brig run mine
brig: missing secret "gh_token" needed by the brig-mine sandbox -- create it first with: brig secret create gh_token

If more than one secret is missing, one error names all of them:

brig: missing 2 secrets needed by the brig-mine sandbox:
  gh_token: create it first with: brig secret create gh_token
  npm_token: create it first with: brig secret create npm_token

A missing optional secret gives a warning, and the run boots. The warning names the secret and the command that supplies it:

$ brig run claude-code
brig: claude-code runs without 1 secret
  ○ claude-credentials  → brig secret import claude-code
                          ↳ run `claude` on the host once to log in

For a secret with no sources:, the message names brig secret create, because import cannot fill it:

brig: claude-code runs without 1 secret
  ○ gh-token  → brig secret create gh-token
                ↳ export GH_TOKEN before running brig, or store one: gh auth token | brig secret create gh-token
Linux hosts without a keyring

On Linux, the store is a Secret Service keyring. A host with no session bus, or with nothing that answers on it, has no store.

Secret Result on a host with no store
Required Every run fails, in the same way as any missing required secret
Optional No warning. The run boots

To change the result, install a keyring. This is why the built-in claude-code profile boots on Linux.

env #

The variables that the guest sees, and the source of each value: a literal, a stored secret, or Brig's environment.

env:
  - name: GH_TOKEN
    ref: secrets.gh_token
  - name: CI
    ref: env.CI
  - name: EDITOR
    value: vi

Each entry has a name: and one source:

Key Source
value: A literal, for configuration that is not a credential
ref: secrets.<name> The named secret in Brig's store
ref: env.<name> The named variable in Brig's environment
refs: A list of refs. The first that resolves wins

ref: is a refs: of length one. A binding carries one of the two spellings.

env:
  - name: GH_TOKEN
    refs: [env.GH_TOKEN, secrets.gh-token]   # shell override first, store second

Brig drops a name that a profile binds from the ambient forward. With a bare ref: secrets.gh-token, the value from GH_TOKEN=$(gh auth token) brig run claude-code does not reach the guest, and BRIG_FORWARD_ENV cannot restore it. The chain above keeps the shell override and adds the store as a fallback. The built-in claude-code profile binds GH_TOKEN this way.

If an earlier env. element resolves, Brig does not need the later secrets. element. A run that the environment satisfies does not open the store or the keychain.

Limits:

  • An entry must have one of value: or ref:. Brig refuses an entry with both or with neither.
  • A ref namespace other than secrets. or env. is a parse error. The error names the two that exist.
  • A secrets.<name> ref whose name is absent from secrets is a parse error.

Runtime variables #

The runtime reads HOME, PATH, TMPDIR and XDG_* for itself. Brig passes their guest values on the runtime's command line as NAME=value. See Security.

A binding for one of these names takes value: or ref: env.<name>. A stored secret never goes on the command line. Brig refuses a binding that resolves from the secret store when the sandbox boots or execs.

BRIG_FORWARD_ENV #

BRIG_FORWARD_ENV overrides which variables Brig carries in from its environment. It replaces only the set that comes from the environment. A ref: secrets.<name> binding does not change.

Sandboxes served by brigd #

For a sandbox that brigd serves, an env.<name> ref resolves against the environment of the daemon, not of the shell that sent the request. See brigd.

forward #

Deprecated. A list of variable names to carry in from Brig's environment. Brig converts it to an equivalent env: binding when it reads the file. See Migration.

forward:
  - MYTOOL_TOKEN
  - GH_TOKEN

brig agent show --json always prints the env: form, because JSON export marshals the parsed profile. Plain YAML export returns the file as written.

files #

Credential files that the guest sees. Each entry names a stored secret and the path under guestHome where Brig writes it.

files:
  - ref: secrets.claude-credentials
    path: .claude/.credentials.json    # relative to guestHome
    mode: "0600"                       # default "0600"; quoted, see below
Key What it is
ref The stored secret, as secrets.<name>
path Target path, relative to guestHome
mode File mode, as a quoted string. Default "0600"

Use files: where the agent can read a credential from a file, and env: where it cannot. A file stays out of /proc/<pid>/environ. The processes that the agent spawns do not inherit it. Brig can rewrite it under a running agent, so a rotated secret can reach a live session.

The agents that use GEMINI_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY and CURSOR_API_KEY read them only from the environment. You can bind one secret through both channels. The exposure is then the union of the two, so do it only when something reads both.

Limits that the parser enforces:

  • ref: only. Brig refuses refs: and env.<name>.
  • mode: is a quoted string. YAML reads an unquoted 0600 as decimal 600, which is 0o1130.
  • The target must be inside a tmpfs volume. A hostmount under that volume must not bind the target back out. Brig refuses this at parse time. See volumes.
  • One binding per path:.

Other limits:

  • field: on a file-bound secret writes a bare token where the agent expects a document. The agent then tries a refresh, fails, and prompts. Use field: only when the agent reads a one-line file.
  • An unresolved binding leaves no file, no mount and no empty target.
  • brig secret push, for rotating a file binding in a running sandbox, is not implemented yet. A re-run rewrites the file under a live agent.

volumes #

The mounts inside guestHome, one primitive per entry.

volumes:
  - kind: tmpfs
    path: .claude               # memory-only: nothing written here reaches the host
    size: 512m                  # optional, default 64m
  - kind: hostmount
    path: .claude/sessions      # ... except these, kept across boots
  - kind: hostmount
    path: .claude/projects
  - kind: hostmount
    path: .claude/history.jsonl
    file: true                  # the target is a file, not a directory
kind: What it does
tmpfs Covers a directory so that nothing written under it can reach your disk
hostmount An exception: one path bound back out to the same path in the guest home
volume Reserved for a named volume that several sandboxes share. Parsed and refused as not yet supported
Key Applies to What it is
path both Path inside guestHome
size tmpfs How big the tmpfs can grow. Default 64m
file hostmount true when the target is a file, not a directory

A tmpfs leaves a files binding no path from the credential to the host. Brig checks this: the covered path must read as tmpfs and /proc/swaps must be empty. If not, the run stops before Brig hands over a credential.

Order in the file does not matter, because Brig mounts parents before children.

size: is a ceiling. At the ceiling, tools in the guest get ENOSPC, and nothing on the host watches for it. A directory of configuration needs less than the home of an agent, where edit history and per-job scratch accumulate.

Limits:

  • A hostmount must be nested under a tmpfs. Otherwise it is a parse error.
  • Brig refuses source: on a hostmount. Its source is implicit.
  • A hostmount takes no size:. It is as large as the guest home it comes from.
  • size: must be a number optionally followed by k, m or g. Brig refuses anything else at parse time.

Warning State that an agent writes under a tmpfs-covered path with no matching hostmount does not survive brig stop.

The built-in profiles differ here:

Profile Hostmounts under .claude Result
claude-code Includes .claude/skills and .claude/plugins A --skills copy there survives a stop
claude-desktop settings.json, CLAUDE.md, sessions, projects, plugins and history.jsonl .claude/skills is not kept. Anything the bundled desktop app writes there is lost at shutdown

claude-code and claude-desktop are the only two built-in profiles that declare volumes:. The other six persist everything under guestHome to host disk, with nothing memory-only. codex, cursor, gemini, grok and opencode declare only the deprecated statePaths:. ubuntu declares neither.

deny #

Variables that Brig never binds, whatever env or forward says.

deny:
  - MYTOOL_ADMIN_KEY   # lets the agent reconfigure the account

Some provider variables outrank the credential that you want the sandbox to use. For example, Claude Code prefers ANTHROPIC_API_KEY to its subscription credential. If Brig forwards it, the sandbox moves from your subscription to metered API billing and does not tell you. Put each variable of this type in deny.

Brig refuses a denied variable with an explanation. BRIG_ALLOW_DENIED=1 overrides the refusal, for a user who wants metered billing.

Limits:

  • deny applies in the same way to a value that arrives by ref:.
  • deny guards the environment channel only. Brig does not check a files: binding against it, so a profile can deliver a metered key inside a settings.json and nothing detects it.

statePaths #

Deprecated, superseded by volumes. It still parses. See Migration.

statePaths:
  - .config/mytool

Limits:

  • Declaring it with volumes: is an error, not a merge.

staleCredentialFiles #

Paths where an older wrapper wrote a credential. Brig never writes a credential there. If Brig finds one, it warns and does not delete it.

staleCredentialFiles: [.claude/.credentials.json]

headless #

States that the agent supports a non-interactive run.

headless: true

guiTitle #

The window title for a kind: gui profile.

kind: gui
guiTitle: Claude Desktop

network #

The network posture of the sandbox. A new sandbox defaults to isolated.

network: isolated
Value Meaning
shared One network for the sandboxes that use it
isolated A network for this sandbox only
offline No route out

See Policies for the postures.

An existing sandbox keeps its recorded posture. BRIG_NETWORK, --network and --offline override the profile and a recorded posture.

The vz and qemu backends, and older sessions
Case Result
No posture named, on hull's vz or qemu backend Falls back to shared, and Brig reports it
Explicit isolated on vz or qemu Refused. Those backends cannot enforce isolation
Existing session with no recorded posture Keeps the posture inspected from the runtime

If you copy a profile that names isolated and change its backend to vz or qemu, change the network to shared too.

hypervisor #

The macOS backend to boot on. Linux ignores the field, because the shim decides.

hypervisor: hvi
Value Notes
vz The default when the field is absent. The only backend with a graphical console
hvi Six of the eight built-in profiles say hvi
qemu

BRIG_HYPERVISOR overrides the field.

runtimeBin #

The runtime binary to drive, in place of the one on PATH. Brig expands ~.

runtimeBin: ~/bin/hull

This field describes your machine, so do not share it in a profile. Use it to pin a profile to a runtime build that you work on.

BRIG_RUNTIME_BIN overrides the field.

rootfsType #

How the guest root reaches the microVM: block, virtiofs or 9pfs.

rootfsType: block        # room to apt install

If the field is unset, the runtime picks its default. That default suits a profile that only runs an agent. If the sandbox installs packages, set block. The sandbox then has a writable disk in place of a share sized to the image.

genericBoot #

States that the image is a plain OCI image such as ubuntu:latest, with no kernel and no urunc metadata. The runtime supplies the kernel and initrd and boots the image unmodified, on macOS and Linux.

name: plain-ubuntu
image: docker.io/library/ubuntu:latest
guestHome: /root/work
kind: shell
binary: bash
genericBoot: true
rootfsType: block        # room to apt install
mem: 4096
cpus: 2

The image's argv, environment, hostname and mounts are restored before the guest switches root.

Two OCI annotations carry the kernel and the initrd. Both are host files and never come from image metadata, so an image cannot name a file on your machine. The kernel file is Image on arm64 and bzImage on x86_64. The guest agent that brig sh talks to comes from the initrd, not from the image.

If the kernel and initrd are missing, Brig fetches them once, on the first brig run of a genericBoot profile.

hull takes the two annotations on its command line.

The kernel and initrd are in hull's store, and Brig asks hull for their location.

hull fetches them.

urunc reads the two annotations from the OCI spec of the container, which nerdctl passes through. This needs the urunc that the runtime bundle builds, because no urunc release reads the pair. See Runtimes.

Brig looks for the kernel and initrd in ~/.local/share/brig/assets.

Brig fetches with oras. If oras is not installed, Brig says so and tells you what to fetch.

genericBoot needs nerdctl, not docker. Docker does not pass OCI annotations to the runtime, so a sandbox booted through it has no kernel. Brig refuses before the boot.

Variable Effect
BRIG_BOOT_ASSETS Overrides where Brig looks for the kernel and initrd, on both platforms. Turns the fetch off
BRIG_BOOT_ASSETS_REF Pins a specific bundle in place of the current one for your platform

The bundle is published as the OCI artifact ghcr.io/nofireai/hull-assets, one tag per guest platform. It pulls anonymously. The repository that builds it is not public. To do the same fetch by hand:

oras pull ghcr.io/nofireai/hull-assets:<os>-<arch> --output <dir>

See Guest image for what genericBoot means for the image.

hostConfigDir #

The directory of your agent configuration on the host. Brig uses it only when the run passes --skills or sets BRIG_SKILLS=1.

hostConfigDir: ~/.claude
projectPaths: [skills, plugins]

Limits:

  • hostConfigDir and projectPaths are required together.
  • Only claude-code declares them, so --skills does nothing on the other seven built-in profiles.

projectPaths #

The subdirectories of hostConfigDir that Brig seeds into the guest home, under the same condition.

hostConfigDir: ~/.claude
projectPaths: [skills, plugins]

Limits:

  • Required together with hostConfigDir.

onboarding #

A first-run state file to seed.

onboarding:
  file: .claude.json
  seed:
    hasCompletedOnboarding: true
    hasTrustDialogAccepted: true
  trustKey: [projects, hasTrustDialogAccepted]
Key What it does
file The agent's state file
seed The flags to write
trustKey The two JSON levels around a directory name, for an agent that records trust per directory

Some agents ask a first-run question that is not authentication, such as a login method that opens a browser. The microVM has no browser. A few non-secret flags in the agent's state file answer the question.

Brig sets trustKey for the directory that each run starts in, resolved to the git repository root as the guest sees it.

Limits:

  • Brig writes seed only when file does not exist. An existing file belongs to the agent.
  • Brig never seeds anything that contains a credential.

reserved #

Marks a profile whose guest home a session name can otherwise map onto.

reserved: true

The built-in claude-desktop profile sets it. That profile owns the guest home of the Desktop app, so Brig refuses brig run claude@desktop. Without the field, that command puts a Claude Code session there.

The trailing word of the name is reserved too. claude-desktop reserves both desktop and claude-desktop.

Warning A profile named my-codex with reserved: true reserves codex and my-codex. Brig then refuses brig agent import of any other profile named codex as a collision.

unpublished #

States that the profile ships without an image. brig run reports this and stops before it reaches the registry, where the pull fails with a 404 that looks like an outage.

unpublished: true

To run the profile anyway, pass --image with an image that you built. brig agent ls marks the profile (no published image). cursor is the only built-in profile with the field.

policy #

Names of policies attached inline to this profile. Every run carries all of them, together with the policies attached separately by name. See Policies.

Limits:

  • Not allowed on a kind: shell or kind: gui profile.

Worked example #

This profile runs a CLI called mytool from your image:

# The vendored CLI needs a bigger guest than the default.
name: mytool
desc: our internal agent
image: ghcr.io/example/mytool:arm64
guestHome: /home/mytool
binary: mytool
forward:
  - MYTOOL_TOKEN
  - GH_TOKEN
deny:
  - MYTOOL_ADMIN_KEY   # lets the agent reconfigure the account
statePaths:
  - .config/mytool
headless: true
mem: 8192
cpus: 4
brig agent import mytool.yaml
MYTOOL_TOKEN=$(pass show mytool/token) brig run mytool

forward: and statePaths: here are the deprecated spellings.

The image is outside ghcr.io/brig-sh, so Brig warns on every boot. See image.

Building the image #

Brig does not build the image. bring-your-own-image.md documents how to build one. The images of the built-in profiles are open source at brig-sh/community-images.

Guest image is the full contract: every binary that Brig execs inside the guest, and the account it expects. It also names a script that checks an image that you built.

Profile directory #

The eight built-in profiles are embedded in the binary, so brig run claude needs no install step.

Your profiles are one file each in $XDG_CONFIG_HOME/brig. The default is ~/.config/brig. The directory is flat: ~/.config/brig/claude-code.yaml. BRIG_PROFILE_DIR overrides the location.

The directory starts empty. Only three commands write to it:

Command Overwrites an existing file
brig agent import
brig agent export <agent> <name> Only with --force
brig agent new <name> --from <agent> Only with --force

The location follows the XDG Base Directory Specification, version 0.8. An empty XDG_CONFIG_HOME counts as unset. Brig ignores a relative one as invalid, because a relative value can give a different set of profiles in each project directory.

Listing profiles #

brig agent ls lists embedded and file-backed profiles together in one namespace. It marks the origin of each:

Marker Meaning
none Embedded
(file) A profile that exists only as a file
(file, overrides built-in) A file that shadows an embedded profile

A file can take the name of a built-in profile. Use this to pin your image for a profile that Brig knows.

Duplicate names #

The file name and the name: inside the file can differ, so two files can declare one name. Brig reports the collision and says which file won. See Confirmation for how brig agent rm treats them.

Settings from older versions
  • BRIG_TEMPLATE_DIR is the older name for BRIG_PROFILE_DIR. It works until v0.4.0.
  • Brig does not read ~/.config/brig/templates and migrates nothing. These files name credential variables, and Brig cannot know which ones you still want. Brig reports files left there on every invocation. Move them with brig agent import.

Built-in profiles #

Profile Alias Kind Image Network
claude-code claude agent ghcr.io/brig-sh/claude-code-stock:root isolated
claude-desktop desktop gui ghcr.io/nofireai/urunc-claude-desktop:aarch64 shared
codex agent ghcr.io/brig-sh/codex-stock:root isolated
cursor agent, example profile ghcr.io/brig-sh/cursor:latest unset
gemini agent, example profile ghcr.io/brig-sh/gemini-stock:root isolated
grok agent, example profile ghcr.io/brig-sh/grok-stock:root isolated
opencode agent, example profile ghcr.io/brig-sh/opencode-stock:root isolated
ubuntu shell docker.io/library/ubuntu:latest isolated

Built-in profiles prints the full YAML of each one.

"example profile" is the desc: of the profile, and brig agent ls prints it. claude-code and codex carry no such marker.

Kinds. brig agent ls lists all eight, although brig agent --help calls the group "the agents you can run". claude-desktop opens a window, and ubuntu opens a shell.

Images. The table states what each profile declares, not whether the image pulls today. Brig expects you to be able to pull seven of the eight images. cursor declares unpublished: true.

Signatures. claude-code, codex, gemini, grok and opencode are Brig's multi-architecture builds under ghcr.io/brig-sh. claude-desktop and ubuntu are outside it, at ghcr.io/nofireai/ and Docker Hub, so Brig warns on every boot. See image. The claude-desktop image is also single-architecture, aarch64 only.

Network. The six isolated profiles use hvi. claude-desktop uses shared because its GUI requires vz. cursor leaves the posture unset: Linux and hvi isolate, and vz and qemu fall back to shared. These are defaults for new sandboxes. See network for existing sessions.

Export and import #

brig agent export claude-code                # prints to stdout
brig agent export claude-code mine           # ~/.config/brig/mine.yaml
brig agent export claude-code mine --force   # ...overwriting what is there
brig agent export claude-code > ./mine.yaml  # a copy somewhere of your own
brig agent export x | brig agent import -

With a destination, export writes the file as Brig ships it, comments included. Import also stores your bytes as written, so your comments and your ordering stay.

An exported built-in profile carries its explicit network: value. Before you change its backend to vz or qemu, see network.

Export writes YAML. If a program reads profiles, use brig agent show --json. Import reads both YAML and JSON.

Destination names #

The destination is a name, never a path. For a copy in another location, export to stdout and redirect it.

Brig refuses these destinations, and each refusal names the collision:

Destination Why it is refused
A path, or a typo that looks like one Brig writes only to the profile directory
A built-in alias, such as claude A profile named claude wins the lookup over the alias. Every brig run claude then means the copy, and the built-in profile is reachable only as claude-code
A name that a reserved profile owns The same reason

For example, Brig refuses brig agent new claude --from claude-code.

Inspecting a run #

brig info <profile> reports what the guest receives. It reports names only, never a value. It annotates a variable from the secret store with (secret). An ambient or literal variable has no annotation.

No test pins the wording, so treat this as the shape of the output:

$ brig info mine
brig: workspace /Users/you/brig/mine (sandbox brig-mine)
brig: runtime hull (/opt/homebrew/bin/hull)
brig: image ghcr.io/brig-sh/claude-code-stock:root (pull missing)
brig: forwarding to guest:
brig:   GH_TOKEN(secret)
brig:   CI
brig:   EDITOR
brig: guest git over HTTPS: off (BRIG_GIT_CONFIG=1 to enable)

No path through this command reads a value, and a test fails the build if a value reaches the output.

brig info resolves secrets as a run does. If a secret is missing from the store, it fails as brig run does.

BRIG_ENV_ARGV #

BRIG_ENV_ARGV=1 puts an ordinary forwarded variable on the command line of the runtime. It is for a runtime build that does not take a bare --env KEY.

It has no effect on a value bound from the secret store, because the host durably logs the argv of every exec. On a runtime build that needs BRIG_ENV_ARGV=1, a stored credential does not arrive.

Removing a profile #

brig agent rm mytool

The argument is a profile name, not a file name. rm resolves it through the merged set, so it takes aliases and finds the file under any file name.

Built-in profiles are compiled in and cannot be removed. To shadow one, import a profile of the same name.

Confirmation #

File that rm resolves to What rm does
The file you named, such as mytool.yaml for brig agent rm mytool Removes it without asking
A file reached through an alias Names the file and waits for a y
A second file that declares the same profile Names the file and waits for a y
A file renamed by hand Names the file and waits for a y

If more than one file declares the name, rm removes all of them.

-y answers in advance, as in brig secret delete:

brig agent rm claude -y   # the alias resolves to claude-code's file

If there is no terminal to ask on, rm refuses and says so.

Existing sandboxes #

rm refuses while a sandbox of the profile exists, running or stopped. It names the brig rm <ref> command for each one.

Remove those sandboxes first. After the file is gone, brig rm <ref> cannot reach them. You must then use brig rm --all, or create a new profile of the same name.

rm does not refuse an override of a built-in profile, because the name still resolves to the built-in profile.

How rm finds the runtime

To find sandboxes, rm asks the runtime of the profile what it holds.

Case Runtime that rm asks
Brig has recorded a session of the profile The profile's runtimeBin
No session recorded yet The runtime on PATH

So rm never executes the runtimeBin of a profile that you imported and never ran.

If Brig cannot ask, rm keeps the file and names the cause. Correct that setting first. The causes are:

  • BRIG_RUNTIME is set to a runtime Brig does not know.
  • runtimeBin points at nothing, on a profile Brig has run.

With no runtime installed, there are no sandboxes, and rm continues.

Type a command, a flag or an error message.