npx packrai RunTimeAdmin/myapp@v2.1.0Produces CycloneDX 1.6 and SPDX 2.3 in under 500ms. No Docker. No agents. No config files.
# Scan a GitHub repo at a specific tag
npx packrai owner/repo@v1.2.0
# Scan the current directory
npx packrai .
# Scan a local directory
npx packrai ./my-project
# Private repo (uses $GITHUB_TOKEN automatically)
npx packrai owner/private-repo
# Diff two SBOMs — see what changed between releases
npx packrai diff old.cyclonedx.json new.cyclonedx.json
# Check for forbidden/restricted licenses
npx packrai . --license-check
# Skip vulnerability lookup (faster, offline-safe)
npx packrai owner/repo --no-vulns
# AI remediation advice — explains vulns and produces a prioritised fix plan
DEEPSEEK_API_KEY=<your-deepseek-key> npx packrai . --explain
# AI explain with CISA KEV context — flags actively-exploited vulns
DEEPSEEK_API_KEY=<your-deepseek-key> KATZILLA_API_KEY=<your-katzilla-key> npx packrai . --explainOutput files written to the current directory:
bom.cyclonedx.json ← CycloneDX 1.6
bom.spdx.json ← SPDX 2.3
flowchart LR
subgraph sources["Lock Files (14 ecosystems)"]
direction TB
A["package-lock.json\nyarn.lock · pnpm-lock.yaml"]
B["Cargo.lock\ngo.mod · poetry.lock"]
C["pom.xml · Gemfile.lock\ncomposer.lock · packages.lock.json"]
end
subgraph engine["PackrAI Engine"]
direction TB
D[Detect] --> E[Parse dep graph]
D --> DF[Dockerfile audit\nsecurity findings]
E --> F[Enrich: licenses\ndeps.dev API]
E --> G[Enrich: vulns\nOSV batch API]
DF --> BV[Base image CVEs\nDocker SBOM attestation]
G --> N[CISA KEV flag\nkatzilla.dev]
G --> O[AI explain\nDeepSeek-V3]
end
subgraph output["Output"]
direction TB
H[CycloneDX 1.6]
I[SPDX 2.3]
J[Quality score 0–100]
end
subgraph platform["Central Platform (optional)"]
K[PackrAI API]
L[(PostgreSQL)]
M[Dashboard]
K --> L --> M
end
sources --> engine
F --> output
G --> output
output -->|ingest| platform
Lock files are the resolved dependency graph — authoritative, deterministic, and exact. PackrAI reads them directly instead of walking the filesystem, which is why it's 60–187× faster than Syft and Trivy on equivalent repos.
| PackrAI | Syft | Trivy | |
|---|---|---|---|
| Speed | 250–465ms | 9–28s | 10–87s |
| Approach | Lock-file parsing | Filesystem scan | Filesystem + image scan |
| Transitives | Full graph | Partial | Partial |
| Dep graph | ✅ | ✅ | ❌ |
| Zero config | ✅ | ❌ | ❌ |
| GitHub URL | ✅ | ❌ | ❌ |
| SBOM diff | ✅ | ❌ | ❌ |
| License policy | ✅ | ❌ | ❌ |
| VEX support | ✅ | ❌ | ❌ |
| Central repo | ✅ | ❌ | ❌ |
| CISA KEV flag | ✅ | ❌ | ❌ |
| AI remediation | ✅ | ❌ | ❌ |
| Dockerfile audit | ✅ | ❌ | ❌ |
| Base image CVEs | ✅ (no Docker) | ❌ | ✅ (requires Docker) |
| Repo | Ecosystem | PackrAI | Syft | Trivy |
|---|---|---|---|---|
nestjs/nest |
npm | 463ms | 27 731ms | 86 550ms |
psf/requests |
Python | 251ms | 9 330ms | 10 502ms |
BurntSushi/ripgrep |
Rust | 268ms | 11 823ms | 15 425ms |
Syft and Trivy run via Docker in this benchmark, adding ~3–5s of startup overhead. Native installs would be somewhat faster — but still an order of magnitude slower on lock-file repos. Reproduce with npm run bench.
Produces fully-spec-compliant CycloneDX 1.6 and SPDX 2.3 with all CISA 2025 minimum elements: component name, version, supplier, purl, cryptographic hashes, license identifiers, dependency relationships, author, timestamp, and tool metadata.
Every component is checked against the OSV database in a single batch call. Severity, CVSS score, and fix version are included in the SBOM output.
npx packrai . --license-checkCategorises each component's license as permissive, notice, restricted (weak copyleft — review required), or forbidden (strong copyleft). Exits 1 if any forbidden license is found. Produces a license compliance score 0–100.
npx packrai diff v1.0.0.cyclonedx.json v1.1.0.cyclonedx.jsonShows exactly what changed between two releases: components added/removed/updated, and new or resolved vulnerabilities. Exits 1 if new vulnerabilities were introduced. Available both as a CLI command and via the API (GET /api/v1/apps/:name/diff).
Mark vulnerabilities as not_affected, fixed, affected, or under_investigation via the API. not_affected statements suppress vulns from risk reports, keeping dashboards signal-rich.
Every vulnerability is automatically cross-referenced against the CISA Known Exploited Vulnerabilities catalog (1,600+ entries, refreshed daily). Vulns on the KEV list are flagged kev: true in the database and surfaced in the AI explain output as highest priority — because being actively exploited in the wild is categorically different from a theoretical CVSS score.
Requires KATZILLA_API_KEY (katzilla.dev). The catalog is fetched once at API startup then refreshed every 24 hours. New vulns are cross-referenced immediately at ingest time without waiting for the refresh cycle.
DEEPSEEK_API_KEY=<your-deepseek-key> npx packrai . --explainAfter scanning, sends your vulnerability list to DeepSeek-V3 and returns:
- 2–3 sentence plain-English risk summary
- Prioritised upgrade plan ordered by impact (most vulns resolved per change first)
- Specific mitigation guidance for vulns with no fix available
- CISA KEV callouts for actively-exploited entries
Available both as a CLI flag (--explain) and as a REST endpoint (POST /api/v1/apps/:name/explain). Requires DEEPSEEK_API_KEY. The API endpoint returns 501 if the key is not configured, so it degrades gracefully.
PackrAI automatically detects and audits every Dockerfile in your project tree (including Dockerfile.prod, Dockerfile.dev, etc.) and reports security findings alongside the SBOM:
| Rule | Severity | What it catches |
|---|---|---|
unpinned-base-image |
HIGH | :latest tag or no tag |
no-digest-pin |
MEDIUM | tag present but no @sha256:... digest |
explicit-root-user |
HIGH | USER root or USER 0 |
no-user-directive |
MEDIUM | no USER directive (defaults to root) |
secret-in-env |
HIGH | ENV/ARG with password/token/secret keyword |
add-instead-of-copy |
LOW | ADD used for plain file copies |
no-healthcheck |
LOW | no HEALTHCHECK directive |
Multi-stage builds are detected. Use --no-docker to skip Dockerfile scanning entirely.
For Docker Official Images (node, nginx, python, ubuntu, etc.), PackrAI pulls the SBOM attestation directly from the Docker Hub OCI registry and queries OSV for known CVEs — with no Docker installation required.
Dockerfile · 2 MEDIUM 1 LOW · multi-stage
[MEDIUM] no-digest-pin:1 Base image 'node:20-alpine' has no digest pin
base: node:20-alpine · 32 CVEs
32 base-image CVEs
How it works:
- Anonymous OAuth token from Docker Hub auth service
- Fetch the OCI image index (multi-platform manifest list)
- Locate the linux/amd64 SBOM attestation entry
- Pull the in-toto statement blob containing the SPDX 2.3 package list
- Batch-query OSV for CVEs across all OS packages
Base image components appear in the CycloneDX output as type: container with pkg:docker/library/node@20-alpine PURLs. If no SBOM attestation is available (private or older images), the lookup returns no CVE data gracefully without failing the pipeline.
Every SBOM gets a completeness score (0–100) measuring alignment with CISA 2025 minimum elements: purl coverage, hash coverage, license coverage, and lock-file fidelity.
name: SBOM
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
pull-requests: write
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: PackrAI
uses: RunTimeAdmin/PACKRAI@v1
with:
format: both
fail-on-critical: true
upload-artifact: trueOn every pull request, PackrAI will:
- Generate CycloneDX 1.6 + SPDX 2.3 SBOMs
- Post a summary comment to the PR with vulnerability count and quality score
- Upload SBOMs as artifacts with 90-day retention
- Block merge if critical vulnerabilities are found
| Input | Default | Description |
|---|---|---|
format |
both |
both · cyclonedx · spdx |
output-dir |
sbom |
Directory to write SBOM files |
fail-on-critical |
true |
Exit 1 if critical vulnerabilities found |
upload-artifact |
true |
Upload SBOMs as GitHub Actions artifacts |
skip-vulns |
false |
Skip OSV enrichment (faster, offline-safe) |
api-url |
"" |
PackrAI central API endpoint |
api-key |
"" |
PackrAI API key (secrets.PACKRAI_API_KEY) |
directory |
. |
Directory to scan |
| Output | Description |
|---|---|
cyclonedx-path |
Path to generated CycloneDX 1.6 BOM |
spdx-path |
Path to generated SPDX 2.3 BOM |
component-count |
Total components enumerated |
vulnerability-count |
Total known vulnerabilities |
critical-count |
Critical severity vulnerabilities (CVSS ≥ 9.0) |
quality-score |
SBOM completeness score 0–100 |
- name: PackrAI
uses: RunTimeAdmin/PACKRAI@v1
with:
format: both
fail-on-critical: true
upload-artifact: true
api-url: ${{ vars.PACKRAI_API_URL }}
api-key: ${{ secrets.PACKRAI_API_KEY }}When api-url and api-key are set, SBOMs are automatically pushed to your PackrAI central instance for org-wide tracking. Upload failures do not fail the build.
| Ecosystem | Lock File | Transitives | Notes |
|---|---|---|---|
| npm | package-lock.json v1/v2/v3 |
✅ Full graph | Hoisting-aware resolver |
| npm | pnpm-lock.yaml v6/v9 |
✅ Full graph | Peer suffix handling |
| npm | yarn.lock v1 |
✅ Full graph | |
| Python | poetry.lock |
✅ Full graph | |
| Python | Pipfile.lock |
✅ Full graph | |
| Python | requirements.txt |
Warns on missing transitives | |
| Rust | Cargo.lock |
✅ Full graph | SHA-256 checksums |
| Go | go.mod + go.sum |
✅ Full graph | Direct/indirect detection |
| Java | pom.xml |
✅ + mvn transitives |
Resolves ${property} vars |
| Java | gradle.lockfile |
✅ Full graph | Requires --write-locks |
| .NET | packages.lock.json |
✅ Full graph | SHA-512 hashes |
| Ruby | Gemfile.lock |
✅ Full graph | SHA-1 checksums via Bundler |
| PHP | composer.lock |
✅ Full graph | Licenses from package metadata |
| Swift | Package.resolved |
Git SHA hashes | |
| Dart/Flutter | pubspec.lock |
SHA-256 hashes |
Monorepos are supported — PackrAI recurses up to 4 directories deep and deduplicates lock files per directory.
packrai <source> [options]
packrai diff <from> <to> [options]
Arguments:
source Local path, owner/repo[@ref], or https://github.com/... URL
Scan options:
-o, --out <dir> Output directory (default: current directory)
-n, --name <name> Project name override
-v, --ver <version> Version override
-a, --author <org> Author or organisation name
--token <token> GitHub token for private repos (or set $GITHUB_TOKEN)
--format <fmt> both | cyclonedx | spdx (default: both)
--license-check Flag forbidden/restricted licenses; exit 1 if any found
--explain AI remediation advice via DeepSeek-V3 (requires DEEPSEEK_API_KEY)
--no-vulns Skip OSV vulnerability enrichment
--no-licenses Skip deps.dev license enrichment
--no-docker Skip Dockerfile audit and base image CVE lookup
--no-recursive Do not recurse into subdirectories
--json Print summary as JSON (machine-readable, for CI)
Diff options:
--json Machine-readable JSON diff output
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Critical vulnerabilities found, or forbidden license detected (--license-check) |
2 |
Fatal error — no lock files, clone failed, or unrecoverable parse error |
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"components": [
{
"type": "library",
"name": "express",
"version": "4.18.2",
"purl": "pkg:npm/express@4.18.2",
"licenses": [{ "license": { "id": "MIT" } }],
"hashes": [{ "alg": "SHA-512", "content": "..." }],
"scope": "required"
}
],
"dependencies": [
{ "ref": "pkg:npm/express@4.18.2", "dependsOn": ["pkg:npm/accepts@1.3.8"] }
],
"vulnerabilities": [
{
"id": "GHSA-rv95-896h-c2vc",
"ratings": [{ "score": 7.5, "severity": "high", "method": "CVSSv3" }],
"affects": [{ "ref": "pkg:npm/express@4.18.2" }]
}
]
}{
"spdxVersion": "SPDX-2.3",
"packages": [...],
"relationships": [
{
"spdxElementId": "SPDXRef-express-4.18.2",
"relationshipType": "DEPENDS_ON",
"relatedSpdxElement": "SPDXRef-accepts-1.3.8"
}
]
}Both formats include all CISA 2025 minimum elements, full transitive dependency relationships, cryptographic hashes, SPDX license identifiers, and OSV vulnerability data.
The PackrAI API server answers org-wide questions like:
"Which of our apps are exposed to CVE-2021-44228, and do any of them have a fix available?"
flowchart TD
subgraph ci["CI/CD (GitHub Actions)"]
A[PackrAI Action] -->|POST /ingest| B
end
subgraph api["PackrAI API"]
B[Ingest endpoint]
C[Search endpoint]
D[Report endpoint]
E[VEX endpoint]
F[Diff endpoint]
P[Explain endpoint\nDeepSeek-V3]
end
subgraph db["PostgreSQL"]
G[(organizations)]
H[(sboms)]
I[(components)]
J[(vulnerabilities\n+ kev flag)]
K[(vex_statements)]
L[(app_latest_sboms)]
M[(kev_catalog\nCISA KEV)]
end
B --> G & H & I & J & L
C --> I & J & K
D --> L & J & K
E --> K
F --> H
P --> J & M
# With Docker (recommended)
cp .env.example .env # set HMAC_SECRET and POSTGRES_PASSWORD
docker compose up -dAPI available at http://localhost:3080.
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/ingest |
Ingest a new SBOM (called by the Action) |
GET |
/api/v1/apps |
List all apps with risk summary |
GET |
/api/v1/apps/:name/diff |
Diff latest two SBOMs for an app |
GET |
/api/v1/search?cve=CVE-... |
Which apps are exposed to this CVE? |
GET |
/api/v1/report |
Org-wide risk report |
POST |
/api/v1/apps/:name/explain |
AI vulnerability summary + remediation plan |
POST |
/api/v1/vex |
Add a VEX statement |
GET |
/api/v1/vex |
List VEX statements |
POST |
/api/v1/keys |
Issue a scoped API key |
See src/api/schema.sql for the full database schema.
git clone https://github.com/RunTimeAdmin/PACKRAI
cd PACKRAI
npm install
npm test # unit tests (66 tests, no external deps)
npm run e2e # integration tests (requires docker compose up -d)
npm run bench # benchmark vs Syft and Trivy
node bin/packrai.js . --no-vulns # run the CLI locallypackrai/
├── bin/packrai.js CLI entry point (scan + diff subcommands)
├── src/
│ ├── pipeline.js Orchestration: detect → parse → enrich → generate
│ ├── diff.js SBOM diffing: components and vulnerabilities
│ ├── licensePolicy.js License tier classification and compliance scoring
│ ├── component.js Shared component model + purl generation
│ ├── github.js GitHub URL parsing + shallow clone
│ ├── osv.js OSV vulnerability enrichment (batch API)
│ ├── kev.js CISA KEV catalog sync (katzilla.dev, daily refresh)
│ ├── explain.js AI remediation advice (DeepSeek-V3 via OpenAI SDK)
│ ├── licenses.js License enrichment (deps.dev API)
│ ├── basevuln.js Base image CVE lookup via Docker SBOM attestations (OCI + OSV)
│ ├── parsers/
│ │ ├── npm.js package-lock.json v1/v2/v3, yarn.lock
│ │ ├── pnpm.js pnpm-lock.yaml v6/v9
│ │ ├── python.js poetry.lock, Pipfile.lock, requirements.txt
│ │ ├── cargo.js Cargo.lock
│ │ ├── golang.js go.mod + go.sum
│ │ ├── maven.js pom.xml
│ │ ├── gradle.js gradle.lockfile
│ │ ├── dotnet.js packages.lock.json (NuGet)
│ │ ├── ruby.js Gemfile.lock
│ │ ├── php.js composer.lock
│ │ ├── swift.js Package.resolved
│ │ ├── dart.js pubspec.lock
│ │ ├── detect.js Lock file detection + deduplication; Dockerfile discovery
│ │ ├── dockerfile.js Dockerfile static security audit (7 rules, no Docker required)
│ │ └── index.js Parser dispatcher
│ ├── generators/
│ │ ├── cyclonedx.js CycloneDX 1.6 generator + validator
│ │ └── spdx.js SPDX 2.3 generator
│ └── api/
│ ├── server.js Express API server
│ ├── db.js PostgreSQL connection pool + transaction helper
│ └── schema.sql Database schema
├── deploy/
│ ├── migrate_001_app_latest_sboms.sql
│ ├── migrate_002_vex.sql
│ └── docker-compose.yml API + Postgres production stack
├── tests/
│ ├── parsers.test.js Parser unit tests
│ └── fixtures/ Sample lock files (all 14 ecosystems)
├── examples/
│ └── github-workflow.yml Annotated copy-paste workflow
└── action.yml GitHub Action definition
- Write
src/parsers/<ecosystem>.js— export aparse*function returningComponent[] - Add detection entry in
src/parsers/detect.js(LOCK_FILE_BY_NAME) - Add dispatcher case in
src/parsers/index.js - Add
case '<ecosystem>'insrc/component.jsmakePurl() - Add fixture in
tests/fixtures/and tests intests/parsers.test.js
| Standard | Version | Status |
|---|---|---|
| CycloneDX | 1.6 | ✅ Full |
| SPDX | 2.3 | ✅ Full |
| NTIA Minimum Elements | 2021 | ✅ All 7 fields |
| CISA Minimum Elements | 2025 | ✅ purl, hashes, licenses, relationships, metadata |
| EO 14028 | — | ✅ Supply chain security |
| OpenVEX / CycloneDX VEX | — | ✅ Via API |
PackrAI is a lock-file-first SBOM generator. This makes it fast and deterministic, but it does not replace container or filesystem scanners for all use cases.
| Area | Detail |
|---|---|
| Container/image scanning | Dockerfile static audit + base image CVE lookup via SBOM attestation (no Docker required). Does not walk running container layers or produce a full container SBOM. Use Trivy for full container image analysis. |
| Compiled binaries | SBOMs are generated from lock files, not compiled output or vendored binaries. |
| No lock file → no SBOM | requirements.txt produces direct-only output with a warning. Projects without any committed lock file are not supported. |
| Java (Maven) | Transitive resolution requires mvn to be installed locally. Without it, direct deps only. |
| Swift / Dart | Flat resolved set only — no dependency graph available in the lock file format. |
| Gradle | Requires --write-locks to produce gradle.lockfile. |
| .NET | Requires RestorePackagesWithLockFile=true and a committed packages.lock.json. |
MIT — see LICENSE
