You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 6c8fb35
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: README.md
+12Lines changed: 12 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1343,6 +1343,17 @@ Registered when `hosted.enabled` is set. Pushing and deleting require `Authoriza
1343
1343
| `GET /api/hosted/{ecosystem}/{name}` | List the archives pushed for a package |
1344
1344
| `DELETE /api/hosted/{ecosystem}/{name}/{version}` | Delete a pushed version |
1345
1345
1346
+
### Projects API
1347
+
1348
+
Registered when `projects.enabled` is set. Uploading and deleting require `Authorization: Bearer <projects.token>`. See [Project inventory](docs/configuration.md#project-inventory).
1349
+
1350
+
| Endpoint | Description |
1351
+
|----------|-------------|
1352
+
| `PUT /api/projects/{project}/sbom` | Replace a project's recorded package versions with the ones the SBOM in the body lists |
1353
+
| `GET /api/projects` | List projects, or with `?purl=pkg:npm/lodash@4.17.20` the projects using a package or version |
1354
+
| `GET /api/projects/{project}` | A project's recorded package versions |
1355
+
| `DELETE /api/projects/{project}` | Delete a project |
1356
+
1346
1357
### Cache API
1347
1358
1348
1359
Registered when `cache_delete` is set. It has no authentication of its own.
@@ -1546,6 +1557,7 @@ The proxy serves a web UI under `/ui`. No separate frontend build is needed -- t
1546
1557
- **Version detail** (`/ui/package/{ecosystem}/{name}/{version}`) -- per-version metadata, integrity hash, artifact cache status, and hit counts.
1547
1558
- **Source browser** (`/ui/package/{ecosystem}/{name}/{version}/browse`) -- browse files inside cached archives with syntax highlighting for text files and image previews.
1548
1559
- **Version diff** (`/ui/package/{ecosystem}/{name}/compare/{v1}...{v2}`) -- side-by-side diff of two cached versions showing added, removed, and changed files.
1560
+
- **Projects** (`/ui/projects`) -- with the [project inventory](docs/configuration.md#project-inventory) enabled, the package versions each repository's uploaded SBOM lists, marked when outdated. Package pages then show which projects use each version.
Copy file name to clipboardExpand all lines: docs/architecture.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -367,6 +367,10 @@ Selective package mirroring for pre-populating the proxy cache. Supports multipl
367
367
368
368
The package also provides a `MetadataCache` for storing raw upstream metadata blobs so the proxy can serve metadata responses offline. The `JobStore` manages async mirror jobs exposed via the `/api/mirror` endpoints.
369
369
370
+
### `internal/inventory`
371
+
372
+
Turns an SBOM uploaded to `/api/projects` into the package versions recorded for the project. Package URLs are mapped to the ecosystem names and unversioned PURLs the handlers record packages under, so a project's components join with `packages` by `purl`. Also compares versions for the outdated marker on the project pages.
Copy file name to clipboardExpand all lines: docs/configuration.md
+83Lines changed: 83 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1647,6 +1647,89 @@ Generic files never come from an upstream: anything not pushed answers 404. Whil
1647
1647
1648
1648
Pushed archives are stored like cached ones but marked as local. The cache size limit and storage retention never remove them; only a delete does. Scanning, when enabled, checks pushed archives like fetched ones.
1649
1649
1650
+
## Project inventory
1651
+
1652
+
The project inventory answers "which repository uses which version of a package?". Each repository uploads an SBOM of its default branch, and the proxy records the package versions it lists. The UI then shows:
1653
+
1654
+
- `/ui/projects`: every project, and per project its package versions, marked when the proxy knows a newer version.
1655
+
- On a package page: which projects use each version, including versions the proxy hasn't cached.
1656
+
1657
+
It is disabled by default. Uploads and deletes require a token:
1658
+
1659
+
```yaml
1660
+
projects:
1661
+
enabled: true
1662
+
token: "${PROXY_PROJECTS_TOKEN}"
1663
+
max_age: "90d"
1664
+
```
1665
+
1666
+
| Config | Environment | Description |
1667
+
|--------|-------------|-------------|
1668
+
| `projects.enabled` | `PROXY_PROJECTS_ENABLED` | Register the `/api/projects` endpoints and the UI pages (default `false`) |
1669
+
| `projects.token` | `PROXY_PROJECTS_TOKEN` | Bearer token required to upload and delete; required when enabled, supports `${VAR}` |
1670
+
| `projects.max_age` | `PROXY_PROJECTS_MAX_AGE` | Remove projects that haven't uploaded for this long, e.g. `90d` or `720h`; empty or `0` keeps them (default) |
1671
+
1672
+
Removing old projects runs hourly. With several instances on one database, one of them does it.
1673
+
1674
+
### Uploading an SBOM
1675
+
1676
+
`PUT /api/projects/{project}/sbom`takes a CycloneDX (JSON or XML) or SPDX (JSON or tag-value) document of up to 32 MiB and replaces everything recorded for the project with what it lists. The optional `ref` and `branch` query parameters are stored and shown with the project.
1677
+
1678
+
The project name is a path such as `group/sub/repo`: segments of letters, digits, `.`, `_` and `-`, at most 255 characters. It can be sent as path segments (`/api/projects/group/sub/repo/sbom`) or URL-encoded as one segment (`/api/projects/group%2Fsub%2Frepo/sbom`).
Entries are recorded by their package URL, with the package named the way the proxy caches it: NuGet IDs lowercased, PyPI names normalized as in PEP 503 (`zope.interface` becomes `zope-interface`), the distribution dropped from `deb`, `rpm` and `apk` package URLs. Entries without a package URL (files, the operating system), package types the proxy doesn't serve (such as `pkg:github`), entries without a version, and conda entries without a `channel` qualifier (the proxy records conda packages as `channel/name`) are skipped. So is the project's own package: the root of a lock file such as `package-lock.json`, and with syft, the Go module its binaries were built from. Duplicates are recorded once.
1694
+
1695
+
A GitLab CI job on the default branch could look like this:
1696
+
1697
+
```yaml
1698
+
sbom:
1699
+
image: alpine:3.20
1700
+
rules:
1701
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
1702
+
script:
1703
+
- apk add --no-cache curl
1704
+
- curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
Reading needs no token, like the rest of `/api`. With `?purl=`, each project carries the `version` it uses and no `component_count`. A delete names the project itself: `DELETE /api/projects/acme/shop/web/sbom`is answered with 400 unless a project `acme/shop/web/sbom` exists.
1724
+
1725
+
A project's page in the UI also has a delete button. It asks for the token and sends it to `DELETE /ui/api/projects/{project}`, which works like the API endpoint and also requires the token; the token is kept for the browser session. Like `/ui/api/cache`, it sits under `/ui`, so rules for the UI at an ingress cover it.
1726
+
1727
+
### What it shows
1728
+
1729
+
The inventory reflects what the projects' SBOMs list, not what was downloaded through the proxy: a project that installs from another registry still shows up, and the proxy may not have cached any of its packages. The outdated marker compares the version a project uses with the latest version the proxy has recorded for the package; a package the proxy has no record of has none. Versions are compared as the SBOM gives them: a version spelled differently from how the proxy caches it, such as Composer's `v6.4.1` for `6.4.1` or a Debian version with or without its epoch, shows as "not cached" on the package page.
1730
+
1731
+
Project names and the packages they use are shown on `/ui` and `/api/projects` without authentication. If that is sensitive, restrict both at your ingress or reverse proxy.
1732
+
1650
1733
## Mirror API
1651
1734
1652
1735
The `/api/mirror` endpoints are disabled by default. Enable them to allow starting mirror jobs via HTTP:
0 commit comments