28 KiB
Architecture Research
Domain: bwrap sandbox wrapper — network isolation, profiles, auth passthrough Researched: 2026-04-10 Confidence: HIGH (existing codebase read directly; new feature patterns verified against nixpkgs, official Claude Code docs, and upstream slirp4netns/pasta documentation)
Standard Architecture
System Overview — v2.0 with new features
claudebox invocation
|
v
┌───────────────────────────────────────────────────────────────────┐
│ claudebox.sh (writeShellApplication, Nix store) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────────┐ │
│ │ Arg parse │ │Profile load │ │ Instance dir resolution │ │
│ │ --profile │ │ ~/.claudebox│ │ ~/.claudebox/instances/ │ │
│ │ --network │ │ /profiles/ │ │ <cwd-hash>/.claude/ │ │
│ └──────┬──────┘ └──────┬──────┘ └────────────┬─────────────┘ │
│ └────────────────┴──────────────┬─────────┘ │
│ v │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Env builder │ │
│ │ - sandbox-generated vars (HOME, USER, PATH, SHELL ...) │ │
│ │ - host allowlist (TERM, LANG, ANTHROPIC_API_KEY ...) │ │
│ │ - profile env vars injected here │ │
│ │ - CLAUDEBOX_EXTRA_ENV escape hatch │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ | │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Mount builder │ │
│ │ - core mounts (nix store, /etc/*, /proc, /dev, tmpfs) │ │
│ │ - auth passthrough (ro-bind ~/.claude/.credentials.json) │ │
│ │ - instance dir (bind ~/.claudebox/instances/<hash>) │ │
│ │ - profile extra mounts appended here │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ | │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Network tier decision │ │
│ │ full → no --unshare-net, share host network │ │
│ │ inet → --unshare-net + pasta sidecar (internet, no LAN) │ │
│ │ none → --unshare-net, no sidecar (offline) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ | │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Pre-launch: env audit display + confirmation │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ | │
│ full/none: exec bwrap ... | inet: bwrap ... & │
│ | pasta $BWRAP_PID │
│ | wait $BWRAP_PID │
└───────────────────────────────────────────────────────────────────┘
|
┌──────────────────────────┴───────────────────────┐
│ bwrap sandbox │
│ ~/.claudebox → ~/.claudebox/instances/<hash> │
│ ~/.claude symlink → ~/.claudebox │
│ ~/.claude/.credentials.json (read-only) │
│ CWD (read-write) │
│ profile devshell PATH prepended to SANDBOX_PATH │
│ network: full | inet (via pasta TAP) | none │
└──────────────────────────────────────────────────┘
Component Responsibilities
| Component | Responsibility | Status (v2.0) |
|---|---|---|
flake.nix |
Nix derivation, runtimeInputs, SANDBOX_PATH injection |
Modified — add pkgs.passt |
claudebox.sh — arg parse |
CLI flags | Modified — add --profile, --network |
claudebox.sh — profile loader |
Read ~/.claudebox/profiles/<name>.json |
New function |
claudebox.sh — instance resolver |
Hash CWD → ~/.claudebox/instances/<hash>/, create if missing |
New function |
claudebox.sh — env builder |
Accumulate --setenv args |
Modified — add profile env injection |
claudebox.sh — mount builder |
Accumulate bwrap mount args | Modified — auth ro-bind, instance bind, profile mounts |
claudebox.sh — network setup |
Decide --unshare-net + pasta sidecar |
New function |
claudebox.sh — package injector |
Resolve profile packages via nix build, prepend to SANDBOX_PATH |
New function |
claudebox.sh — exec block |
exec bwrap or bwrap & wait depending on tier |
Modified — split by network tier |
| Profile store | ~/.claudebox/profiles/<name>.json |
New on-disk schema |
| Instance store | ~/.claudebox/instances/<cwd-hash>/ |
New on-disk layout |
| Auth source | ~/.claude/.credentials.json (host, read-only mount) |
New mount only |
Recommended Project Structure
claudebox/
├── flake.nix # add pkgs.passt to runtimeDeps
├── claudebox.sh # main script — extended with new sections
└── profiles/ # optional bundled example profiles
└── example.json
~/.claudebox/ # runtime state on host
├── CLAUDE.md # existing
├── SANDBOX.md # existing, overwritten each launch
├── profiles/ # user-defined profiles
│ ├── default.json
│ ├── work.json
│ └── offline.json
└── instances/ # per-project conversation history
├── a3f7b2c1d9e1f430/ # sha256 of /home/user/projects/foo (16 hex chars)
│ └── .claude/ # conversations, settings scoped to this project
│ ├── settings.local.json
│ └── projects/
└── d9e1f430a3f7b2c1/
└── .claude/
Structure Rationale
profiles/: Flat JSON keeps profiles shell-readable withjqwithout a Nix dependency at profile-load time. A.nixvariant is possible for devshell injection but adds complexity; JSON is preferred for v2.0.instances/<hash>/: Hashing the absolute CWD path gives stable, collision-resistant names without encoding slashes.sha256sumtruncated to 16 hex chars is sufficient — 2^64 space, no practical collision risk.instances/<hash>/.claude/: Claude Code reads~/.claude/for all state (conversation history inprojects/, settings insettings.local.json). Mapping this per-project gives isolated history automatically without any changes to Claude Code itself.
Architectural Patterns
Pattern 1: Instance Dir via CWD Hash
What: Derive a stable instance directory from sha256sum of the absolute CWD. Create on first use. Bind-mount it as ~/.claudebox inside the sandbox (preserving the existing ~/.claudebox → ~/.claude symlink that already exists in the script).
When to use: Every launch. Replaces the current single ~/.claudebox bind-mount target.
Trade-offs: One sha256sum call and one mkdir -p per launch. Negligible latency. History is siloed per CWD, which is the desired behavior.
Integration point in existing code (line 345-346 of claudebox.sh):
# EXISTING (line ~345 in current claudebox.sh):
# --bind "$HOME/.claudebox" "$HOME/.claudebox" \
# --symlink "$HOME/.claudebox" "$HOME/.claude" \
# REPLACE WITH:
INSTANCE_HASH=$(printf '%s' "$CWD" | sha256sum | cut -c1-16)
INSTANCE_DIR="$HOME/.claudebox/instances/$INSTANCE_HASH"
mkdir -p "$INSTANCE_DIR/.claude"
# In bwrap call:
# --bind "$INSTANCE_DIR" "$HOME/.claudebox" \
# --symlink "$HOME/.claudebox" "$HOME/.claude" \ (unchanged)
The ~/.claudebox → ~/.claude symlink line is unchanged. The SANDBOX.md write and CLAUDE.md prepend logic (lines 107-154) currently targets $HOME/.claudebox/ — these should continue targeting $HOME/.claudebox/ on the host (the shared root), not the instance dir. SANDBOX.md is global to all instances; CLAUDE.md is too. Only conversation history (inside .claude/) is per-instance.
Pattern 2: Auth Passthrough as Read-Only Mount
What: Mount ~/.claude/.credentials.json from the host into the sandbox at the same path, read-only. No other files from the host ~/.claude/ are mounted.
When to use: Always (unconditional in v2.0).
Why only .credentials.json: Verified against official Claude Code docs — on Linux, credentials are stored at ~/.claude/.credentials.json (mode 0600). The host ~/.claude/ otherwise contains all historical conversation state. We do not want that leaking into the sandbox; only the auth token is needed.
Path resolution inside sandbox: The sandbox's ~/.claude/ is the instance dir (via ~/.claudebox → ~/.claude symlink). The credential file must be bound inside this path:
AUTH_CREDS="$HOME/.claude/.credentials.json"
if [[ -f "$AUTH_CREDS" ]]; then
# In bwrap mount args (after the instance dir bind and symlink):
MOUNT_ARGS+=(--ro-bind "$AUTH_CREDS" "$HOME/.claudebox/.claude/.credentials.json")
# Which resolves to ~/.claude/.credentials.json inside sandbox
fi
Wait — the symlink chain inside sandbox is: ~/.claudebox is the instance dir, ~/.claude symlinks to ~/.claudebox. So ~/.claude/.credentials.json → ~/.claudebox/.credentials.json → $INSTANCE_DIR/.credentials.json. The ro-bind should target $HOME/.claudebox/.credentials.json inside bwrap's view of the namespace (which is the instance dir bind target). Simpler alternative: bind directly to the resolved path $INSTANCE_DIR/.credentials.json on the host before launching bwrap.
# Simpler: copy (or bind) creds into instance dir before launch
# ro-bind from host ~/.claude/.credentials.json
# to instance dir path that becomes ~/.claude/.credentials.json inside sandbox
if [[ -f "$HOME/.claude/.credentials.json" ]]; then
MOUNT_ARGS+=(--ro-bind "$HOME/.claude/.credentials.json" \
"$INSTANCE_DIR/.credentials.json")
fi
# Then inside sandbox: ~/.claude/.credentials.json exists read-only
Credential precedence note (HIGH confidence — official docs): Claude Code selects credentials in this order: cloud provider env vars → ANTHROPIC_AUTH_TOKEN → ANTHROPIC_API_KEY → apiKeyHelper → CLAUDE_CODE_OAUTH_TOKEN → OAuth file at ~/.claude/.credentials.json. The existing host-allowlist for ANTHROPIC_API_KEY therefore continues to take precedence over the passthrough file if both are present.
Pattern 3: Tiered Network via Sidecar Process
What: Three network tiers controlled by --network flag (default: full):
full— no--unshare-net; sandbox shares host network. Current behavior.inet—--unshare-net+ pasta sidecar; internet access via userspace NAT, no LAN, no Tailscale, no host loopback services.none—--unshare-netalone; loopback only, fully offline.
When to use: Expose via --network inet|none|full CLI flag. Profile network field sets the default for that profile (overridable by CLI flag).
pasta vs slirp4netns decision — use pasta:
- Both
pkgs.passt(version2025_09_19.623dbf6) andpkgs.slirp4netns(version1.3.3) are in nixpkgs and verified available. - pasta is the current default in Podman 5 and RHEL 9.5; actively maintained.
- pasta avoids NAT (it forwards at Layer-4 via native sockets), giving better performance and simpler DNS.
- pasta supports
--no-map-gwto prevent host gateway access and--no-tcp-ports --no-udp-portsto block incoming connections. - slirp4netns has
--disable-host-loopbackbut still routes to LAN by default. - Add
pkgs.passttoruntimeDepsinflake.nix. Keepslirp4netnsas a named alternative but do not add it by default.
Mechanism for inet tier — critical exec change:
The current script ends with exec bwrap ... (line 327, claudebox.sh). exec replaces the shell process, leaving no parent to launch a sidecar. For inet mode, the script must fork instead:
if [[ "$NETWORK_TIER" == "inet" ]]; then
BWRAP_PIDFILE=$(mktemp)
trap 'rm -f "$BWRAP_PIDFILE"' EXIT
bwrap \
--unshare-net \
--pidfile "$BWRAP_PIDFILE" \
"${ENV_ARGS[@]}" \
... (all other mount args) ... \
-- "${SANDBOX_CMD[@]}" &
BWRAP_PID=$!
# Wait for bwrap to write its PID (namespace is ready when pidfile is non-empty)
until [[ -s "$BWRAP_PIDFILE" ]]; do sleep 0.05; done
SANDBOX_PID=$(cat "$BWRAP_PIDFILE")
# Attach pasta to the network namespace
pasta --config-net --no-tcp-ports --no-udp-ports "$SANDBOX_PID" &
PASTA_PID=$!
wait "$BWRAP_PID"
STATUS=$?
kill "$PASTA_PID" 2>/dev/null || true
exit "$STATUS"
elif [[ "$NETWORK_TIER" == "none" ]]; then
exec bwrap --unshare-net "${ENV_ARGS[@]}" ... -- "${SANDBOX_CMD[@]}"
else # full (default)
exec bwrap "${ENV_ARGS[@]}" ... -- "${SANDBOX_CMD[@]}"
fi
DNS for inet mode: pasta provides DNS forwarding automatically through its virtual gateway. The existing /etc/resolv.conf ro-bind must be REMOVED for inet mode (the sandbox resolv.conf would point to host DNS on the host network, unreachable in the new namespace). pasta configures the TAP device with 10.0.2.100 and routes DNS queries through its gateway. A synthetic /etc/resolv.conf inside the sandbox pointing to 10.0.2.3 (pasta's default gateway DNS) should be provided via --ro-bind from a tempfile.
--pidfile flag availability: bwrap has supported --pidfile since version 0.4.0. The nixpkgs version is 0.9.x, so this is available. (HIGH confidence — verified against bwrap manpage.)
Pattern 4: Profile Schema (JSON)
What: A flat JSON file at ~/.claudebox/profiles/<name>.json describing what a named profile adds to the baseline sandbox.
Schema:
{
"name": "work",
"network": "inet",
"env": {
"AWS_PROFILE": "work"
},
"extra_env_passthrough": ["MY_ORG_TOKEN", "VAULT_TOKEN"],
"mounts": [
{ "host": "~/.aws/credentials", "sandbox": "~/.aws/credentials", "mode": "ro" }
],
"packages": ["awscli2", "kubectl"]
}
Loading with jq (already in runtimeDeps):
PROFILE_NAME="${CLAUDEBOX_PROFILE:-${1#--profile=}}" # or parsed via arg loop
PROFILE_FILE="$HOME/.claudebox/profiles/${PROFILE_NAME}.json"
if [[ -f "$PROFILE_FILE" ]]; then
PROFILE_NETWORK=$(jq -r '.network // empty' "$PROFILE_FILE")
mapfile -t PROFILE_PACKAGES < <(jq -r '.packages[]? // empty' "$PROFILE_FILE")
mapfile -t PROFILE_PASSTHROUGH < <(jq -r '.extra_env_passthrough[]? // empty' "$PROFILE_FILE")
# ... etc
fi
Profile env injection: Profile env entries are treated like CLAUDEBOX_EXTRA_ENV values — they are sandbox-side constants (not host passthrough). The extra_env_passthrough list extends HOST_ALLOWLIST dynamically, allowing host env vars not in the hardcoded allowlist to pass through when a profile explicitly permits them. This preserves the allowlist security model: the profile, not the shell environment, decides what's allowed.
Pattern 5: Nix Package Injection
What: Profile packages field lists nixpkgs attribute names. Resolved via nix build --no-link --print-out-paths before launch. Results prepended to SANDBOX_PATH.
Why nix build not nix shell: nix shell nixpkgs#pkg spawns a child shell and cannot inject into the parent's SANDBOX_PATH variable. nix build --print-out-paths returns the store path, which can be prepended to the PATH string before it's passed to --setenv.
EXTRA_BIN_PATHS=""
for pkg in "${PROFILE_PACKAGES[@]}"; do
pkg_out=$(nix build --no-link --print-out-paths "nixpkgs#${pkg}" 2>/dev/null) || {
echo "Warning: could not resolve profile package: $pkg" >&2
continue
}
EXTRA_BIN_PATHS="${pkg_out}/bin:${EXTRA_BIN_PATHS}"
done
SANDBOX_PATH="${EXTRA_BIN_PATHS}${SANDBOX_PATH}"
Latency: Zero if the store path is already built/cached. First-time use triggers a download; thereafter cached. The existing nix daemon socket bind ensures builds work inside the script (the script itself runs with full host access, not inside bwrap).
devshell injection (deferred): Full nix develop .#devShell integration — where a project's own flake.nix devShell is evaluated and its environment injected — is significantly more complex (requires evaluating the flake, capturing buildEnv, etc.). Defer to a later milestone. The packages field covers the practical 80% use case.
Data Flow
Launch Flow (inet tier, with profile)
claudebox --profile work --network inet
|
[Arg parse] → PROFILE_NAME=work, NETWORK_TIER=inet
|
[Profile load] ~/.claudebox/profiles/work.json
→ PROFILE_NETWORK=inet, PROFILE_PACKAGES=[awscli2],
PROFILE_ENV={AWS_PROFILE=work}, PROFILE_MOUNTS=[~/.aws/credentials]
|
[Instance resolve]
INSTANCE_HASH = sha256("$CWD")[0:16]
mkdir -p ~/.claudebox/instances/$INSTANCE_HASH/.claude
|
[Package resolve]
nix build nixpkgs#awscli2 → /nix/store/xxx-awscli2
SANDBOX_PATH = /nix/store/xxx-awscli2/bin:$SANDBOX_PATH
|
[Env build]
base vars (HOME, USER, PATH=SANDBOX_PATH, SHELL, ...)
+ profile env (AWS_PROFILE=work)
+ host allowlist (TERM, LANG, ANTHROPIC_API_KEY if set, ...)
+ profile extra_env_passthrough (MY_ORG_TOKEN if set on host)
|
[Mount build]
core mounts (nix store, /etc/*, proc, dev, tmpfs ...)
+ instance dir bind (→ ~/.claudebox inside sandbox)
+ ~/.claudebox → ~/.claude symlink (unchanged)
+ auth creds ro-bind (~/.claude/.credentials.json)
+ profile mounts (~/.aws/credentials ro)
|
[DNS tempfile for inet mode]
echo "nameserver 10.0.2.3" > /tmp/resolv-$$.conf
(replaces the /etc/resolv.conf ro-bind)
|
[Env audit display + confirmation]
|
[Network: inet]
bwrap --unshare-net --pidfile $PIDFILE ... &
BWRAP_PID=$!
until [[ -s $PIDFILE ]]; do sleep 0.05; done
pasta --config-net --no-tcp-ports --no-udp-ports $(cat $PIDFILE) &
wait $BWRAP_PID → exit with its status
|
[Inside sandbox]
~/.claude/ = instance dir for this CWD (isolated history)
~/.claude/.credentials.json (ro, host OAuth token)
awscli2 in PATH
AWS_PROFILE=work in env
~/.aws/credentials accessible (ro)
internet via pasta, no LAN, no Tailscale, no host loopback
State Locations (host)
| Purpose | Host Path | Sandbox Path | Mode |
|---|---|---|---|
| Shared claudebox config | ~/.claudebox/ |
n/a (host-side only) | rw |
| SANDBOX.md / CLAUDE.md | ~/.claudebox/SANDBOX.md |
n/a | rw |
| Per-project history | ~/.claudebox/instances/<hash>/ |
~/.claudebox/ |
rw (bind) |
| Per-project .claude | ~/.claudebox/instances/<hash>/.claude/ |
~/.claude/ (via symlink) |
rw |
| Host auth token | ~/.claude/.credentials.json |
~/.claude/.credentials.json |
ro |
| Profile definitions | ~/.claudebox/profiles/<name>.json |
not mounted | — |
Anti-Patterns
Anti-Pattern 1: Mounting entire ~/.claude for auth passthrough
What people do: --bind ~/.claude ~/.claude to get credentials working.
Why it's wrong: Exposes all conversation history, settings, and any file Claude Code stores in ~/.claude. Also breaks per-project isolation because the host .claude overwrites the instance dir.
Do this instead: Mount only ~/.claude/.credentials.json read-only. Keep the instance dir as the effective ~/.claude target inside the sandbox.
Anti-Pattern 2: exec bwrap for all network tiers
What people do: Keep exec bwrap unconditionally for simplicity even when adding inet mode.
Why it's wrong: exec replaces the shell process. There is no parent process left to launch the pasta sidecar after the bwrap namespace is created.
Do this instead: Branch on NETWORK_TIER: use exec bwrap for full and none (no sidecar needed); use bwrap ... & + wait for inet.
Anti-Pattern 3: Hardcoding secrets in profile JSON
What people do: "env": { "AWS_SECRET_ACCESS_KEY": "hardcoded-value" } in a profile file.
Why it's wrong: Profile files at ~/.claudebox/profiles/ are plaintext. Hardcoded secrets become plaintext files on disk.
Do this instead: Use extra_env_passthrough to declare which host env vars are allowed into the sandbox. The value must be set in the host shell environment before running claudebox. The profile says "permit this variable" not "store this value."
Anti-Pattern 4: Per-profile nix develop evaluation at launch
What people do: nix develop .#profileEnv to inject a full devshell environment at launch time.
Why it's wrong: nix develop evaluates a flake, which takes hundreds of milliseconds to seconds even with caching. It also requires the profile to point to a valid flake with a devShell output.
Do this instead: Profile packages field lists nixpkgs attribute names. Resolve via nix build --no-link --print-out-paths. Fast (cached after first use), no flake required. Full devshell support is a later milestone.
Anti-Pattern 5: Removing /etc/resolv.conf mount for all tiers when adding inet support
What people do: Remove the resolv.conf bind globally when adding network isolation, breaking full mode DNS.
Why it's wrong: full and none tiers still rely on the /etc/resolv.conf bind-mount. Only inet mode needs it replaced with a pasta-managed DNS address.
Do this instead: Branch the resolv.conf mount on NETWORK_TIER: full/none keep the ro-bind from host; inet provides a tempfile pointing to pasta's gateway DNS (nameserver 10.0.2.3).
Integration Points
New vs Modified Components
| Component | Change Type | What Changes |
|---|---|---|
flake.nix |
Modified | Add pkgs.passt to runtimeDeps (one line) |
claudebox.sh — arg parse |
Modified | Add --profile NAME, --network full|inet|none flags |
claudebox.sh — exec block |
Modified | Split exec bwrap into three branches by NETWORK_TIER |
claudebox.sh — mount builder |
Modified | Replace fixed ~/.claudebox bind with instance dir bind; add auth creds ro-bind; add profile mounts |
claudebox.sh — env builder |
Modified | Add profile env injection after CLAUDEBOX_EXTRA_ENV block |
claudebox.sh — profile loader |
New function | jq parse of ~/.claudebox/profiles/<name>.json |
claudebox.sh — instance resolver |
New function | sha256sum of CWD, mkdir -p, set INSTANCE_DIR |
claudebox.sh — network setup |
New function | Set NETWORK_TIER var; build --unshare-net flag; build pasta invocation for inet |
claudebox.sh — package injector |
New function | nix build loop, build EXTRA_BIN_PATHS |
~/.claudebox/profiles/ |
New on-disk | User-created JSON profile files |
~/.claudebox/instances/ |
New on-disk | Auto-created per-project dirs |
Build Order (dependency-ordered)
-
Auth passthrough — Smallest change: add one conditional ro-bind for
~/.claude/.credentials.json. Validates that the instance dir and auth file coexist correctly before instance dir is fully wired up. No new Nix deps. Unblocks all downstream features that require Claude to authenticate inside the sandbox. -
Per-project instance dirs — Replace the single
~/.claudeboxbind-mount target with~/.claudebox/instances/<hash>. Depends on auth passthrough (step 1) having the credential bind path logic stable. No new Nix deps. After this, each project has isolated conversation history. -
Tiered network —
nonetier — Add--networkflag and implementnonewith--unshare-netalone. No new deps; bwrap is already present. Validates the exec→wait refactor structural change independently of pasta. Low risk: if offline mode is broken,fullmode is unaffected. -
Tiered network —
inettier (pasta) — Addpkgs.passtto flake, implement pasta sidecar forinet. Depends on step 3 having the exec→wait refactor validated. DNS tempfile handling belongs here. This is the highest-risk step (new external process, timing dependency on--pidfile). -
Named profiles (env + mounts + network tier) — Add
--profileflag, implement JSON profile loader withjq, inject env/mounts/network-tier from profile. Depends on steps 2 and 4 being stable (profiles can control all three axes). Pure shell logic, no new deps. -
Nix package injection — Add
packagesfield to profile schema, implementnix buildloop. Last because it has latency risk and is independently testable without touching any other subsystem.
Sources
- Existing codebase (
claudebox.shlines 1–351,flake.nix): direct read — HIGH confidence - Claude Code credential storage path on Linux (
~/.claude/.credentials.json): official docs athttps://code.claude.com/docs/en/authentication— HIGH confidence - Claude Code credential precedence order: same official docs — HIGH confidence
- pasta/passt PID-based namespace attachment and
--config-netflag:https://passt.top/passt/about/— MEDIUM confidence (pasta standalone bwrap integration not documented with exact flag combinations; verify--no-tcp-portsand DNS gateway address during implementation) - slirp4netns
--configure --disable-host-loopbackpattern:https://github.com/rootless-containers/slirp4netnsmanpage — MEDIUM confidence - nixpkgs package availability: verified locally via
nix eval—pkgs.passtversion2025_09_19.623dbf6,pkgs.slirp4netnsversion1.3.3— HIGH confidence - bwrap
--pidfileflag: bwrap 0.4.0+ manpage; current nixpkgs has 0.9.x — HIGH confidence - pasta as Podman 5 / RHEL 9.5 default: WebSearch corroborated — MEDIUM confidence
Architecture research for: claudebox v2.0 — network isolation, profiles, auth passthrough Researched: 2026-04-10