BOMpage
BOMpage tracking its own dependencies
Track what your software is made of, across every project, over time.
Your CI already generates an SBOM — the list of every library your application depends on. But each one is a snapshot in a single pipeline: nobody can see them side by side, and nobody keeps yesterday's.
BOMpage collects those SBOMs into one Git repository and turns them into a
dashboard you can publish on GitLab or GitHub Pages. It needs no server, no
database and no account: the result is a folder of static files.
You can then answer, in a few clicks:
- Which version of
openssldoes each of our services ship? - What changed in this application's dependencies since last month?
- Are we still pulling a strong-copyleft licence anywhere?
- Which project has stopped reporting? (its pipeline is probably broken)
How it fits together
Three pieces, set up in this order:
your projects central repo published site
┌──────────────┐ ┌───────────────────────┐ ┌─────────────────┐
│ app-api CI │──push─▶│ reports/app-api/ │ │ │
│ web-front CI │──push─▶│ reports/web-front/ │─build─▶│ a static page │
│ batch-etl CI │──push─▶│ reports/batch-etl/ │ │ (Pages) │
└──────────────┘ └───────────────────────┘ └─────────────────┘
bompage push one folder per project bompage build
(from each pipeline) one file per day (from that repo's CI)
- Each project pushes its SBOM into the central repository at the end of its pipeline. A snapshot is only committed when the content actually changed, so the history stays readable.
- The central repository is just folders and JSON files. One directory per project, discovered automatically — nothing to declare, nothing to configure when a new project appears.
- Its own pipeline rebuilds the dashboard and publishes it.
Both SPDX and CycloneDX are accepted, so whatever your scanner emits (Trivy, Syft, the CycloneDX Maven plugin…) will work.
Install
Python 3.12 or later:
python3 -m pip install bompage
bompage --help
The published package already contains the dashboard, so you need nothing from
the JavaScript world to use BOMpage.
Try it in two minutes
If you already have an SBOM file, put it in a folder named after your project and build:
mkdir -p reports/my-service
cp sbom.json reports/my-service/sbom-20260914.json # sbom-YYYYMMDD.json
bompage build --reports reports --output public
python3 -m http.server -d public # then open http://localhost:8000
A local file server is required: the page loads its data with fetch, which
browsers refuse over file://.
The file name carries the date — sbom-YYYYMMDD.json, optionally
sbom-YYYYMMDD-1.4.2.json to record a version. That is the whole convention;
files that do not follow it are skipped with a warning rather than breaking the
build.
The four commands
Every command accepts --help, and the
full option reference is generated
from the code, so it is always current.
bompage build — make the site
Runs in the central repository's pipeline. No network access needed.
bompage build --reports reports --output public \
--stale-after 2 \
--recent-days 30 \
--metadata "team=platform"
--stale-after 2— flag a project that has not reported for 2+ days.--recent-days 30— window covered by the "recent changes" panel.--metadata "key=value"— free rows shown in the dashboard header; repeat it, or use--metadata-component "app-api:owner=backend"for one project.
Careful:
--outputis emptied on every build.BOMpagerefuses to write into a folder it did not create itself; add--forceonly if you are sure.
bompage push — send one SBOM
Runs in each project's pipeline. Needs the git command and a write token.
export BOMPAGE_TOKEN=…
bompage push \
--component app-api \
--sbom build/sbom.cdx.json \
--repo https://gitlab.com/acme/bompage.git
--component app-api— becomesreports/app-api/in the central repo.BOMPAGE_TOKEN— pass the token through the environment, not with--token: anything on a command line is readable by other processes on the same runner, andBOMpagewarns you when you do.
Optionally, set BOMPAGE_SIGN_KEY to a private SSH key and push also writes
a signed attestation sidecar (.att / .att.sig) beside the SBOM, which
build then verifies (see bompage verify below). Leave it unset and nothing
changes — signing is entirely opt-in.
On GitLab you usually do not call it by hand — include the ready-made
bompage-push job instead (signing, if BOMPAGE_SIGN_KEY is set in the
project, is picked up automatically — see deploy/README.md):
stages:
- sbom-release # bompage_push_stage default; declare it or override the input
include:
- component: gitlab.com/op_so/components/pages/bompage@v1.0.0
inputs:
bompage_push_repo: https://gitlab.com/acme/bompage.git
bompage_push_component: app-api
bompage_push_sbom: build/sbom.cdx.json
bompage prune — keep the repository small
Deletes snapshots older than --keep-days in a single ordinary commit —
no history rewriting. The most recent snapshot of each project is always kept,
so a project that went quiet stays visible on the dashboard.
export BOMPAGE_TOKEN=…
bompage prune \
--repo https://gitlab.com/acme/bompage.git \
--keep-days 90 \
--dry-run
--dry-run lists what would go without changing anything; drop it to actually
commit. Best run as a scheduled job in the central repository.
bompage verify — check a download
Run by anyone who downloaded an SBOM, not by a pipeline. Needs no repository
access, only the file, its .att / .att.sig sidecars, and the trust root.
Today those sidecars live only in the central repo's reports/<component>/
tree — the published site does not yet serve them (a planned dashboard
change, see the spec):
bompage verify reports/app-api/sbom-20260914.json --allowed-signers ./allowed_signers
It is a convenience only: the same check can always be done with stock
ssh-keygen, jq and sha256sum alone, which is documented in
docs/verify.md
for anyone who does not want to trust bompage itself for that. This only
matters for a deployment that signs its pushes (see BOMPAGE_SIGN_KEY above)
— an unsigned one has nothing here to verify.
Set up a central repository
The deploy/ folder
is a ready-to-copy skeleton: the layout, the Pages job and the scheduled prune
job. See
deploy/README.md
for the steps.
What the dashboard shows
A live one is published at
op_so.gitlab.io/pages/sbom-bompage:
BOMpage runs the whole flow on itself, so that page is built by the released
version from an SBOM its own pipeline pushed.
Per project: the current inventory (name, version, ecosystem, licence), with strong-copyleft dependencies flagged; the version history of any package; what was added and removed recently; a comparison between any two snapshots; and a warning when a project has stopped reporting.
Across projects: a search for any package name, and the list of dependencies sitting at diverging versions in several projects of the same ecosystem.
Not built yet: the versioned api/v1/ REST surface, for tools that would read
this data rather than look at it.
Contributing
Bug reports and merge requests are welcome. The development setup, the test
suite and the quality gates are documented in
CONTRIBUTING.md.
Authors
- FX Soubirou - Initial work - GitLab repositories
License
This program is free software: you can redistribute it and/or modify it under the terms of the MIT License (MIT). See the LICENSE for details.
Release files for bompage 1.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bompage-1.3.1.tar.gz | 1.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bompage-1.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / bompage-1.3.1.tar.gz
| Download URL | bompage-1.3.1.tar.gz |
|---|---|
| Size | 1.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2ac2d27e3c948ae530337154ae7f8f1cec6d057004c2113e0c9631ea64fa3067
|
|
BLAKE2b-256 checksum How to use checksums |
de021d11f4bdc2c500349acf47c5690c6af89c6cf965f59c53b080c2bd8eba0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / bompage-1.3.1-py3-none-any.whl
| Download URL | bompage-1.3.1-py3-none-any.whl |
|---|---|
| Size | 609.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4c204d90adf8e7adafd9c8323a45195d15f0b52cc52436f736640792af70e92d
|
|
BLAKE2b-256 checksum How to use checksums |
9d4e9ad5c7abfa16dc508b704f64a26cf0ce60f61857c586e1aea1fdaa5a2323
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|