escrow

module
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: MIT

README ΒΆ

πŸ”’ escrow

A supply-chain firewall for your package managers. escrow sits between your developers (or CI) and the public registries β€” npm, PyPI, Go, Cargo, NuGet, Maven/Gradle, Composer β€” and won't hand over a package until it has passed your policy: a minimum age, a clean OSV vulnerability record, a known publisher. One static binary, seven ecosystems, a real-time dashboard in the browser and the terminal.

Release License Go Platforms Ecosystems Single binary

developer / CI  β†’  escrow proxy  β†’  upstream registry
                         β”‚
                   policy engine
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           age          osv       publisher Β· popularity

A package that fails policy is removed from the manifest before the tool ever sees it β€” not an error a --force can override, just a version that appears not to exist. Blocked events surface in the dashboard, where an operator approves with one click. And because new CVEs land every day, escrow keeps re-scanning what you already pulled and flags anything that turned vulnerable after the fact.

npm PyPI Go Cargo NuGet Maven / Gradle Composer
βœ… βœ… βœ… βœ… βœ… βœ… βœ…

Real-time operator console β€” light/dark, color-blind-safe β€” and a full terminal UI (escrow-cli tui) for the same views over SSH.

Jump to: Why escrow? Β· Quick install Β· See it in action Β· Documentation


πŸ€” Why escrow?

Most supply-chain attacks don't exploit a clever bug β€” they ship a brand-new version of a package you already trust, from a hijacked maintainer account, and race to spread before anyone notices. By the time an advisory exists, the malware has been in node_modules for hours.

escrow's core bet is time. A version published today can't be installed through the proxy until it has aged past your threshold (say, 7 days) β€” long enough for the community, the registry, and the scanners to catch a bad release and pull it. The attacks below were all caught and removed within hours to a couple of days. With a 7-day quarantine, none would have reached a developer installing through escrow:

Incident What happened Closed by
ua-parser-js Β· Oct 2021 Hijacked maintainer account published 0.7.29 / 0.8.0 / 1.0.0 with a cryptominer + credential stealer β€” live on npm for ~4 hours πŸ—“οΈ age gate
coa & rc Β· Nov 2021 Same playbook days later: account takeover, malicious new versions, pulled within hours πŸ—“οΈ age gate
colors & faker Β· Jan 2022 Maintainer self-sabotage shipped an infinite-loop colors@1.4.44-liberty-2, breaking thousands of builds overnight πŸ—“οΈ age gate
Log4Shell Β· CVE-2021-44228 A known critical CVE in log4j-core (Maven) β€” not new, but catastrophic πŸ” OSV scan + πŸ” re-scan

The age gate closes the zero-day-malware-in-a-fresh-version window; the OSV scan and continuous re-scan close the known-CVE window β€” including CVEs disclosed after you downloaded a package. escrow is also deliberately honest about what it can't stop (postinstall hooks, typosquatting, git deps) β€” see Security model & threat coverage.

This is not "catches everything." It's a focused, layered gate over the most common and most time-sensitive attack window β€” and it tells you plainly where it doesn't reach.


πŸš€ Quick install

brew tap jverhoeks/tap
brew install escrow

brew services start escrow      # background service, auto-starts on login
# β†’ http://localhost:7888/dashboard
# credentials are printed to: $(brew --prefix)/var/log/escrow.log

Config lives at $(brew --prefix)/etc/escrow/escrow.toml; brew services restart escrow to reload. The formula also installs escrow-cli β€” the companion that routes your dev environment through the proxy, watches activity, and reloads config. See Routing traffic to escrow.

🐳 Docker
docker run -p 7888:7888 ghcr.io/jverhoeks/escrow:latest
# or, with a full debug config (all 7 ecosystems, admin / escrow):
cd docker/ && mkdir -p data && cp escrow.debug.toml data/escrow.toml && docker compose up -d
πŸ“¦ Binary
# pick your platform: darwin-arm64 Β· darwin-amd64 Β· linux-amd64 Β· linux-arm64
curl -L https://raw.githubusercontent.com/jverhoeks/escrow/main/escrow-darwin-arm64 -o escrow
chmod +x escrow && ./escrow            # binds 127.0.0.1:7888 (localhost only)
./escrow --host=0.0.0.0                # listen on all interfaces (team/CI use)

