Manage Dependencies and Caching
Running a package install inside an untrusted sandbox creates a dilemma. The packages have to be downloaded, but giving an agent full Internet access risks data exfiltration or credential theft.
DynoCap solves this by pairing filtered network egress with lockfile-keyed dependency seeds. Each runtime has exactly one package manager. The runtime declares which registry hosts filtered egress may reach and how its package cache is keyed.
This guide shows you how to:
- Warm a dependency seed in a one-time sandbox whose egress is restricted to your runtime’s registry.
- Reuse that seed in later sandboxes that have no network device.
- Invalidate or refresh the seed when your lockfile changes.
Runtimes at a glance
| Runtime | Package manager | Seed key (lockfile) | Filtered egress allows | Online install | Offline install |
|---|---|---|---|---|---|
node:24 |
npm | package-lock.json |
registry.npmjs.org |
npm ci |
npm ci --offline |
node:22 |
npm | package-lock.json |
registry.npmjs.org |
npm ci |
npm ci --offline |
python:3.14 |
uv | uv.lock |
pypi.org, files.pythonhosted.org |
uv sync --frozen |
uv sync --offline --frozen |
go:1.27 |
Go modules | go.sum |
proxy.golang.org, sum.golang.org |
go mod download |
go mod download |
shell:1 |
none | no seed | only a task repository URL’s host | n/a | n/a |
When --runtime is omitted, the project’s files choose the runtime (package.json → node:24, pyproject.toml → python:3.14, go.mod → go:1.27); anything else gets shell:1. Prepare a runtime once per home before you use it, for example dynocap runtime prepare . --runtime python:3.14.
Some files change the seed key besides the lockfile: .npmrc for npm and uv.toml for uv. The prepared runtime’s digest is also part of the key, so a seed is never restored into a different runtime or version. uv uses the runtime’s Python and never downloads an interpreter. Go runs with GOTOOLCHAIN=local and never switches to another toolchain.
The workflow pattern: “deps” sandbox vs. “worker” sandbox
- Warm step: Clone into a sandbox named
depswith--network filtered, run the online install, andfinish. Finishing publishes the package cache as a seed. - Offline step: Clone again with the default
--network none. DynoCap restores the matching seed before your first command, and the offline install needs no network.
+-------------------------------------------------------------+
| 1. WARM SEED (deps) |
| dynocap clone ./project deps --runtime python:3.14 \ |
| --network filtered |
| dynocap exec deps -- uv sync --frozen |
| dynocap finish deps ---> Saves a seed keyed by uv.lock |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| 2. OFFLINE TASKS (worker) |
| dynocap clone ./project worker --runtime python:3.14 |
| dynocap exec worker -- uv sync --offline --frozen |
| (Installs from the restored seed, zero network) |
+-------------------------------------------------------------+Step 1: Warm the dependency seed
Make sure your project contains its runtime’s lockfile, such as package-lock.json, uv.lock or go.sum. Then clone it with filtered networking:
dynocap clone ./my-project deps --runtime node:24 --network filteredWhy
--network filtered? Under filtered mode, DynoCap connects a virtio-net interface whose host forwarder answers DNS only for the runtime’s registry hosts and, for a repository URL, that URL’s host. It forwards only HTTPS to those names. Every other address and port is dropped.
Run the online install:
dynocap exec deps --timeout 300 -- npm ciFinish the sandbox:
dynocap finish depsFinishing archives the runtime’s package cache (for example /work/npm-cache/_cacache, /work/uv-cache or /work/go/pkg/mod/cache/download) into ~/.dynocap/dependency-cache/<key>.tar on the host. A seed is only published if the lockfile is unchanged at finish. It is also skipped if any command pointed the package manager at another registry or cache, for example through npm_config_*, UV_INDEX*, GOPROXY or GOFLAGS, or a --registry or --index argument.
Clean up the finished record:
dynocap rm depsStep 2: Run worker tasks completely offline
Clone your project again for your agent or test runner, leaving the network at its default (none):
dynocap clone ./my-project worker --runtime node:24If the lockfile matches a seed, DynoCap unpacks it into the guest before clone returns. Run the offline install:
dynocap exec worker -- npm ci --offlineThen run your tests or let your coding agent work:
dynocap exec worker -- npm testWhich tasks publish a seed
A seed is the package cache a networked install produced, and later offline sandboxes trust it. DynoCap therefore publishes a seed only from a task where:
- every command was one of the runtime’s install commands:
npm ciornpm installfor node,uv syncfor python,go mod downloadfor go (a plainsh -c "npm ci"from the SDK counts; anything with shell syntax does not); - no command pointed the package manager at another registry or cache.
A task that runs anything else, such as npm test, a shell, or a script, never publishes or republishes a seed, because that code could rewrite the cache. Keep the deps sandbox to the install alone.
Seeds are keyed by the task’s source (its directory or repository URL) as well as the lockfile, so a seed never crosses into another project.
A seed is only as trustworthy as the install that produced it: packages’ install scripts ran with network access. npm re-checks each package against the lockfile’s integrity hashes when it installs offline; uv and Go reuse their caches as they are.
Cache invalidation and updates
- If you change dependencies: a lockfile change produces a different key, so later clones find no matching seed.
- To update the seed: re-run Step 1 with
--network filtered. - Location and size: seeds live in
~/.dynocap/dependency-cache/. They are capped at 2 GiB in total, and the least recently restored seeds are evicted first. You can inspect or delete them safely:Shell ls -lh ~/.dynocap/dependency-cache
Troubleshooting
A registry name does not resolve in a --network none sandbox
The sandbox has no network device. Use the offline form of the install: npm ci --offline, uv sync --offline --frozen, or go mod download after a seed was restored.
Download blocked during an install in filtered mode
Filtered egress allows HTTPS only to the hosts in the table above. Dependencies from private Git repositories, a custom registry (Artifactory, GitHub Packages, a private PyPI index) or a Go GOPROXY other than proxy.golang.org are blocked. In those cases use --network nat, which is unrestricted. A seed is still not published when a command overrides the registry.
This page is also plain Markdown, for agents and for you.