Launch Claude Code with isolated config directories per profile. Each profile stores its own credentials and session history, so you can maintain multiple Claude accounts without re-authenticating.
- uv — Python package manager used to install and run
claude-profile - claude — Claude Code CLI must be installed and on your
PATH
Sandbox mode (optional) additionally requires podman
with the krun runtime — sudo dnf install crun-krun on Fedora — and libkrun >= 1.8.
From GitHub (no clone required):
uv tool install git+https://github.com/quickvm/claude-profileFrom a local clone:
uv tool install .claude-profile add <name> # create a new profile
claude-profile add <name> --sandbox # create a profile that runs in a microVM
claude-profile list # list all profiles, auth status, and sandbox mode
claude-profile remove <name> # delete a profile
claude-profile links <name> # inspect or change directory symlinks
claude-profile sandbox <name> # turn microVM mode on/off for a profile
claude-profile build # build the microVM sandbox image
claude-profile sandbox-skill # (re)generate the in-sandbox tools skill
claude-profile <name> [args...] # launch claude with the given profileadd copies settings.json and CLAUDE.md from ~/.claude into the new profile directory
and symlinks statusline.sh to ~/.claude/statusline.sh, so every profile, sandboxed or
not, runs the one status line script. It then prompts whether to symlink commands/,
skills/ and hooks/ to your global ~/.claude directories (default: yes). Decline to keep
those directories isolated per profile. Link hooks/ if your settings run hooks from
~/.claude/hooks/: inside the sandbox ~/.claude is the profile directory, so an unlinked
guard hook is missing there and fails open.
claude-profile add work
# Link 'commands' to ~/.claude/commands? [Y/n]:
# Link 'skills' to ~/.claude/skills? [Y/n]:Use links to inspect or change how commands/, skills/ and hooks/ are connected after a
profile is created:
claude-profile links work # show link status for all dirs
claude-profile links work commands # show link status for commands/ only
claude-profile links work --link # symlink all dirs to ~/.claude
claude-profile links work commands --link # symlink just commands/
claude-profile links work --unlink # isolate all dirs
claude-profile links work skills --unlink # isolate just skills/claude-profile add work
claude-profile add personal
claude-profile work # launch claude with work profile
claude-profile personal --resume # pass args through to claudeAdd aliases to ~/.bashrc:
alias claude-work='claude-profile work'
alias claude-personal='claude-profile personal'Settings every profile should have, such as hooks, can live in one file instead of being
copied into each profile's settings.json:
~/.claude-profiles/shared-settings.jsonEvery launch passes it to claude with --settings. claude merges it over the profile's own
settings, and hooks from both run. {profile} anywhere in the file becomes the profile's
name, so a hook can tell profiles apart:
{
"hooks": {
"Stop": [{"hooks": [{"type": "http",
"url": "http://127.0.0.1:8080/hook?profile={profile}"}]}]
}
}In the sandbox, HTTP hooks aimed at 127.0.0.1 or localhost are re-pointed at the host
(inside the VM, 127.0.0.1 is the VM itself), so local hook receivers hear from sandboxed
sessions too. Passing your own --settings skips the shared file for that launch.
Sandbox mode runs Claude Code inside a lightweight microVM (podman + the
krun runtime) instead of directly on your host.
The agent gets its own kernel behind a hypervisor and sees only the directories mounted in,
so you can turn it loose with --dangerously-skip-permissions without it touching the rest
of your machine. That flag is added automatically in sandbox mode.
sudo dnf install crun-krun # Fedora: podman + krun runtime (libkrun >= 1.8)
claude-profile build # build the sandbox image
claude-profile add work --sandbox # mark a profile as sandboxedbuild builds the image from a Containerfile shipped with the tool: Fedora + claude,
uv, git/ripgrep/fd/jq/yq, python (pyyaml/jinja2), openssl/make/trash, and dnf-scoped
passwordless sudo. Edit it for more baked-in tooling and rebuild; override the image name
with CLAUDE_PROFILE_SANDBOX_IMAGE.
You don't have to rebuild the image to keep the sandbox current. If your host's Claude Code
came from the native installer (claude.ai/install.sh), that binary is self-contained, so
each launch mounts the host's current version into the VM read-only and runs it — the
sandbox is on whatever version you are, and it follows along when Claude Code updates
itself on the host. The in-VM auto-updater is switched off, since the VM is thrown away at
exit and an update would only be discarded.
The binary is copied to ~/.local/share/claude-profile/claude/<version> first (mounting it
would otherwise SELinux-relabel your real install), which happens once per version, not
once per launch. If your claude came from npm or a distro package instead, the VM runs the
version baked into the image and claude-profile build is how you update it.
On launch the agent is told (via the system prompt) that it's in the sandbox, and the
bundled sandbox-tools skill explains how to self-provision. Inside the VM it can:
sudo dnf install -y <pkg>— passwordless sudo coversdnfandpodman. Either one amounts to root inside the VM, so the sudo rules are a convenience, not a boundary; the VM is the boundaryuv tool install <tool>/uv run --with <lib> …— no root needed
Installs are per-session (the VM is ephemeral). For a tool you need every time, add it to
the Containerfile and rebuild, or build a derived image:
# ~/my-sandbox/Containerfile
FROM claude-profile-sandbox:latest
USER root
RUN dnf install -y <your-package> && dnf clean all
USER appuser
RUN uv tool install <your-tool>
USER root # MUST end as root: the entrypoint starts as root to map your UID and set
# up agent-socket dirs. A non-root final USER silently breaks SSH/GPG forwarding.podman build -t my-sandbox ~/my-sandbox
export CLAUDE_PROFILE_SANDBOX_IMAGE=my-sandbox # claude-profile uses it automaticallyclaude-profile build builds the image CLAUDE_PROFILE_SANDBOX_IMAGE names, so with that export
it would replace my-sandbox with the plain image. Rebuild the base under its own name, then
yours:
CLAUDE_PROFILE_SANDBOX_IMAGE=claude-profile-sandbox:latest claude-profile build
podman build -t my-sandbox ~/my-sandboxThe skill's "already installed" list is generated from the image — after changing the
Containerfile, run claude-profile sandbox-skill (or --check to catch drift).
A sandboxed profile launches in a microVM on every run — same command as always:
cd ~/src/myproject
claude-profile work # boots a microVM, mounts this directory, runs claudeEach launch is its own throwaway microVM, so you can run many at once. This fits a per-worktree workflow — one terminal per worktree:
wt switch feature-a && claude-profile work # microVM #1, sees only this worktree
wt switch feature-b && claude-profile work # microVM #2, isolated from #1The launcher mounts the current directory's git work tree at its real path, read-write: the
whole repo when you start in a subdirectory (the session still starts in that directory),
and just the directory outside a repo. It refuses when that would be your home directory or
anything above it (such as /), which would expose your whole home directory, or would
overlap the profiles directory and its credentials. It also mounts
the repo's git common dir when it lives outside the work tree, so git and wt work inside
the VM and each
worktree keeps its own session history. A profile's credentials and config are shared
across its VMs, just as they already are across host terminals. If the profile links
commands, skills or statusline.sh to your global ~/.claude, those targets are
mounted read-only into the VM so the links resolve there too, as is your ~/.gitconfig
(so git identity and signing config apply).
Flip an existing profile between host and microVM (its credentials carry over either way):
claude-profile sandbox personal --on # personal now launches in a microVM
claude-profile sandbox personal --off # back to running on the host
claude-profile sandbox personal # show current modeOverride per launch without changing the profile's default, via CLAUDE_PROFILE_SANDBOX:
CLAUDE_PROFILE_SANDBOX=0 claude-profile personal # this run on the host
CLAUDE_PROFILE_SANDBOX=1 claude-profile personal # this run in a microVMAliases make both modes one word:
alias claude-personal='claude-profile personal' # profile default
alias claude-personal-host='CLAUDE_PROFILE_SANDBOX=0 claude-profile personal'Authenticate inside the VM the first time — claude-profile work /login prints a URL to
open in your host browser; the token is saved in the profile dir and reused afterward. Or
supply an API key, which is forwarded into the VM:
claude-profile env work --set ANTHROPIC_API_KEY=sk-...To use your SSH keys inside the sandbox (git over SSH, commit signing, ssh to servers), enable agent forwarding:
export CLAUDE_PROFILE_SANDBOX_SSH_AGENT=1 # globally, or set it per launch
claude-profile personalThis forwards your active agent ($SSH_AUTH_SOCK) and, if present, the 1Password agent
(~/.1password/agent.sock) into the VM — hardware/security keys included — so ssh-add -l
and git push work there. Dead agent sockets (e.g. a stale gnome-keyring stub) are skipped.
Inside the VM the agents appear as /run/claude-sandbox/ssh-agent-0.sock, -1.sock and so
on, agents holding keys first, and SSH_AUTH_SOCK points at ssh-agent-0.sock. To use the
other one, export SSH_AUTH_SOCK=/run/claude-sandbox/ssh-agent-1.sock.
Host-key checking persists per profile: your ~/.ssh/known_hosts is mounted read-only for
verification, and host keys ssh accepts inside the VM are saved to a per-profile
known_hosts that carries over to the next launch — your real ~/.ssh/known_hosts is never
modified.
A microVM has its own kernel, so the socket can't be bind-mounted. Instead claude-profile
relays the agent over pasta networking from a bridge it serves on the host's 127.0.0.1,
which means an SSH-agent launch runs the VM as a child process instead of exec'ing it (the
TUI is unchanged).
Security: while the VM runs, the bridge listens on a TCP port on the host's 127.0.0.1.
Every connection must start with a random token made for that launch and handed only to the
VM, so other users' processes, containers on the host network and other sandbox VMs can't use
your agent through it. Code in the sandbox can use your keys to authenticate (it cannot read
them). Leave this off for untrusted work.
If you sign commits with GPG, enable gpg-agent forwarding:
export CLAUDE_PROFILE_SANDBOX_GPG_AGENT=1
claude-profile personalThe sandbox gets a fresh GNUPGHOME seeded with your public keys (gpg --export) plus a
bridge to your gpg-agent's restricted S.gpg-agent.extra socket. Signing happens on the
host, so your secret keys (or smartcard) never enter the VM. gpg-agent applies your usual
PIN and touch policy: with the PIN cached and no touch requirement, the sandbox can sign
without any prompt. The restricted socket also allows decryption, so the sandbox can
decrypt data encrypted to your keys. It goes through the same token-checked bridge as the
SSH agent. Your ~/.gitconfig is mounted read-only too. Files it
pulls in with include or includeIf are not, so the launcher resolves user.name and
user.email for the working directory on the host and passes them in as global config,
together with user.signingkey, commit.gpgsign and gpg.format when GPG forwarding is
on and the format is openpgp. Commits in the VM get the identity and key git would use for
that directory on the host, and a repo's own config still overrides them. Note:
gpg --list-secret-keys looks empty
inside the VM (the restricted socket hides key listing) — that's expected; signing still
works.
Claude Code lets you paste an image (Ctrl+V) into the prompt. It reads the image by shelling
out to wl-paste, which needs the desktop clipboard — something the microVM, with its own
kernel and no display, can't reach. Enable a scoped clipboard bridge:
export CLAUDE_PROFILE_SANDBOX_CLIPBOARD=1
claude-profile personalThe bridge runs your real wl-paste on demand, read-only invocations only, and streams just
the clipboard bytes into the VM; an in-VM wl-paste shim feeds them to Claude Code. Requires
wl-paste on the host (dnf install wl-clipboard) and a Wayland session.
Security: deliberately not full Wayland forwarding. Proxying the whole compositor (e.g.
waypipe) would also hand the sandbox screen capture and keystroke injection into your focused
window — a practical escape. This bridge is read-only clipboard: the sandbox can read
what's on your clipboard while it runs, and nothing else. Like the agent forwards, it runs
the VM as a child process instead of exec'ing it, because claude-profile serves the bridge.
The image ships gh. To let the agent act on your GitHub account (open PRs, comment, gh api),
forward your login:
export CLAUDE_PROFILE_SANDBOX_GH=1
claude-profile personalgh stores its token in your system keyring (or hosts.yml), which the microVM can't reach, so
claude-profile reads it on the host with gh auth token and passes it in as GH_TOKEN — the
env var gh reads natively. No config file is mounted. If no token is found, you get a warning
and gh is simply unauthenticated inside the VM.
Security: this hands the sandbox a token with your account's scopes (yours are repo,
workflow, read:org, gist) — the agent can do anything they allow, including pushing code and
triggering workflows. It is off by default; enable it only when you want the agent working against
your real GitHub account.
The image ships the infisical CLI. To let the agent read your secrets, allowlist which of your
infisical logins to forward — by exact email, or by domain (which also covers its
subdomains), comma-separated:
export CLAUDE_PROFILE_SANDBOX_INFISICAL="corp.example,me@example.org"
claude-profile personalinfisical keeps its login token in your system keyring, which the microVM can't reach. For each
allowlisted, non-expired login, claude-profile reads the token on the host and forwards it as
env: the primary (your active login if it's allowlisted, else the first match) becomes
INFISICAL_TOKEN + INFISICAL_API_URL, so infisical secrets --projectId … --env … works with no
extra flags; every allowlisted login is also placed in CLAUDE_SANDBOX_INFISICAL (JSON) so the
agent can target a non-primary org with --token/--domain. The keyring is never modified and
nothing is written to disk.
Only currently-valid logins are forwarded — the CLI can't refresh, so an expired login is skipped
with a warning until you re-run infisical login on the host. Forwarded tokens are good for ~10
days. The primary domain is set globally, which overrides any repo's .infisical.json, so for
a non-primary org always pass --domain explicitly.
Security: each forwarded token grants the sandbox that login's full access to your secrets. It is off by default and takes an explicit allowlist — no login is forwarded unless you name it.
The image ships the pulumi CLI. To let the agent run pulumi preview/up against your stacks,
forward your Pulumi Cloud token:
export CLAUDE_PROFILE_SANDBOX_PULUMI=1
claude-profile personalpulumi stores its token in ~/.pulumi/credentials.json, which the microVM can't reach, so
claude-profile reads it on the host and passes it in as PULUMI_ACCESS_TOKEN — the env var
pulumi reads natively. Only Pulumi Cloud (https) backends are forwarded; self-managed backends
(s3://, file://, …) carry no token and are skipped. If no token is found you get a warning and
pulumi is unauthenticated inside the VM.
Security: this hands the sandbox a token with full access to your Pulumi Cloud stacks — the
agent can read state and run pulumi up/destroy. It is off by default; enable it only when you
want the agent working against your real Pulumi account.
The image ships bk (the Buildkite CLI). It authenticates from BUILDKITE_API_TOKEN — no dedicated
toggle needed; forward that token via CLAUDE_PROFILE_SANDBOX_FORWARD_ENV (the same token the
buildkite MCP uses). Pass --org <slug> per command, or run bk configure in-session for a default
org.
The image includes podman, so the agent can build and run containers inside the VM. They run
rootful: plain podman is wrapped to sudo automatically, and sudo podman works too:
podman run --rm docker.io/library/alpine echo hi
podman build -t myimage .Files a nested container writes into the mounted repo belong to root inside the VM, which is a
subuid on the host, so you can't delete them there without podman unshare rm -rf <path>.
Containers that write into the repo should run with --user "$(id -u):$(id -g)"; the agent
is told to do this.
Rootless podman doesn't work here (the nested user namespace can't be mapped), but the krun guest
kernel treats uid 0 as real root, so podman behaves like rootful podman on a normal Fedora host,
with fuse-overlayfs storage. Container networking works normally — external DNS and
container-to-container name resolution both resolve — because the sandbox runs the microVM with a
real guest network stack (krun.use_passt=1) instead of libkrun's default TSI socket
impersonation. (TSI silently drops socket options like SO_REUSEADDR, which breaks gRPC, and
intercepts container DNS.) This needs passt on the host and a recent crun/libkrun.
Your MCP servers are configured in the profile's .claude.json, which is mounted into the VM, so
claude sees them — but servers written for the host don't all launch there:
-
HTTP servers (e.g.
windmill,exa) work as-is: the VM has network egress and any OAuth token travels in the mounted config. -
Container servers (
podman run …, e.g.github,buildkite, thevictoria*servers) work because the sandbox routespodmanthrough rootful sudo automatically. A server that passes a token through as-e VARneeds that variable forwarded into the VM (servers that bake the value into the config'senvblock, like thevictoria*ones, already travel with the config):export CLAUDE_PROFILE_SANDBOX_FORWARD_ENV="BUILDKITE_API_TOKEN,GITHUB_PERSONAL_ACCESS_TOKEN"
-
Host-path servers (that run a host binary or read a host directory — e.g. an Obsidian vault path) won't work unless that path is mounted or the tool is installed in the VM.
Container MCP images are pulled inside the VM on each launch (it's ephemeral). To avoid that, pre-cache them once on the host:
claude-profile sandbox-cache personal # pull this profile's MCP images into a shared storesandbox-cache discovers the podman run … <image> servers from the profile's .claude.json,
pulls them into ~/.local/share/claude-profile/image-store, and makes it world-readable. The
sandbox then mounts that store read-only (a podman additionalimagestore) whenever it's populated,
so those images are found locally instead of pulled — no launch-time pull. It's read-only and
shared, so your parallel worktree sandboxes can't corrupt it. Re-run to update; --clear empties it.
A microVM raises the bar considerably but is not a perfect boundary. Networking stays open (claude needs the API), so the sandbox limits filesystem and process blast radius — not network egress — and the agent can read the profile credentials mounted into the VM. For genuinely untrusted code, prefer a full or cloud VM. This is based on the Fedora Magazine article Sandbox AI coding agents with microVMs on Fedora Linux.
Credentials the agent in the VM can read or use:
- Always: the profile's Claude login (
.credentials.jsonin the mounted profile directory), the profile's.envvalues (as environment variables), tokens in the MCP server entries of the profile's.claude.json, API keys in ancestor.mcp.jsonfiles (see below), and anything in a copy of your~/.gitconfig, such as a token in a remote URL. - When you turn them on: your SSH agents (
CLAUDE_PROFILE_SANDBOX_SSH_AGENT: it can authenticate with your keys, not read them), gpg-agent (CLAUDE_PROFILE_SANDBOX_GPG_AGENT: it can sign and decrypt, not read the secret keys), your GitHub token (CLAUDE_PROFILE_SANDBOX_GH), the allowlisted Infisical logins (CLAUDE_PROFILE_SANDBOX_INFISICAL), your Pulumi Cloud token (CLAUDE_PROFILE_SANDBOX_PULUMI), the variables named inCLAUDE_PROFILE_SANDBOX_FORWARD_ENV, your clipboard (CLAUDE_PROFILE_SANDBOX_CLIPBOARD, read-only), and your logged-in browser through the Claude extension (see below). - When the profile uses Agent Vault: a session token, in
HTTPS_PROXYandHTTP_PROXY, instead of the credentials themselves. It works only through the proxy, only for the bundle's services, and only until the launch ends or 24 hours pass.
Not mounted: ~/.ssh (only a copy of known_hosts), your keyring, and other profiles'
directories.
What else the VM can reach:
-
Writable state the host trusts later. The profile directory and the working tree are mounted read-write, so the agent could change files that run on the host afterwards. The ones the launcher knows about are protected:
- The repo's
.git/config(and each submodule's) is read-only in the VM, so the agent can't set something likecore.fsmonitorthat your nextgit statuswould run. Git commands that save config there can't save it:git push -upushes but doesn't record the upstream, andgit remote addfails. .git/hooks/, the profile'ssettings.jsonand the project's.claude/settings.local.jsonare copies in the VM, thrown away when it exits. Hooks you have on the host still run in the VM.- The
.sandboxmarker and.envare read-only, so the agent can't switch the next launch to the host or plant variables in it. - On a host launch of a sandboxed profile you're warned if
statusline.shis no longer the link to~/.claude/statusline.sh. - When a session that forwards anything (SSH, GPG, clipboard) ends, you're told
which MCP servers or
.mcp.jsonapprovals it added to the profile's.claude.json, and whether it created a.claude/settings.local.json. Other launches can't report this, so check.claude.jsonyourself after them.
Everything else in the working tree,
.claude/settings.jsonand.mcp.jsonincluded, is yours to review as you would any change the agent makes. - The repo's
-
MCP config above the working directory. Ancestor
.mcp.jsonfiles (often~/.mcp.json) are mounted read-only, so any API keys in them are readable in the VM. -
Your browser. claude reaches the Claude Chrome extension through Anthropic's bridge, signed in with the profile's claude.ai login, which the VM holds. So while the extension is signed in to that account, anything in the VM can drive your logged-in Chrome with
claude --chrome, whether or notCLAUDE_PROFILE_SANDBOX_CHROMEis set (that setting only adds the flag). The sandbox runs claude with--dangerously-skip-permissions, so Claude Code doesn't ask before those actions either; the extension's site permissions are the remaining check. To limit what a session can reach, install the extension in a separate Chrome profile that isn't signed in to accounts you care about, narrow its site permissions, and turn it off when you aren't using it. -
The host's loopback. When a bridge or a loopback hook is active, the VM runs with
--map-host-loopback, which reaches every service listening on the host's127.0.0.1, not only the bridges.
A profile can send claude's HTTPS through an
Infisical Agent Vault
proxy, which attaches the real credential to each request for a service in an access bundle.
claude and everything it runs (gh, curl, git, SDKs, MCP servers) then work with placeholder
tokens, and the credentials stay in Infisical and on the proxy. It is off unless a profile opts
in, and it needs the infisical CLI on the host, logged in to the instance that holds the bundle.
Opt a profile in through its environment:
claude-profile env work \
--set CLAUDE_PROFILE_AGENT_VAULT_BUNDLE=coding \
--set CLAUDE_PROFILE_AGENT_VAULT_PROXY=10.0.1.5:17323 \
--set CLAUDE_PROFILE_AGENT_VAULT_CA_FINGERPRINT=SHA256:9F:2C:... \
--set GH_TOKEN=agent-vault| Key | Required | Description |
|---|---|---|
CLAUDE_PROFILE_AGENT_VAULT_BUNDLE |
yes | The access bundle the session covers. Setting it turns Agent Vault on for the profile; unsetting it turns it off and keeps the other keys |
CLAUDE_PROFILE_AGENT_VAULT_PROXY |
with BUNDLE |
The proxy's host:port |
CLAUDE_PROFILE_AGENT_VAULT_CA_FINGERPRINT |
with BUNDLE |
The proxy CA's fingerprint, as the Proxies page shows it (SHA256:...) |
CLAUDE_PROFILE_AGENT_VAULT_INFISICAL_PROFILE |
no | The infisical login profile to mint sessions with (see infisical profile list), when the CLI would otherwise pick another |
CLAUDE_PROFILE_AGENT_VAULT_NO_PROXY |
no | Comma-separated hosts that skip the proxy, in addition to the ones listed below |
claude-profile reads these keys and claude never sees them. Every other variable in the profile's
environment reaches claude as usual, which is where placeholders go: a Bearer service replaces the
header whatever its value, so a placeholder only needs to satisfy tools that won't run without a
token, such as gh. These keys are per profile, not CLAUDE_PROFILE_* variables in your shell,
and the sandbox can't change them: the profile's .env is read-only in the VM.
What a launch does:
- Pre-flight. It asks the proxy for its CA and compares it with the pinned fingerprint. If the
proxy doesn't answer, serves another CA, or the
infisicalCLI is missing, the launch goes ahead without Agent Vault and says so in red. In the sandbox the agent is told as well, andCLAUDE_SANDBOX_AGENT_VAULTisunavailable(activeotherwise). - Session. It starts itself again under
infisical agent-vault run, which mints a 24-hour session over the bundle with your Infisical login and revokes it when the launch exits, including on SIGHUP and SIGTERM. If minting fails, the launch stops with the CLI's error; unsetCLAUDE_PROFILE_AGENT_VAULT_BUNDLEto launch without it. - Environment. claude gets
HTTPS_PROXYandHTTP_PROXY, which carry the session token, and a CA bundle with your system's roots plus the proxy's CA, so proxied and direct HTTPS both verify. The Claude API (.anthropic.com), the login and token hosts (claude.ai,claude.comand their subdomains) and the Claude in Chrome bridge (.claudeusercontent.com) skip the proxy, so your prompts and the profile's login never pass through it.
In the sandbox, the session token reaches the VM in the same unlinked env file as the rest of the
profile's environment, never in podman's argv or environment. The combined CA bundle is mounted
over the VM's system bundle, and the VM's NO_PROXY adds the host loopback, where the bridges
listen. On a host launch, infisical agent-vault run stays as claude's parent for the whole
session, since it revokes the session when claude exits; other launches replace claude-profile
with claude.
podman runs without the proxy. The proxy decrypts all HTTPS with its own CA, and containers
and image build steps don't trust it, so a host launch puts a small podman shim first on
claude's PATH, and in the VM the image's podman wrapper does the same. Containers, builds and
pulls go direct, as they do without Agent Vault, so an MCP server you run with podman needs its
real token, unless its command includes --http-proxy=true. Then podman passes the proxy in, and
the server can hold a placeholder like any other tool:
"args": ["run", "-i", "--rm", "--http-proxy=true", "--env", "VL_INSTANCE_BEARER_TOKEN", "…"],
"env": {"VL_INSTANCE_BEARER_TOKEN": "__VLOGS_TOKEN__"}For HTTPS, a podman run or podman create with the flag also gets the session's CA bundle at
/run/agent-vault/ca-bundle.pem, and the CA variables that infisical agent-vault run sets
(SSL_CERT_FILE, NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE,
GIT_SSL_CAINFO, DENO_CERT) point there, so the container trusts the proxy. This needs run
or create as podman's first argument. A host the server reaches must not be in NO_PROXY. A
server with a remote HTTP endpoint needs none of this: claude calls the endpoint itself, through
the proxy. The VM side needs an image built from this version (claude-profile build).
| Variable | Default | Description |
|---|---|---|
CLAUDE_PROFILE_PROFILES_BASE |
~/.claude-profiles |
Directory where profiles are stored |
CLAUDE_PROFILE_CLAUDE_BIN |
claude |
Path to the claude binary |
CLAUDE_PROFILE_PODMAN_BIN |
podman |
Path to the podman binary (sandbox mode) |
CLAUDE_PROFILE_INFISICAL_BIN |
infisical |
Path to the infisical CLI (Agent Vault profiles) |
CLAUDE_PROFILE_SANDBOX_IMAGE |
claude-profile-sandbox:latest |
Image used for sandbox launches |
CLAUDE_PROFILE_SANDBOX_RAM_MIB |
4096 |
microVM memory in MiB |
CLAUDE_PROFILE_SANDBOX_CPUS |
4 |
microVM vCPU count |
CLAUDE_PROFILE_SANDBOX_SKIP_PERMISSIONS |
true |
Auto-add --dangerously-skip-permissions in sandbox mode |
CLAUDE_PROFILE_SANDBOX_SSH_AGENT |
false |
Forward your SSH agent(s) into the VM through a token-checked bridge |
CLAUDE_PROFILE_SANDBOX_GPG_AGENT |
false |
Forward your gpg-agent (signing) into the VM; seeds public keys, mounts ~/.gitconfig |
CLAUDE_PROFILE_SANDBOX_CLIPBOARD |
false |
Bridge your clipboard into the VM (read-only) so image paste works; needs wl-paste on the host |
CLAUDE_PROFILE_SANDBOX_CHROME |
false |
Start the VM's claude with --chrome, so Claude in Chrome drives your browser through Anthropic's bridge; needs a full OAuth login, not a setup-token |
CLAUDE_PROFILE_SANDBOX_GH |
false |
Forward your GitHub login into the VM as GH_TOKEN (read via gh auth token) so gh acts as you |
CLAUDE_PROFILE_SANDBOX_INFISICAL |
(empty) | Allowlist (comma-separated emails/domains) of infisical logins to forward into the VM as INFISICAL_TOKEN/--token |
CLAUDE_PROFILE_SANDBOX_PULUMI |
false |
Forward your Pulumi Cloud token into the VM as PULUMI_ACCESS_TOKEN so pulumi acts as you |
CLAUDE_PROFILE_SANDBOX_FORWARD_ENV |
(empty) | Comma-separated host env var names to copy into the VM (e.g. tokens MCP servers pass through as -e VAR, or HTTPS_PROXY: the host's proxy settings aren't passed in otherwise) |
CLAUDE_PROFILE_SANDBOX |
(unset) | Per-launch override: 1 forces microVM, 0 forces host; unset uses the profile's setting |