Configuration
Build a guest image from a Dockerfile at create time, without a separate build step.
The --context <dir> flag on create enables a self-contained build mode: nexus starts a buildkitd inside the VM, builds the image from the supplied Dockerfile context, and then boots the sandbox from the resulting image. No pre-built image reference is needed.
Implementation spelling partial
The flag is built today as --file. The target spelling is --context. All examples on this page use the target spelling.
Self-contained build flags
These flags are part of create. See Lifecycle commands for the full flag set.
| Flag | Type | Default | Description |
|---|---|---|---|
--context <dir> | string | — | Dockerfile build context directory on the host |
--dockerfile <path> | string | Dockerfile | Dockerfile path, relative to --context |
--context and --image / --rootfs are mutually exclusive: exactly one image source must be provided.
Example
nexus create myproject/dev-1 \
--context /data/repos/myrepo \
--memory 8192nexus sandbox create and --file; see CLI sandbox commands for the mapping.nexus copies the context directory into the VM, runs buildkitd, builds the image, and boots the sandbox. The build cache is stored on a virtio-blk disk and reused across subsequent --context creates for the same project.
What --context captures
The context directory is captured at create time. The capture includes:
- Tracked files (including dirty/modified tracked files)
- Untracked files
- Unpushed commits
It does not include .git history beyond what the working tree reflects.
Project config file (.nexus/config.yaml)
.nexus/config.yaml is an optional per-repository configuration file placed inside the .nexus/ directory at the repository root. nexus discovers it by walking up from the process working directory to the nearest directory that contains a .git entry (the repository root). An absent file is a no-op. A present but malformed file, or a file with an unknown YAML key, is a hard error.
version: 1
# Extend the outbound allowlist for all sandboxes in this repo.
egress:
allow:
- proxy.golang.org
- sum.golang.org
- storage.googleapis.com
# Path policies: destination-scoped allowlists (host-keyed).
policy:
# GitHub: policy entries define the allowed paths for this repo.
- host: github.com
paths: ["/owner/name/**"]
- host: api.github.com
paths: ["/repos/owner/name/**", "/repos/owner/name", "/user"]
- host: uploads.github.com
paths: ["/**"]
# Generic per-host path allowlist (any provider).
- host: api.example.com
paths: ["/v4/projects/123/**"]
# SECURITY WARNING — GitHub glob tightness is the author's responsibility.
# The policy layer is generic default-deny globs; the system cannot automatically
# narrow what you write. For GitHub hosts you MUST:
# • Scope every api.github.com path to /repos/<owner>/<repo>/** (plus specific
# endpoints like /repos/<owner>/<repo> and /user). Do NOT write /** or / at root.
# • Do NOT list /graphql under api.github.com. GraphQL is a parallel write channel
# that bypasses path-allowlist semantics; listing it reopens the sole-bound risk
# (the operator's full-scope token becomes the only protection).
# • Do NOT list /** under github.com.
# An unscoped or /graphql-listing GitHub glob reopens the sole-bound risk.
# A stricter parse-time graphql lint (automatic floor) is a future TBD option.
# Brokered VCS/API secrets — injected as 64-hex placeholders in the guest;
# real token swapped host-side by the MITM proxy (PDF-R-020).
secrets:
# GitHub
- env: GH_TOKEN
hosts: [github.com, api.github.com, uploads.github.com]
# Non-GitHub
- env: GITLAB_TOKEN
hosts: [gitlab.com]
# Generic API token
- env: API_TOKEN
hosts: [api.example.com]
# Default sandbox settings for this repo.
sandbox:
image: sha256:<digest> # default --image; overridden by --image flag
memory: 4096 # MiB; overridden by --memory flag
vcpus: 2 # overridden by --vcpus flag
agent: claude-code # default agent profile; overridden by --agent flag
mounts:
- ./src:/work/src # relative paths resolved from the repository root (the directory holding .nexus/)Flag precedence: explicit CLI flags win over .nexus/config.yaml values; .nexus/config.yaml values win over built-in defaults.
egress.allow is additive — config hosts are unioned with --allow-host flags; neither replaces the other.
A host is either open or policy-gated, never both. A host listed under egress.allow is open passthrough (any path, no credential). A host listed under egress.policy or egress.secrets[].hosts is policy-gated: default-deny on paths, credential brokered. Because the policy layer takes precedence, an allow entry for a policy-gated host would be silently inert — the file would claim open access the perimeter does not grant. config.Load therefore rejects the file at parse time (hostnames compare case-insensitively):
nexus config: host "github.com" is listed under egress.allow and egress.policy; a host can be open (allow) or policy-gated (policy/secrets), not both — remove it from egress.allowFor policy-gated github.com, public archive and release downloads (/<owner>/<repo>/archive/..., /<owner>/<repo>/releases/download/...) are already permitted without an allow entry; see Egress and perimeter.
sandbox.mounts is replaced by any explicit --mount flag on the command line. To use both, list all mounts in .nexus/config.yaml and omit --mount on the command line.
Validating the file
nexus config validate [dir] [--json]dir defaults to the current directory. The command walks up to the nearest .git boundary, loads .nexus/config.yaml, and applies the same checks every other nexus command applies at load time: strict unknown-key rejection, version range check, and the egress allow/policy host-overlap rejection.
Exit 0 on success:
ok: /path/to/repo/.nexus/config.yaml
version: 1
image: ghcr.io/owner/app:dev
containerfile: /path/to/repo/.nexus/Containerfile
egress: mode=policy-gated allow=3 policy=4 secrets=1image prints (unset) when sandbox.image is absent; containerfile prints (absent) when .nexus/Containerfile does not exist. egress mode is default (no egress keys), allow-only, or policy-gated. Exit 1 with the loader error on stderr for any parse failure. A missing .nexus/config.yaml is also exit 1 — unlike other commands where absence is a no-op; this verb exists to confirm the file is present and valid.
--json emits {"schema_version":1,"kind":"config_validate","data":{"path":"...","version":1,"image":"...","containerfile":"...","containerfile_present":true,"egress":{"mode":"policy-gated","allow_hosts":3,"policy_hosts":4,"secrets_hosts":1}}} on success, or the standard error envelope on failure.
Config source for worktree sandboxes
Worktree sandboxes (auto-created by the herdr plugin) read .nexus/config.yaml from the worktree's own checkout — the same file the --file build reads. Egress policy, brokered secrets, and sandbox.nested all come from that file. A change takes effect on the next worktree-sandbox create for that checkout; no push to the default branch is needed. A checkout without the file gets no egress policy and no nested opt-in; a malformed file is an error. See the agent skill for the authoring workflow.