1. (A)GPL prohibition in module manifests (HARD BLOCK)
SHALL NOT add any package with a GPL-2.0, GPL-3.0, AGPL-3.0, GPL-2.0-or-later,
GPL-3.0-or-later, or AGPL-3.0-or-later license to any module-package.yaml
pip_dependencies list. There is no allowlist path that permits GPL/AGPL in
distributed module manifests. The scripts/license_allowlist.yaml module-manifest
scope exists exclusively for LGPL packages invoked as a subprocess (see Section 3,
“CONDITIONAL”); it does not unblock GPL or AGPL licenses.
Rationale: pip_dependencies in module manifests are installed on end-user
systems via specfact module install. Force-installing GPL software constitutes
a license violation under Apache-2.0 and blocks enterprise/commercial adoption.
Action on violation: Remove the GPL package, run hatch run license-check,
and propose a MIT/Apache-2.0/BSD alternative.
2. (A)GPL in dev env extras (MUST DOCUMENT + PHASE 2 PLAN)
GPL packages in dev-only extras (e.g. pylint) require:
- A
dev-only-scoped entry inscripts/license_allowlist.yamlwith areason. - An explicit Phase 2 removal plan in the
reasonfield. - A comment in
pyproject.tomlat the dependency line.
They are never acceptable in module manifests (see Section 1).
3. Approved licenses for module manifest pip_dependencies
| License | Approved | Notes |
|---|---|---|
| MIT | YES | Unrestricted |
| Apache-2.0 | YES | Unrestricted |
| BSD-2-Clause / BSD-3-Clause | YES | Unrestricted |
| PSF | YES | Unrestricted |
| LGPL-2.1 / LGPL-3.0 | CONDITIONAL | Allowed when invoked as subprocess (not statically linked); requires module-manifest allowlist entry with subprocess justification |
| GPL-2.0 / GPL-3.0 / AGPL | BLOCKED | Never in module manifests; dev-only with allowlist + Phase 2 plan |
4. Required gates before any manifest or dependency change is merged
Run these in order:
hatch run license-check # scripts/check_license_compliance.py — exit 0 required
hatch run security-audit # pip-audit --desc --strict — review CVEs ≥ CVSS 7.0
hatch run bandit-scan # bandit -r src/ -ll — review and document findings
5. New pip_dependencies in module manifests — checklist
Before adding a new pip_dependencies entry to any module-package.yaml:
- Check the package license on PyPI (
pip show <pkg>or PyPI JSON API). - Verify the license is in the Approved column above (Section 3).
- If LGPL: document subprocess invocation in
license_allowlist.yaml. - Run
hatch run license-check— must exit 0. - Re-sign the module manifest (
hatch run sign-modules). - Run
hatch run verify-modules-signature(strict bundle frommodule-verify-policy.sh) — must pass.
6. Phase 2 tracking
| Package | Current status | Phase 2 action |
|---|---|---|
pylint |
dev-only (GPL-2.0-or-later) | Replace with ruff --select ALL once SLF001/W0212 and R0801 gaps are resolved |
yamllint |
dev-only (GPL-3.0-or-later) | Replace with a non-GPL YAML lint path once CI / pre-commit parity is preserved |
gitpython |
runtime (CVE history) | Replace with dulwich adapter (3-file rewrite) |
7. Static license map
check_license_compliance.py uses a static license map for known module
pip_dependencies to avoid network calls. The mapping lives in
scripts/module_pip_dependencies_licenses.yaml (licenses: key, lowercase
package name → SPDX expression).
If you add a new manifest dependency that is not in the map, the gate
will fail (not warn) and flag it for review. Update
scripts/module_pip_dependencies_licenses.yaml after license review before
the manifest can be merged.