Bulk resolver and vulnerability server; CycloneDX SBOMs are the only input
analyse now asks the depinder server, in bulk and by purl, for package facts and for vulnerabilities. The registries and the local Trivy/Grype scan stay as the fallback. Every lookup follows one rule: local SQLite cache, then one ask to the server bounded by a deadline and never retried, then the local fallback.
Breaking changes
- CycloneDX SBOMs are the only input. The lockfile and manifest parsers (npm/yarn, Maven/Gradle
deptree.txt, pip/pipenv, Composer, NuGet, Gemfile.lock) are removed, and with them the depinder/ results subfolder. Generate SBOMs with Syft or Trivy (a DepMiner run writes both) and point analyse at them. Other files are ignored; one info line gives their count.
- The old plugin names are aliases.
-p java (or maven, gradle) selects sbom-java, -p npm (or js, yarn, ...) selects sbom-npm, and so on for ruby/gem, python/pip, php/composer, dotnet/nuget. update takes the same names.
- External plugins are gone. A
plugins.json in the working folder is no longer read.
- SQLite is the only cache; MongoDB is removed. Every run uses
~/.dxw/depinder/cache/depinder.sqlite, or DEPINDER_CACHE_DB. analyse no longer switches to MongoDB when a depinder-mongo container runs, and MONGO_URI, MONGO_USER and MONGO_PASSWORD are not read.
- Cache subcommands removed:
depinder cache init, cache up / start and cache down / stop. depinder cache / cache info shows the SQLite path, size and row counts (fresh and expired); cache import <dir> is unchanged.
analyse --target is removed. The manifests that give Black Duck's Path prefix now come from DepMiner's index.json, found beside the input on its own. --depminer-index <file> points at one elsewhere; --no-depminer-index turns it off. The scanned repositories are no longer needed.
- Programmatic use: the package is now one CommonJS bundle.
dist/depinder.js (exporting mainCommand) and the depinder bin are the only code entries; deep imports of dist/<module> no longer resolve.
Bulk purl resolver
- On by default, at
https://libs.dxworks.org. It needs DEPINDER_RESOLVER_TOKEN; without it the run warns once and fetches everything from the registries.
analyse collects every dependency's purl first and asks the resolver about all of them in one phase, before any registry. Answers are written to the local cache as they stream in, so a run stopped halfway keeps them. What the resolver does not know, or anything when it is unreachable, goes to the registries package by package.
- Chunks of 2,000 purls are posted at once with one shared deadline, each once, with no retries. A failed request keeps what it delivered and turns the resolver off for the rest of the run, with one warning.
- Each purl is asked about once per process. Requests carry the run's cache max age; answers are stamped with the server's
confirmed_at.
depinder update asks the resolver first too, then the registries for what is left.
- Purls come from the SBOM, normalized to the resolver's canonical form: qualifiers and subpath dropped, npm, composer and NuGet names lowercased, PyPI names per PEP 503, Go module case restored from the component name (Trivy lowercases it), and an
UNKNOWN version becomes a versionless purl.
| Setting |
Meaning |
--resolver-url <url> / DEPINDER_RESOLVER_URL |
Where the resolver is (analyse and update). Default https://libs.dxworks.org |
DEPINDER_RESOLVER_TOKEN |
Bearer token for the resolver and the vulnerability server |
--no-resolver |
Skip the resolver (and the vulnerability server) for this run (analyse and update) |
DEPINDER_RESOLVER_MAX_WAIT_MS |
Budget for the whole bulk phase. Default 60000 |
DEPINDER_RESOLVER_CONCURRENCY |
Caps the chunks in flight. Default: all of them |
Vulnerability server
- With the token set and
--vuln-source naming both trivy and grype (the default), analyse sends its purls to the server's /vulnerabilities at the same time as /resolve. The server runs Trivy and Grype and merges their findings the same way the local scan does; the local scanners do not run. github is still matched locally and merged in.
- If any request fails or the deadline passes, the whole run falls back to the local scan, with a warning that says why. Only then do the local preflight and scanner database updates run.
sbom-scan-provenance.json records vulnerabilitySource (server, or local with a fallbackReason), the scanner versions and the database builds that answered. The run warns when a server database build is stale.
- On the local path, provenance now records the database build the scan actually read, not the one seen before the refresh.
| Setting |
Meaning |
--no-vuln-server |
Scan locally; the resolver is still used |
--vuln-server |
Keep the server's scan under --no-resolver |
DEPINDER_VULN_MAX_WAIT_MS |
How long to wait before scanning locally. Default 30000 |
DEPINDER_VULN_CHUNK_SIZE |
Purls per request. Default and maximum 5000 |
Registry fallback and cache
- Packages neither the cache nor the resolver answered are fetched by the same code the server runs, so a package gets the same facts either way.
--registry-limits / DEPINDER_REGISTRY_LIMITS set the requests at once per purl type, with an optional gap in ms (npm=16,cargo=1:1000). Defaults: golang 64, nuget 32, the rest 8.
- A transient registry failure (network error, timeout, 502/503/504) is retried once; a 429 is waited out once, honouring
Retry-After (capped at 60 s).
- Cached libraries expire. A library older than
--cache-max-age <duration> (90s, 30m, 12h, 7d; a bare number is seconds), else DEPINDER_CACHE_MAX_AGE, else 1d, counts as missing and is fetched again. The 24-hour miss TTL and --refresh are unchanged.
depinder update [updated_before] [plugins...] re-fetches the SQLite rows written before the date; with no date, the expired ones. It takes --cache-max-age, --resolver-url, --no-resolver and --registry-limits.
Fixes and output changes
- npm packages with no license get an empty Licenses cell on warm runs too; a warm run used to write the text
null.
*-licenses.csv reads licence strings the same way as the Black Duck columns, so both group a library under the same licence. Some labels change, e.g. GPL-3.0 is no longer read as GPL-3.0-or-later, Eclipse Public License v2.0 is EPL-2.0 (was EPL-1.0), MIT/Apache-2.0 is MIT OR Apache-2.0 (was Apache-2.0), and unrecognised licence text is kept as written instead of unknown.
- Upgrade guidance uses the resolver's pre-release flag. npm versions like
2.0.0-0 and Go pseudo-versions are now pre-releases; Maven -nightly, -canary, -unstable, -next, -insiders, -experimental, -pre and -a1/-b1 stay pre-releases.
- A yanked or unlisted version in use keeps its release date and licence, and is never counted as a version to move to. A missing release date leaves the date and age cells in
libs.csv blank instead of measuring from now.
- NuGet packages whose every version is unlisted take their licence from the highest unlisted version instead of reporting unknown.
- Composer and Cargo take the Black Duck
Component Link from the repository URL, falling back to the homepage; other types take the homepage, falling back to the repository.
- Go modules that Trivy lowercased are looked up under their real case, so their licences and links are found.
- An SBOM that fails to parse or scan is skipped whole, with one warning.
--profile (or DEPINDER_PROFILE=1) counts every outgoing request per host and adds the resolver:*, vuln:purls, vuln:server and vuln:server-wait phases.
Compatibility
- Node 24 or later, as before.
- Existing SQLite caches keep working: cache keys (
java:, npm:, ...) are unchanged. Rows cached before this version have no pre-release flag; for them depinder applies the same rule the resolver uses.
- MongoDB cache contents are not migrated; they are refilled from the resolver and the registries. Files an earlier
cache init left in ~/.dxw/depinder/cache/ (docker-compose.yml, init-mongo.js) can be deleted.
- CSV and Black Duck file names and columns are unchanged for SBOM input. The
Type column stays blank: Syft and Trivy SBOMs carry no dependency scope.