dproxy
Run untrusted developer toolchains — node, npm, npx, bun, python, uv,
go, cargo, rustc, and more — in disposable, locked-down containers so
package and build code cannot reach unrelated host resources. dproxy proxies the
whole toolchain, not only package installation.
dproxy is a single stateless host binary (no daemon, no persistent containers).
Every command runs in a fresh --rm container with CAP_DROP=ALL, a read-only
root filesystem, no-new-privileges, and explicit resource limits. Networking is
deny-by-default and, when enabled, is filtered through a digest-pinned gateway.
Host support
Linux today. dproxy runs on Linux hosts with Docker Engine.
macOS and Windows are tracked same-release work, not a future version: the
gateway dataplane backends (PF on darwin, WFP on windows) and the non-Linux
hardened-filesystem-ownership backends are not yet implemented. See
docs/platform-backends.md for the concrete work
items and their fail-closed acceptance criteria. The design commitment is
all-OS from day one — see the
design spec.
Prerequisites
- Linux host
- Docker Engine (API ≥ 1.40), reachable by the invoking user
- A Go toolchain is only required to build dproxy itself
Install
go install github.com/i-rocky/dproxy/cmd/dproxy@latest
dproxy install
dproxy install wires global shims and shell integration (PATH + completion)
into your shell rc with no project required. Restart your shell (or re-source
its rc) afterward. On first use of a tool, dproxy auto-locks that tool's image
digest into a global project so the digest-pinning invariant holds even without
a prior dproxy init.
Per-project use
cd my-project
dproxy init # writes .dproxy.toml (requested tools + sandbox policy)
dproxy lock # resolves images and pins digests to .dproxy.lock
dproxy npm install # runs `npm install` sandboxed
dproxy <tool> [args] runs the tool in the foreground and relays its terminal,
signals, and exit status. dproxy --explain <tool> prints the resolved plan
(mounts, egress allowlist, resource limits) without running anything.
How it works
For each invocation dproxy finds the nearest .dproxy.toml, verifies
.dproxy.lock, resolves the tool image by immutable digest, builds the
sandbox policy, and starts the command container. When networking is enabled it
also creates a per-invocation network and a trusted filtering gateway that
enforces DNS redirection to a validated resolver and pinned-endpoint allowlists.
All per-invocation containers and networks are removed when the command exits.
See the design spec for the
full architecture, trusted computing base, and threat model.
Security posture
dproxy exposes no host resources except explicitly mounted project paths and
dproxy-managed caches. See SECURITY.md for the full policy.
Two limitations are stated honestly:
- The project directory is mounted read/write. Tools must be able to create
lockfiles, dependencies, and build output, so sandboxed code can read and
modify project contents — including
.env, source, and .git when present.
Host files are protected by being absent, not by UID isolation.
- Registry-front egress has a residual channel. When a tool's allowlist is
derived from its manifest (e.g.
npm → registry.npmjs.org), shared CDN
fronts mean an allowed destination cannot always be distinguished from an
unrelated one on the same front.
Documentation
License
MIT — see LICENSE.