BOMpage
A generic, server-less system to centralise the SBOM history of N components in a dedicated Git repository and publish a static dashboard (GitLab / GitHub Pages) plus an optional static REST API.
Status: SPDX and CycloneDX. Covered: last known state (§7.1), per-package timeline & recent changes (§7.2), data freshness (§7.5), transverse search (§7.3) and cross-component version drift (§7.4), snapshot retention (§5.4), plus the reusable CI push component (§9). Not yet: the arbitrary two-date diff (§7.6) and the versioned
api/v1/surface (§8).
What it does
From a central bompage repository laid out as reports/<component>/sbom-YYYYMMDD[-version].json,
the bompage CLI:
- discovers components dynamically (one directory = one component);
- parses every dated snapshot of each component (SPDX and CycloneDX, including CycloneDX nested sub-components) into a common inventory model;
- derives, per component: current inventory (name, version, ecosystem from the
purl, licence, strong-copyleft flag), the per-package version timeline, the added/removed deltas, a recent-changes feed and a freshness verdict; - builds a cross-component inverted index (package → components) and the list of active version drifts;
- writes a JSON data tree under
public/data/(index.json,sbom-data.json,components/<name>.json,packages-index.json,drifts.json) plus a copy of each raw manifest; - ships a constant dashboard page (no project data baked in) that fetches that tree at runtime: per-component tabs with a stale badge, ecosystem filter, client-side search, a recent-changes panel, a click-through version timeline, a global transverse search and an "active drifts" view.
Ingestion is the push-report reusable GitLab
CI/CD component (spec §9): it pip installs a pinned bompage release and
runs bompage push, which clones the central repo with a scoped token, drops
the SBOM under reports/<component>/ and commits only if the content changed
(anti-noise filter, §10). The logic lives in
src/bompage/push.py (it shells out to git; no VCS
library).
Retention is bompage prune (spec §5.4,
src/bompage/prune.py): it removes snapshots older than
--keep-days (default 90) from reports/ in one ordinary commit — no
history rewrite — always keeping each component's most recent snapshot, and
runs as a scheduled job in the central repo's own pipeline. Shared git plumbing
sits in src/bompage/_git.py.
The page is built with Material Web (Material Design 3) + lit, bundled by Vite, with Roboto and a subset of Material Symbols self-hosted (no CDN, no runtime network call).
Usage
Usage: bompage [OPTIONS] COMMAND [ARGS]...
Track SBOM history of N components and build a static dashboard.
Options:
--version Show the version and exit.
--help Show this message and exit.
Commands:
build Build the static dashboard and its data/ tree from a reports/ tree.
push Publish one SBOM into the central repository (clone, commit iff changed, push).
prune Remove SBOM snapshots older than --keep-days from the central repository.
Every command also accepts --help. Full generated reference:
CLI documentation.
bompage build
Build the static dashboard and its data/ tree from a reports/ tree. Runs
locally or in the central repo's Pages job; needs no network access.
| Option | Default | Description |
|---|---|---|
--reports PATH |
reports |
Directory holding one sub-directory per component (must exist). |
--output PATH |
public |
Directory the static site is written to. |
--web-dir PATH |
(packaged assets) | Pre-built dashboard (index.html + assets/) to use instead of the one bundled in the wheel. |
--stale-after N |
2 |
A component with no new report for more than N days is flagged stale (min 0). |
--recent-days N |
30 |
Sliding window, in days, for the dashboard's recent-changes feed (min 1). |
--metadata "key=value" |
— | Global metadata row shown in the dashboard header. Repeatable. |
--metadata-component "component:key=value" |
— | Per-component metadata row. Repeatable. |
bompage build --reports reports --output public \
--stale-after 2 --recent-days 30 \
--metadata "team=platform" --metadata-component "app-api:owner=backend"
bompage push
Publish one SBOM into the central repository: clone with a scoped token, drop the
file under reports/<component>/, and commit only if the content changed
(anti-noise filter, spec §10), then push. Requires the git binary on PATH.
| Option | Default | Description |
|---|---|---|
--component TEXT |
required | Logical component name (one directory under reports/). |
--sbom PATH |
required | Path to the SBOM file produced by the calling job (must exist). |
--repo TEXT |
required | HTTPS clone URL of the central bompage repository. |
--token TEXT |
required — env BOMPAGE_TOKEN |
Scoped write token for the central repository. |
--branch TEXT |
main |
Target branch in the central repository. |
--sbom-version TEXT |
— | Version suffix appended to the SBOM file name. |
--sbom-format TEXT |
— | Informative (spdx | cyclonedx); recorded in the commit message. |
--git-user TEXT |
bompage-ci |
Commit author name. |
--git-email TEXT |
bompage-ci@localhost |
Commit author email. |
export BOMPAGE_TOKEN=… # scoped write token
bompage push \
--component app-api \
--sbom build/sbom.cdx.json \
--sbom-format cyclonedx \
--repo https://gitlab.com/acme/bompage.git
bompage prune
Remove SBOM snapshots older than --keep-days from reports/ in a single
ordinary commit — no history rewrite — always keeping each component's most
recent snapshot (spec §5.4). Same git-on-PATH and token requirements as
push; runs as a scheduled job in the central repo's own pipeline.
| Option | Default | Description |
|---|---|---|
--repo TEXT |
required | HTTPS clone URL of the central bompage repository. |
--token TEXT |
required — env BOMPAGE_TOKEN |
Scoped write token for the central repository. |
--branch TEXT |
main |
Target branch in the central repository. |
--keep-days N |
90 |
Snapshots older than N days are removed; the most recent snapshot of each component is always kept (min 1). |
--dry-run |
(off) | List what would be removed without committing or pushing. |
--git-user TEXT |
bompage-ci |
Commit author name. |
--git-email TEXT |
bompage-ci@localhost |
Commit author email. |
export BOMPAGE_TOKEN=…
bompage prune --repo https://gitlab.com/acme/bompage.git --keep-days 90 --dry-run
Preview locally
Preview with any static file server (the page uses fetch, so it will not run
from file://):
bompage build --reports reports --output public
python3 -m http.server -d public
Installation
With Python environment
- Minimal Python version: 3.14
python3 -m pip install bompage
bompage --help
The published wheel ships the pre-built dashboard, so no JS toolchain is needed
to use bompage.
From source
The dashboard assets (src/bompage/web/) are a Vite build output and are not
committed. JS dependencies are managed with pnpm. Build them
once before running the tests or bompage build:
brew install pnpm # or: corepack enable && corepack prepare pnpm@11 --activate
fnm use # Node 24, per .node-version
pnpm install --frozen-lockfile
pnpm run build:web # -> src/bompage/web/ (or: task 00:010-build-web)
The dashboard sources are checked with Biome + tsc (pnpm run lint:web,
pnpm run typecheck:web, pnpm run format:web; task 00:015-check-web).
task 00:190-check runs the whole static battery, task 00:290-check-all adds
the tests. Tests that need the built page skip cleanly until it is built; the
browser smoke test is uv run pytest -m e2e (needs uv run playwright install chromium).
Tests
task 00:010-build-web
uv run python scripts/gen-demo-data.py
uv run python -m http.server -d public 8000
Deployment
The deploy/ directory is a scaffold for bootstrapping a central bompage
repository — the pages job plus a scheduled bompage prune job (see
deploy/README.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.0.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.0.1.tar.gz | 382.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bompage-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 418.0 kB
Release files / bompage-1.0.1.tar.gz
| Download URL | bompage-1.0.1.tar.gz |
|---|---|
| Size | 382.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0eeb081a22966681af6a5593bbc7eba7fd61b564b97fdc552bde8ec6f884c086
|
|
BLAKE2b-256 checksum How to use checksums |
6da568989d7fbff3892e47a2fa6c9f227286cf2e150dc6fadc64f9407e00e762
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","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.0.1-py3-none-any.whl
| Download URL | bompage-1.0.1-py3-none-any.whl |
|---|---|
| Size | 35.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
169588ec27b19132564a48177435e8e51de55ac7dacb9e43b79de593aa9338f7
|
|
BLAKE2b-256 checksum How to use checksums |
f4f3871917d37106c8e3dce9ba91ff6a45a2e84c6962bc76b664252e2e840d8a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","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}
|