Security model
What vx trusts, what it checks, what the sandbox stops and what it does not, and how to report a problem. Report a vulnerability privately through GitHub’s private vulnerability reporting on vznjs/vx (Security → Report a vulnerability), never in a public issue.
What vx trusts
Section titled “What vx trusts”- Your configs.
vx.config.tsandvx.workspace.tsare programs vx runs in its own process. A config can do anything you can: vx validates what it returns, not what it does. What a config imports must already be installed: vx runs Bun with--no-install, so an import nonode_modulesprovides is refused, never fetched from the registry (L-22). - Your plugins. A plugin is code you imported. It runs in vx’s process with a config’s reach; vx checks the shape of what it hands back and isolates a telemetry plugin’s failures, nothing more.
- Your lockfile and your repo. vx parses a lockfile (and
turbo.jsonor an Nx graph when migrating) into keys and refuses one it cannot read, naming the file. A lockfile that names another install is a different key, never a code path. - Whoever can write your remote cache. A cache key names the
inputs, not the bytes, so a store that answers a key with bytes of its
choosing is trusted as a writer, as it is in every action cache. Use a
store only your builds write to, or
turboCache()’s signature key, which refuses an artifact a keyholder did not sign.
What vx checks on bytes it did not write
Section titled “What vx checks on bytes it did not write”Every artifact that arrives from a remote, and every one read back from the local store:
- ends in a CRC-32 of its entries: a byte changed in transit or at rest makes the hit a miss, never a replay of the damaged bytes;
- on arrival, records the cache key it was packed under, and is refused under another (a local read-back is not re-checked);
- on arrival, carries only the files the task declares as outputs;
- lands each file under the task’s project or declared workspace path: traversal and names that escape are refused before anything lands, a refused archive leaves nothing behind, and links and devices are never written;
- is bounded: a decompression bomb, an oversized header or body, and a remote answer larger than asked for are refused as they are read.
Remote execution (@vzn/vx-reapi) holds each blob to its digest, and
writes a server’s outputs only inside the workspace, through no link
that leads out of it.
What the sandbox stops
Section titled “What the sandbox stops”A task with exec.sandbox runs where only what it declares exists:
- reads of the workspace outside the task’s project and grants fail;
- writes outside its
allow.writegrants fail (declared outputs grant none); - the network is closed except to the domains granted, one union per run: a task granted any domain reaches every domain the run grants;
- its temp directory and port-bridge socket are its own (mode 0700), unreachable from another task and another local user.
An undeclared touch of the task’s own files fails the task, and a failed task is never cached. See Sandboxing tasks.
What it does not stop
Section titled “What it does not stop”- A task with no
exec.sandbox. It runs with your permissions. - Reads outside the workspace root.
~/.cache,/etcand the like stay readable (tools need them) and fold into no key. - Resource use. CPU, memory and disk are bounded by timeouts and the artifact ceiling, not by the sandbox.
- A weaker sandbox where the host cannot nest one:
weakerWhenNestedin a container, a task that itself sandboxes on macOS.vx infosays what the host supports.
Secrets
Section titled “Secrets”The value, of 6 characters or more, of a variable whose name holds
TOKEN, SECRET, KEY, PASSWORD, PASSWD or CREDENTIAL (not one
ending _FILE, _PATH or _DIR, nor GIT_CONFIG_KEY_<n>), or that a
task lists in exec.env.secret, is printed as *** wherever vx shows, stores or
exports it: task output, the stdout a hit replays, commands, telemetry
and vx show. Values in the run history are digests under a per-store
salt, and what follows -- on the command line reaches telemetry as a
count, not a quote.
Releases
Section titled “Releases”Release binaries carry a build-provenance attestation
(gh attestation verify vx-<target> --repo vznjs/vx); vx upgrade
checks each download’s SHA-256 before it replaces anything; npm packages
publish with provenance; and every third-party action in CI runs from a
full commit SHA, held by a test.