On first boot escrow generates escrow.toml with a random dashboard password and prints the credentials to stdout β€” save them.

πŸ”Œ Point your tools at the proxy β€” one command

No hand-editing .npmrc, pip.conf, settings.xml, config.toml… escrow-cli wires up every package manager on the machine to use the proxy, and backs up each file before it touches it:

escrow-cli config write                      # configure all tools, globally
escrow-cli config write --ecosystems npm,pypi  # …or just the ones you use
escrow-cli status                            # is the proxy up? which tools are wired up?
escrow-cli config restore                    # undo β€” restore the originals it backed up

It covers npm/pnpm/yarn/bun Β· pip/uv/poetry Β· go Β· cargo Β· nuget Β· maven/gradle Β· composer β€” and can also inject proxy env vars for GUI editors (VSCode, Zed) or add a kernel-level network redirect that catches every process. β†’ Routing traffic to escrow.


🌐 Supported ecosystems

Ecosystem Tools Proxy URL Config key
npm npm, pnpm, yarn, bun http://localhost:7888/ npm = true
PyPI pip, uv http://localhost:7888/pypi/simple/ pypi = true
Go modules go http://localhost:7888/go/ go = true
Cargo cargo http://localhost:7888/cargo/ cargo = true
Composer composer http://localhost:7888/composer/ composer = true
NuGet dotnet, nuget http://localhost:7888/nuget/index.json nuget = true
Maven / Gradle mvn, gradle http://localhost:7888/maven2/ maven = true

β†’ Step-by-step setup for each tool: per-tool quickstarts.


πŸ“Έ See it in action

A real-time operator console β€” light/dark, color-blind-safe (icons and color), with shared Activity and Ecosystem filters β€” plus a full terminal UI for the same views over SSH.

CVEs blocked by advisory Package tree (downloaded + flagged) Terminal UI β€” live feed
CVEs Package tree TUI live

β†’ Full visual tour: dashboard & terminal UI β†’


πŸ“š Documentation

Guide What's inside
Dashboard & terminal UI Every view, light/dark, the escrow-cli tui, approve/block β€” with screenshots
Policy, scanning & lists Age gate Β· OSV Β· publisher Β· popularity Β· continuous re-scan Β· settings & hot-reload Β· allow/blocklist
Routing traffic to escrow The 4 methods: config files Β· local Β· shell/launch env Β· network redirect β€” and a coverage matrix
Security model & threat coverage What it does and doesn't protect against Β· trust pipeline Β· dashboard hardening Β· comparison
Deployment, storage & alerts TLS Β· internal mirrors Β· health Β· disk cache Β· systemd Β· S3 storage Β· webhooks
Configuration reference Every escrow.toml key, with defaults
GitHub Actions One-step CI supply-chain gate + Renovate composition
escrow-cli reference All escrow-cli commands: setup Β· config Β· status Β· tui Β· live Β· reload
Per-tool quickstarts npm Β· pnpm Β· yarn Β· bun Β· pip Β· uv Β· go Β· cargo Β· composer Β· dotnet Β· maven Β· gradle

⚑ GitHub Actions

Use escrow as a one-step supply-chain gate in any CI pipeline β€” add it before your install steps, no other changes needed:

steps:
  - uses: actions/checkout@v6

  - uses: jverhoeks/escrow@v1
    with:
      ecosystems: 'npm'
      min-days: '7'
      osv-severity: 'HIGH'

  - uses: actions/setup-node@v6
    with: { node-version: '20' }

  - run: npm install --ignore-scripts   # automatically uses the escrow registry

Escrow exports NPM_CONFIG_REGISTRY, PIP_INDEX_URL, GOPROXY, etc. so every install routes through the proxy, and caches packages in the Actions cache between runs.

