Share a license report
The request comes from legal or from a customer's procurement questionnaire and it is deceptively simple: "What open-source licenses are in the product?"
Every ecosystem has an answer:
# Node
npx license-checker --production --out licenses.html
# Python
pip-licenses --format=html --with-license-file --with-urls > licenses.html
# Rust
cargo about generate about.hbs > licenses.html
# Ruby / Java / multi-language
license_finder report --format html > licenses.html
# Go
go-licenses report ./... --template licenses.tpl > licenses.html
And then the answer gets pasted into a spreadsheet, emailed, and is wrong within a sprint. Which is why the request comes back every quarter.
Flag, don't just list
A report that lists 1,400 dependencies alphabetically technically answers the question and practically answers nothing. The reader is looking for four things:
- Copyleft — GPL, AGPL, LGPL, SSPL. These are the ones with obligations that can reach your own source.
- Unknown / no license detected. Usually a packaging bug, occasionally a genuine problem, always worth resolving before shipping.
- Non-standard or custom licenses, which need a human to read them.
- Changes since last time, which is the only part that needs attention on a recurring basis.
license-checker supports this directly:
npx license-checker --production \
--onlyAllow 'MIT;ISC;Apache-2.0;BSD-2-Clause;BSD-3-Clause;CC0-1.0;Unlicense' \
--out licenses.html
which exits non-zero on anything outside the allowlist — so the report has a gate behind it rather than being a document that is generated and never read.
Publish it
Drag licenses.html into the app, or POST it:
curl -fsS -X POST "https://commareports.com/api/v1/reports" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html licenses.html \
--arg t "Licenses — myapp 1.4.2" '{title: $t, html: $html}')"
The part that saves the recurring request
Generate it in the release pipeline and PATCH the same report id:
npx license-checker --production --out licenses.html
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html licenses.html \
--arg t "Licenses — myapp $VERSION" '{title: $t, html: $html}')"
The URL you gave legal in Q1 still resolves to the current dependency tree in Q4. The revision list answers "what were we shipping when we signed that contract?", which is the question that actually matters in a dispute and which a spreadsheet cannot answer at all.
Attribution pages are the same artifact
Most permissive licenses require the license text and copyright notice to travel with your distribution. That obligation and this report are the same document — generate it with the full text included:
pip-licenses --format=html --with-license-file --with-notice-file > licenses.html
and the published URL can be the attribution page you link from your app's about screen. Public access is appropriate here; it is the case where the report is meant to be world-readable.
For an internal audit that names unreleased components, use team or domain-gated access instead. See the sharing model.
What review adds
- Anchored threads on a specific dependency — "AGPL, but we only use it as a separate process over HTTP; legal signed off, see CONTRACT-118".
- Revisions, which are the dependency-tree history.
- Domain-gated access, so a customer's counsel can read it without an account.
See commenting on HTML.
Limits
- Entry HTML: 5 MB. Including full license text for a large tree is what pushes past it — publish the summary as the report body and attach the full-text bundle as an asset.
- 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.