Skip to content

Sandbox boundary

fend’s threat model assumes any package may be malicious. This page is the authoritative list of what crosses the host↔VM boundary and what doesn’t.

Host pathVM pathModeNotes
<project-dir>/workspaceread-writeVirtioFS. The only place the VM can write that’s also visible on the host.
~/.fend/cache/npm//root/.npmread-writeShared npm cache across projects.
~/.fend/overlays/<runtime>//usr/localread-onlyNode/Bun/Python runtime, content-addressed.

Everything else is unmounted. The VM has its own minimal Linux rootfs (busybox

  • musl) — it does not see your home directory, your other projects, or any system path on the host.

These don’t exist inside the VM at all:

  • ~/.ssh/ — SSH keys
  • ~/.aws/ — AWS credentials and config
  • ~/.gnupg/ — GPG keys
  • ~/Library/ — macOS Keychain database, browser data, cookies, app state
  • ~/.config/ — shell config, app config, anything you store there
  • ~/.zshrc, ~/.bashrc, ~/.profile — shell init
  • .env files outside the project directory
  • Any other directory under ~/repos/ or wherever you keep code
  • Any other path on the host filesystem

A package running cat ~/.ssh/id_rsa inside the sandbox gets ENOENT. There is nothing to read because the path was never mounted.

Forwarded to the sandbox automatically:

TERM, LANG, LC_ALL, LC_CTYPE, COLORTERM, NO_COLOR, FORCE_COLOR
EDITOR, VISUAL
GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL
GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL

Plus FEND_PROJECT (the project directory’s basename) is always set inside the VM.

Environment variables — explicitly stripped

Section titled “Environment variables — explicitly stripped”

These are never forwarded, even if set in your shell:

ANTHROPIC_*, CLAUDE_* # Anthropic credentials
AWS_* # AWS credentials
GITHUB_TOKEN # GitHub PAT
NPM_CONFIG_* # npm registry tokens, auth
HTTP_PROXY, HTTPS_PROXY # could be abused to redirect installs
SSH_AUTH_SOCK # SSH agent socket

If you need to pass a token in, do so explicitly:

Terminal window
fend --env GITHUB_TOKEN=ghp_xxx npm install
fend --env-file .env.local npm run seed

Values passed via --env or --env-file go into the process environment inside the VM only. The .env-file itself is parsed on the host and the values are sent over the vsock control channel — they are never written to any filesystem the VM can see.

The VM has full outbound internet access via the host’s NAT. It needs this to download packages from registries.

What the VM cannot do:

  • Listen on a host port directly. Listening sockets are detected by the guest agent and forwarded via the daemon, not by the VM binding to host interfaces.
  • Reach the host loopback (127.0.0.1). The VM’s 127.0.0.1 is the VM’s own loopback; it cannot reach services running on localhost on your Mac.
  • See your VPN routing, browser cookies, or other network state on the host.

A per-project network policy (allow / deny / log) is planned for the macOS app tier. For now the only knob is “is the VM running” — the alpha CLI does not block outbound traffic.

Defaults per project VM (override in .fend.toml):

ResourceDefault
CPUs2
Memory2 GB
Cold boot~800 ms
Warm dispatch~5 ms
Resume from pause~100 ms
Idle pause threshold5 min
Idle stop threshold30 min

Per-project state lives in ~/.fend/state/<hash>/. Stale entries (project directory deleted, or rootfs untouched for 30+ days) are reclaimed automatically.

The boundary is set conservatively because the threat model assumes hostile dependencies. There are legitimate reasons to widen it for a specific command:

  • Need a token? Use --env/--env-file. Per-call, never persisted.
  • Need Claude Code’s OAuth token for claude? Use fend claude — it injects CLAUDE_CODE_OAUTH_TOKEN from the macOS Keychain, but only for that command.
  • Need a tool that’s not in the runtime overlay? File an issue — we add tools to the curated overlay set rather than letting the VM reach the host.

Widening the boundary by reaching into ~/ from the VM is intentionally not supported. That’s the whole product.