β†’ Full guide, inputs/outputs, caching, and Renovate composition: docs/github-actions.md.


πŸ”¨ Building from source

git clone https://github.com/jverhoeks/escrow
cd escrow
go build -o escrow     ./cmd/escrow        # proxy server
go build -o escrow-cli ./cmd/escrow-cli    # companion CLI (macOS / Linux)
go test ./...

πŸ“„ License

MIT Β© 2026 Jacob Verhoeks

Directories ΒΆ

Path Synopsis
cmd
escrow command
escrow-cli command
escrow-cli/tui
Package tui implements `escrow-cli tui`, an interactive terminal dashboard that reads the running proxy's authenticated API (with an offline event-log fallback handled in run.go).
Package tui implements `escrow-cli tui`, an interactive terminal dashboard that reads the running proxy's authenticated API (with an offline event-log fallback handled in run.go).
internal
accesslog
Package accesslog provides a ring-buffer server access log for the dashboard.
Package accesslog provides a ring-buffer server access log for the dashboard.
alerts
Package alerts dispatches webhook notifications for policy block events.
Package alerts dispatches webhook notifications for policy block events.
allow
Package allow manages a persisted allow-list of package versions that bypass all policy checks.
Package allow manages a persisted allow-list of package versions that bypass all policy checks.
block
Package block manages a persisted block-list of package versions that are always denied.
Package block manages a persisted block-list of package versions that are always denied.
cache
Package cache defines the Cache interface and provides disk, memory, and S3 backends for storing proxied package artifacts and metadata.
Package cache defines the Cache interface and provides disk, memory, and S3 backends for storing proxied package artifacts and metadata.
cireport
Package cireport provides a summary endpoint for CI pipeline integration, returning allow/block stats for the current run.
Package cireport provides a summary endpoint for CI pipeline integration, returning allow/block stats for the current run.
config
Package config handles escrow's TOML configuration: loading, validation, default generation, runtime access, and live-reload support.
Package config handles escrow's TOML configuration: loading, validation, default generation, runtime access, and live-reload support.
dashboard
Package dashboard implements the web-based operator console: authentication, live SSE event stream, allow/block management, settings, cache controls, and package inventory views.
Package dashboard implements the web-based operator console: authentication, live SSE event stream, allow/block management, settings, cache controls, and package inventory views.
dlstats
Package dlstats tracks per-version download counts and timestamps for the dashboard.
Package dlstats tracks per-version download counts and timestamps for the dashboard.
egress
Package egress implements a forward proxy for Docker build protection, with SSRF-safe DNS resolution, allow/block policy, and per-IP rate limiting.
Package egress implements a forward proxy for Docker build protection, with SSRF-safe DNS resolution, allow/block policy, and per-IP rate limiting.
egresslog
Package egresslog provides a rotating access log for the egress proxy, recording allowed and blocked outbound connections.
Package egresslog provides a rotating access log for the egress proxy, recording allowed and blocked outbound connections.
eventlog
Package eventlog provides a structured event ring buffer for policy decisions, with optional JSONL persistence and SSE fan-out for the dashboard live stream.
Package eventlog provides a structured event ring buffer for policy decisions, with optional JSONL persistence and SSE fan-out for the dashboard live stream.
gate
Package gate implements the query-parameter gate, a lightweight mechanism for wrapping upstream metadata responses with escrow's policy headers.
Package gate implements the query-parameter gate, a lightweight mechanism for wrapping upstream metadata responses with escrow's policy headers.
handler/cargo
Package cargo implements the Cargo sparse registry protocol proxy with age-gated and policy-filtered crate index responses.
Package cargo implements the Cargo sparse registry protocol proxy with age-gated and policy-filtered crate index responses.
handler/composer
Package composer implements the Composer/Packagist v2 metadata proxy with age-gated and policy-filtered version lists.
Package composer implements the Composer/Packagist v2 metadata proxy with age-gated and policy-filtered version lists.
handler/gomod
Package gomod implements the Go module proxy protocol (GOPROXY) with age-gated and policy-filtered module listings, info, mod, and zip responses.
Package gomod implements the Go module proxy protocol (GOPROXY) with age-gated and policy-filtered module listings, info, mod, and zip responses.
handler/maven
Package maven implements the Maven 2 repository layout proxy with age-gated and policy-filtered metadata and artifact responses.
Package maven implements the Maven 2 repository layout proxy with age-gated and policy-filtered metadata and artifact responses.
handler/npm
Package npm implements the npm registry proxy with age-gated, policy-filtered manifest responses and tarball caching.
Package npm implements the npm registry proxy with age-gated, policy-filtered manifest responses and tarball caching.
handler/nuget
Package nuget implements the NuGet v3 protocol proxy with age-gated and policy-filtered registration, version list, and package download responses.
Package nuget implements the NuGet v3 protocol proxy with age-gated and policy-filtered registration, version list, and package download responses.
handler/pypi
Package pypi implements the PyPI Simple API + JSON API proxy with age-gated and policy-filtered package listings and artifact caching.
Package pypi implements the PyPI Simple API + JSON API proxy with age-gated and policy-filtered package listings and artifact caching.
logfile
Package logfile holds shared helpers for the append-only JSONL observability logs (eventlog, egresslog).
Package logfile holds shared helpers for the append-only JSONL observability logs (eventlog, egresslog).
metrics
Package metrics exposes Prometheus metrics for escrow's proxy, cache, upstream, egress, and health endpoints, and provides a /metrics handler and /healthz handler.
Package metrics exposes Prometheus metrics for escrow's proxy, cache, upstream, egress, and health endpoints, and provides a /metrics handler and /healthz handler.
pkgname
Package pkgname provides ecosystem-specific package name normalization and validation.
Package pkgname provides ecosystem-specific package name normalization and validation.
pkgref
Package pkgref provides helpers for splitting and joining "name@version" package references.
Package pkgref provides helpers for splitting and joining "name@version" package references.
policy
Package policy implements the decision engine that applies trust signals (age, OSV, publisher, popularity) to package versions and returns an allow/warn/block verdict.
Package policy implements the decision engine that applies trust signals (age, OSV, publisher, popularity) to package versions and returns an allow/warn/block verdict.
rescan
Package rescan continuously re-evaluates previously downloaded packages against current policy, flagging newly vulnerable versions for operator review.
Package rescan continuously re-evaluates previously downloaded packages against current policy, flagging newly vulnerable versions for operator review.
ringbuf
Package ringbuf provides a lock-free ring buffer for high-throughput, bounded-size event storage.
Package ringbuf provides a lock-free ring buffer for high-throughput, bounded-size event storage.
server
Package server wires the HTTP server, TLS, graceful shutdown, middleware stack, and route mounting for the escrow proxy and its dashboard.
Package server wires the HTTP server, TLS, graceful shutdown, middleware stack, and route mounting for the escrow proxy and its dashboard.
staleserve
Package staleserve provides a middleware that serves stale cached metadata when the upstream is unreachable, within a configurable max-age window.
Package staleserve provides a middleware that serves stale cached metadata when the upstream is unreachable, within a configurable max-age window.
trust
Package trust implements reputation signals for policy evaluation: package age, OSV vulnerability lookups, publisher account age, and download popularity spike detection.
Package trust implements reputation signals for policy evaluation: package age, OSV vulnerability lookups, publisher account age, and download popularity spike detection.
upstream
Package upstream provides an HTTP client with retries, connection-pool metering, and error counting for upstream registry requests.
Package upstream provides an HTTP client with retries, connection-pool metering, and error counting for upstream registry requests.
upstreamlog
Package upstreamlog provides a ring buffer for upstream registry request logs, recording status, latency, and ecosystem per request.
Package upstreamlog provides a ring buffer for upstream registry request logs, recording status, latency, and ecosystem per request.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL