Research-2143: Root licence files, package licence fields, and where LICENSE-MIT was published¶
Question¶
What do licence detectors read from the repository root before and after the layout of ADR-1699, which package manifests misstate the licences of the files they ship, and which published artifacts contained the root LICENSE-MIT that ADR-0686 added?
This digest records facts and where they were read. It is not legal advice.
Sources¶
- licensee 10.1.0, the detector GitHub's licence feature is built on (
gem install licenseein theruby:3.4image,licensee detect <dir>), run 2026-10-05 on copies of the root files. - GitHub REST API
GET /repos/VMAFx/vmafxandGET /repos/VMAFx/vmafx/license, read 2026-10-05. cargo package --list -p <crate> --allow-dirty --offline(cargo 1.98.1) andcargo deny --offline check(cargo-deny 0.19.8) in this worktree.- Artifact Hub
docs/helm_annotations.md(artifacthub.io/license), read 2026-10-05. - The registries: GHCR (package versions through the GitHub API; image manifests, configs and layers through
ghcr.io/v2, anonymous pull tokens),proxy.golang.org(module list and the zips' central directories by HTTP range requests), PyPI's JSON API and files, the crates.io API andcargo search vmafx, and the release assets of every release (gh release download). git log -S'LICENSE-MIT',git cat-file -e <tag>:LICENSE-MITandgit ls-remote --tags originin this repository.
Findings¶
Root files and detectors¶
| Root files | licensee per file | licensee project result |
|---|---|---|
master 8e60965f0: LICENSE (Netflix BSD-2-Clause-Patent text), LICENSE-MIT | LICENSE: NOASSERTION (closest: BSD-2-Clause-Patent, 76.64 %); LICENSE-MIT: MIT, exact match, attribution "Copyright (c) 2026 Lusoris" | NOASSERTION |
First revision of this branch: LICENSE (LICENSES/EUPL-1.2.txt), LICENSE-BSD-2-Clause-Patent | LICENSE: EUPL-1.2, Dice 99.20 % (threshold 98 %); LICENSE-BSD-2-Clause-Patent: NOASSERTION (76.64 %) | NOASSERTION |
ADR-1699: LICENSE (LICENSES/EUPL-1.2.txt), NOTICE (Netflix's text) | LICENSE: EUPL-1.2, 99.20 %; NOTICE is not a candidate licence file (matched files: LICENSE only) | EUPL-1.2 |
ADR-1699 with LICENSE-MIT planted back | LICENSE: EUPL-1.2; LICENSE-MIT: MIT, exact | NOASSERTION |
LICENSES/EUPL-1.2.txt alone as LICENSE | EUPL-1.2, 99.20 % | EUPL-1.2 |
On the whole tree (root files, LICENSES/ and Cargo.toml) the result depends on the licensee version, because 9.19.0 (PR licensee/licensee#926, January 2026) added LICENSES/ to the files it reads:
| Tree | licensee 9.18.0 | licensee 10.1.0 | GitHub (2026-10-05) |
|---|---|---|---|
master layout (LICENSE Netflix, LICENSE-MIT, LICENSES/, Cargo.toml) | NOASSERTION (matched LICENSE, LICENSE-MIT, Cargo.toml) | NOASSERTION | other |
this branch's working tree (LICENSE, NOTICE, LICENSES/, Cargo.toml) | EUPL-1.2 (matched LICENSE, Cargo.toml) | NOASSERTION (matched LICENSE, the 15 LICENSES/ texts, Cargo.toml) | eupl-1.2 (GET /repos/VMAFx/vmafx/license?ref=fix/root-licence-files-eupl; other while the branch had LICENSE-BSD-2-Clause-Patent) |
eza-community/eza (LICENSE.txt EUPL-1.2, LICENSES/ with CC-BY-4.0, EUPL-1.2 and MIT) | EUPL-1.2 | NOASSERTION | eupl-1.2 |
GitHub's answers for eza and for this branch match 9.18.0's behaviour. In both versions NOTICE is not a candidate licence file.
licensee reports a project licence only when the root's licence files agree on one licence; two licence files with different licences give NOASSERTION, which GitHub's API shows as other. Which names count is Licensee::ProjectFiles::LicenseFile::FILENAME_REGEXES of 10.1.0: LICENSE, LICENCE, UNLICENSE with any extension or -/_ suffix, COPYING*, <word>-LICENSE*, <word>-COPYING*, OFL*, COPYRIGHT* and PATENTS*, each with a score; NOTICE matches only the catch-all, score 0. On 2026-10-05 the API returned {"key": "other", "spdx_id": "NOASSERTION"} for the repository with LICENSE as its licence file. The repository description already read "fork code EUPL-1.2, Netflix code BSD-2-Clause-Patent"; the topics name no licence. The byte-identical copy of LICENSES/EUPL-1.2.txt is detected, so the repository keeps one EUPL-1.2 text rather than a second, differently wrapped one.
git log -S'LICENSE-MIT' finds three commits: 7f3504af4 (#1546, ADR-0686, 2026-05-28) added it, 384d97d03 (the layout move of the same day) touched it, and 02e7d834f (#1196) listed it in .github/ci-impact.json. No packaging configuration (pyproject.toml, Cargo.toml, a Dockerfile COPY, a bundle builder, a release workflow) ever named it.
Package manifests against their files¶
Read with tools/rc1-tester/image/licensing.py (SPDX header, else REUSE.toml), over the files each package ships:
| Manifest | Declared on master | The files carry |
|---|---|---|
Cargo.toml [workspace.package], inherited by vmafx-sys | BSD-2-Clause-Patent | EUPL-1.2: cargo package --list gives AGENTS.md, Cargo.toml, README.md, build.rs, examples/score.rs, src/lib.rs, src/safe.rs, tests/integration_test.rs and the workspace Cargo.lock; the inherited root readme is not shipped because the crate has its own README.md |
bindings/rust/vmafx/Cargo.toml | BSD-2-Clause-Patent | EUPL-1.2 (eight sources, AGENTS.md, README.md, Cargo.toml, Cargo.lock) |
core/src/feature/rust/tad/Cargo.toml (publish = false, inherits) | BSD-2-Clause-Patent | EUPL-1.2 AND BSD-2-Clause-Patent: build.rs, src/lib.rs and Cargo.lock are EUPL-1.2; the root README.md (copied in as the inherited readme) and the crate's Cargo.toml (no header; REUSE.toml core/**, 2016-2020 Netflix) are BSD-2-Clause-Patent |
deploy/helm/vmafx/Chart.yaml artifacthub.io/license | BSD-2-Clause-Patent | EUPL-1.2 (the chart's 34 own files) |
deploy/helm/vmafx/charts/prometheus-pushgateway-3.9.0.tgz | its own Chart.yaml: Apache-2.0 | REUSE.toml recorded EUPL-1.2 (the tree default); the upstream repository prometheus-community/helm-charts is Apache-2.0 and the archive holds no licence text |
tools/rc1-tester/pyproject.toml | EUPL-1.2 | EUPL-1.2 AND MIT: the sdist carries image/licenses/hacl-star.txt and image/licenses/nv-codec-headers.txt, MIT in REUSE.toml |
| The seven packages of ADR-1560 | as declared | already equal (held by python/test/setup_metadata_test.py) |
tools/markdownlint/package.json | no license | "private": true; npm refuses to publish it |
pyproject.toml (root) | no license | the repository's tool configuration, never built (ADR-1127) |
go.mod has no licence field. Artifact Hub requires artifacthub.io/license to be "a valid SPDX identifier". With EUPL-1.2 for vmafx-sys and vmafx, cargo-deny needs EUPL-1.2 in deny.toml's allow list; vmafx-tad is private and ignored, and BSD-2-Clause-Patent is then unmatched (cargo-deny warns about an unused allowance), so the list swaps one for the other: licenses ok.
Where LICENSE-MIT was published¶
| Artifact | Contained LICENSE-MIT? | How it was read |
|---|---|---|
proxy.golang.org, module github.com/VMAFx/vmafx | Yes for v1.0.0-rc.1 and v1.0.0-rc.2: github.com/VMAFx/vmafx@v1.0.0-rc.N/LICENSE-MIT, 1,064 bytes, "MIT License / Copyright (c) 2026 Lusoris", next to LICENSE (Netflix's text). The proxy's list holds 26 versions; the other 24 are Netflix tags from 2016 to 2023, before the file existed | @v/list; central directory of each zip (92 MB) by range requests, about 0.9 MB read |
GitHub source archives (/archive/refs/tags/<tag>.zip, .tar.gz) | Yes for every tag on origin whose tree has it: v1.0.0-rc.1, v1.0.0-rc.2, tester-20261004-4d3792b3, tester-20261004-860050c3, tester-windows-20261004-2889f963, archive/eupl-relicense-v1; and the master tree from 2026-05-28 to this change. GitHub builds these with git archive; .gitattributes has no export-ignore | git cat-file -e <tag>:LICENSE-MIT, git ls-remote --tags origin |
GHCR ghcr.io/vmafx/vmafx, vmafx-server, vmafx-operator (39 tags: rc.1 and rc.2 CPU, server, CUDA 13, oneAPI 2025, their -source images, three tester builds with their GPU kits and sources) | No | Every tag's image config history: the final stages copy build outputs (/dist/lib/, /dist/model/, /dist/bin/, /venv, /opt/vmafx/..., named tester files), never the repository root. Every layer listed (amd64) for the CPU and CUDA 13 images and the vmafx-server and vmafx-operator images of both releases (140 layers); the CPU tester v1.0.0-rc.2-411-g854bf047e-tester except its venv and fixture layers; vmafx-operator:v1.0.0-rc.1-source holds Debian source packages and dependency module zips, not this module |
Release assets of v1.0.0-rc.1, v1.0.0-rc.2 (libvmaf.so*, vmaf, models.tar.gz, licenses-*.tar.gz, THIRD_PARTY_NOTICES-*) | No | The 14 licence tarballs extracted: no file equal to LICENSE-MIT, no "Copyright (c) 2026 Lusoris"; their 18 texts/MIT.txt are LICENSES/MIT.txt and their 20 texts/BSD-2-Clause-Patent.txt are the root LICENSE of the time, byte for byte |
Tester bundles (macOS tester-20261004-*, Windows tester-windows-20261004-2889f963) | No | Their published bundle-files*.txt listings (5 files) |
vmaf-mcp 1.0.0rc1, 1.0.0rc2 on PyPI (sdist and wheel; yanked per ADR-1578) | No | The four files downloaded: none holds a licence file of any name |
| crates.io | Nothing published: vmafx, vmafx-sys, vmafx-tad do not exist; cargo search vmafx returns nothing | crates.io API |
| Helm chart | Not published (helm-chart.yml lints and tests only) | workflow |
The root LICENSE of the time (Netflix's text) reached the artifacts as BSD-2-Clause-Patent.txt through licensing.json spdx_texts; after ADR-1699 the same bytes come from NOTICE, so a rebuilt artifact's notices do not change.
What the Go retraction reaches¶
The root go.mod now carries retract [v1.0.0-rc.1, v1.0.0-rc.2]. The Go modules reference says the go command loads retractions from the go.mod of the version @latest resolves to, and that the retracting version must be higher than every other release or pre-release. For this module path the proxy lists 26 versions (go 1.27.1, go list -m -versions github.com/VMAFx/vmafx, 2026-10-05): v1.0.0-rc.1, v1.0.0-rc.2, Netflix's tags v1.0.2 to v1.5.3, and v2.0.0+incompatible to v3.0.0+incompatible. go list -m github.com/VMAFx/vmafx@latest resolves to v3.0.0+incompatible: the highest compatible release, v1.5.3, has no go.mod, so the +incompatible versions stay eligible.
A local file-based module proxy reproduced that resolution and showed the consequence:
| Versions served | @latest | go list -m -versions |
|---|---|---|
rc.1, rc.2, v1.5.3, v3.0.0+incompatible (today) | v3.0.0+incompatible | rc.1, rc.2, v1.5.3, v3.0.0+incompatible |
+ v1.0.0-rc.3 whose go.mod has the retraction | v3.0.0+incompatible | rc.1, rc.2 still listed; -u -json of rc.2 shows no Retracted |
+ v1.0.0 with the retraction | v3.0.0+incompatible | unchanged |
+ v1.6.0 with the retraction | v1.6.0 | rc.1, rc.2 gone (listed only with -retracted) |
| only rc.1, rc.2, rc.3 (control) | rc.3 | rc.1, rc.2 gone; -u -json of rc.2 shows Retracted with the rationale |
So with Netflix's tags in the module's version list, a retraction published in a v1.0.0-* or v1.0.0 version is not read by the go command.
.toml files and the provenance tool¶
relicense_fork_files.py skipped every file it had no comment style for, which included .toml. With .toml classified (praetor's .codex/agents/*.toml projections excluded), 15 files changed: 13 fork files gained the EUPL-1.2 header (REUSE.toml, .gitleaks.toml, .helix/config.toml, .helix/languages.toml, osv-scanner.toml, the root pyproject.toml, the ai/, dev-llm/, mcp-server/vmaf-mcp/ and ensemble-kit pyproject.toml, and the three crate manifests), and tools/vmaf-tune/pyproject.toml and tools/vmaf-roi-score/pyproject.toml (both "Copyright 2026 Lusoris") moved from BSD-2-Clause-Patent to EUPL-1.2. Those two had been kept by veto 3 because upstream has a python/pyproject.toml; the name now carries no provenance signal. bindings/rust/vmafx-sys/Cargo.toml was flagged by the derivation detector for "bindings.rs is produced by bindgen from libvmaf.h"; it is a manifest and was recorded under [not_ports]. python/pyproject.toml (an upstream path) keeps its terms through REUSE.toml.
Alternatives explored¶
- Canonical choosealicense text instead of
LICENSES/EUPL-1.2.txt. Not needed: the SPDX text already passes licensee's threshold (99.20 %), and a second EUPL-1.2 text would differ from the one REUSE and every package ship. - Counting the Helm subchart in the chart's own licence. Rejected in ADR-1699: Artifact Hub takes one identifier, and the subchart declares its own; the gate instead holds the subchart's
REUSE.tomlrecord to its declaration, which found theEUPL-1.2record above. - Running
cargo package --listin the gate. It needs a Rust toolchain and a populated registry cache; the gate models cargo's selection instead and refusesinclude,excludeandlicense-file. For all three crates the model's file set iscargo package --list(its generatedCargo.tomland.cargo_vcs_info.jsonmapped to their sources) plus the workspace manifest whose inherited fields cargo writes into the generatedCargo.toml.
Open questions¶
- GitHub's licence API reports
eupl-1.2for this branch's ref; the sidebar shows the default branch. A GitHub detector that readsLICENSES/(licensee 9.19.0 or later) would reportNOASSERTIONfor this tree. - The
go.modretraction reaches Go clients only through a version that@latestresolves to; see "What the Go retraction reaches". - The crates ship no licence text; none is published.