diff --git a/docs/changelog/+python-sbom.packaging.rst b/docs/changelog/+python-sbom.packaging.rst
new file mode 100644
index 0000000..8639e06
--- /dev/null
+++ b/docs/changelog/+python-sbom.packaging.rst
@@ -0,0 +1,2 @@
+Every wheel now carries a `PEP 770 `_ CycloneDX SBOM in its
+``.dist-info/sboms`` directory, written at build time by `python-sbom `_.
diff --git a/hatch_build.py b/hatch_build.py
new file mode 100644
index 0000000..32b4b1d
--- /dev/null
+++ b/hatch_build.py
@@ -0,0 +1,14 @@
+"""Ship a PEP 770 SBOM in wheels built on Python 3.11 or later."""
+
+from __future__ import annotations
+
+import sys
+
+if sys.version_info >= (3, 11):
+ from python_sbom.hatch import SbomBuildHook
+else: # python-sbom needs Python 3.11, so builds on older interpreters carry no SBOM
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface as SbomBuildHook
+
+
+def get_build_hook() -> type[SbomBuildHook]:
+ return SbomBuildHook
diff --git a/pylock.toml b/pylock.toml
index b15d24b..07c8522 100644
--- a/pylock.toml
+++ b/pylock.toml
@@ -798,6 +798,31 @@ size = 7552
[packages.wheels.hashes]
sha256 = "9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7"
+[[packages]]
+name = "installer"
+version = "1.1.0"
+marker = "\"build\" in dependency_groups"
+requires-python = ">=3.10"
+index = "https://pypi.org/simple/"
+
+[packages.sdist]
+name = "installer-1.1.0.tar.gz"
+upload-time = 2026-10-12 19:35:52.851170+00:00
+url = "https://files.pythonhosted.org/packages/56/d3/2fab33d15004d41ae8e7b5d3e310510f7f34a7e1dd032a3e60d8b38d8fba/installer-1.1.0.tar.gz"
+size = 481257
+
+[packages.sdist.hashes]
+sha256 = "5adc0c01ce62d809989e246987cd68944fe5df8af562d0358f65b2ab15cb5de7"
+
+[[packages.wheels]]
+name = "installer-1.1.0-py3-none-any.whl"
+upload-time = 2026-10-12 19:35:51.096942+00:00
+url = "https://files.pythonhosted.org/packages/d7/7a/63547a792a12148882205d79e07644091d5b7690f40c2bb9d41452a8bdc2/installer-1.1.0-py3-none-any.whl"
+size = 464466
+
+[packages.wheels.hashes]
+sha256 = "f55f5728ff61d3dd54dbc5a397cf3f7307cb64b180c20b72afbebf01c9e2c8a0"
+
[[packages]]
name = "jaraco-classes"
version = "3.4.0"
@@ -1179,6 +1204,31 @@ size = 832161
[packages.wheels.hashes]
sha256 = "70f5ac8626e899a4bab0ef74ca2f5bd602f49c7b739e6e5026b4afc6d63dac42"
+[[packages]]
+name = "packageurl-python"
+version = "0.17.6"
+marker = "\"build\" in dependency_groups"
+requires-python = ">=3.8"
+index = "https://pypi.org/simple/"
+
+[packages.sdist]
+name = "packageurl_python-0.17.6.tar.gz"
+upload-time = 2025-11-24 15:20:17.998933+00:00
+url = "https://files.pythonhosted.org/packages/f5/d6/3b5a4e3cfaef7a53869a26ceb034d1ff5e5c27c814ce77260a96d50ab7bb/packageurl_python-0.17.6.tar.gz"
+size = 50618
+
+[packages.sdist.hashes]
+sha256 = "1252ce3a102372ca6f86eb968e16f9014c4ba511c5c37d95a7f023e2ca6e5c25"
+
+[[packages.wheels]]
+name = "packageurl_python-0.17.6-py3-none-any.whl"
+upload-time = 2025-11-24 15:20:16.962118+00:00
+url = "https://files.pythonhosted.org/packages/b1/2f/c7277b7615a93f51b5fbc1eacfc1b75e8103370e786fd8ce2abf6e5c04ab/packageurl_python-0.17.6-py3-none-any.whl"
+size = 36776
+
+[packages.wheels.hashes]
+sha256 = "31a85c2717bc41dd818f3c62908685ff9eebcb68588213745b14a6ee9e7df7c9"
+
[[packages]]
name = "packaging"
version = "26.3"
@@ -1447,6 +1497,38 @@ size = 11248
[packages.wheels.hashes]
sha256 = "1b82aa4b37137248bd687055e109723debeb07b489de1565297dc9589c5f2d6b"
+[[packages]]
+name = "python-sbom"
+version = "0.1.2"
+marker = "\"build\" in dependency_groups"
+requires-python = ">=3.11"
+dependencies = [
+ { name = "hatchling" },
+ { name = "installer" },
+ { name = "packageurl-python" },
+ { name = "packaging" },
+ { name = "zipremove" },
+]
+index = "https://pypi.org/simple/"
+
+[packages.sdist]
+name = "python_sbom-0.1.2.tar.gz"
+upload-time = 2026-11-08 17:49:13.751304+00:00
+url = "https://files.pythonhosted.org/packages/65/a7/abd61bb6e6cf681fc9bf1ff02f2357caf0ae958e9613e1a0f2fe00672726/python_sbom-0.1.2.tar.gz"
+size = 245685
+
+[packages.sdist.hashes]
+sha256 = "5b611b565d6946926c8a1f6ec635beeea49887459f0a542f5350b77baafb2281"
+
+[[packages.wheels]]
+name = "python_sbom-0.1.2-py3-none-any.whl"
+upload-time = 2026-11-08 17:49:12.072616+00:00
+url = "https://files.pythonhosted.org/packages/5f/19/8c21420f27fcb14995e9b1c6cb451edaf341070d18996c42a96d77b80e64/python_sbom-0.1.2-py3-none-any.whl"
+size = 196051
+
+[packages.wheels.hashes]
+sha256 = "d831293bc389cbb9c444b30141692866d3c54f64e3c50a74fe6d74bd8517474a"
+
[[packages]]
name = "pyyaml"
version = "6.0.3"
@@ -2519,6 +2601,31 @@ size = 6195
[packages.wheels.hashes]
sha256 = "3fa599046443d4ca830d06e3d180cd0a675d5871af0a68daa5623318bb4d17e3"
+[[packages]]
+name = "zipremove"
+version = "0.10.0"
+marker = "\"build\" in dependency_groups"
+requires-python = "~=3.7"
+index = "https://pypi.org/simple/"
+
+[packages.sdist]
+name = "zipremove-0.10.0.tar.gz"
+upload-time = 2026-07-17 15:50:45.516180+00:00
+url = "https://files.pythonhosted.org/packages/92/30/47d65b9213f52df8a51f603cb0a2e5ef02c22172a8a17805e46c3beabb0d/zipremove-0.10.0.tar.gz"
+size = 25448
+
+[packages.sdist.hashes]
+sha256 = "3c5957c115447b54299d3a23128764b59c4558fc98c4b878ff2dbc5db8a62021"
+
+[[packages.wheels]]
+name = "zipremove-0.10.0-py3-none-any.whl"
+upload-time = 2026-07-17 15:50:44.653422+00:00
+url = "https://files.pythonhosted.org/packages/0a/e4/2225c1890ce6e239ca23efabb80a3481229a14a2deaa489f5e89723a5aa6/zipremove-0.10.0-py3-none-any.whl"
+size = 11280
+
+[packages.wheels.hashes]
+sha256 = "04e15df7083642d5b791e51f597cffef8a92d770ecf5160c529b5a7b53b40100"
+
[tool.nab]
nab-version = "0.0.23"
created-at = 2026-11-03 15:38:15.600801+00:00
@@ -2528,7 +2635,6 @@ command-line = [
"--all-groups",
"--all-extras",
"--no-cache",
- "--upgrade",
]
input-path = "pyproject.toml"
mode = "universal"
diff --git a/pyproject.toml b/pyproject.toml
index 48af5e2..6463ace 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,6 +5,7 @@ requires = [
"hatch-vcs>=0.5",
"hatchling>=1.27; python_version<'3.10'",
"hatchling>=1.28; python_version>='3.10'",
+ "python-sbom[hatch]>=0.1.2; python_version>='3.11'",
]
[project]
@@ -79,6 +80,7 @@ build = [
"hatch-vcs>=0.5",
"hatchling>=1.27; python_version<'3.10'",
"hatchling>=1.28; python_version>='3.10'",
+ "python-sbom[hatch]>=0.1.2; python_version>='3.11'",
"setuptools>=80",
]
pkg-meta = [
@@ -103,6 +105,7 @@ release-prepare = [
version.source = "vcs"
metadata.hooks.fancy-pypi-readme.content-type = "text/markdown"
metadata.hooks.fancy-pypi-readme.fragments = [ { path = "README.md" } ]
+build.hooks.custom.path = "hatch_build.py"
[tool.uv]
dependency-groups.docs = { requires-python = ">=3.12" }
@@ -270,6 +273,9 @@ type = [
{ directory = "packaging", name = "Packaging", showcontent = true },
]
+[tool.python-sbom]
+supplier = { name = "tox-dev", url = [ "https://github.com/tox-dev" ] }
+
[tool.nab]
mode = "universal"
base-group = "project-runtime"
@@ -280,6 +286,9 @@ matrix.platforms = [ "linux_x86_64" ]
# release-creator 4.4.0 needs a seven-day cutoff exception.
# expires 2026-11-09
packages.release-creator.uploaded-prior-to = 2026-11-02T21:45:00Z
+# python-sbom 0.1.2 needs a seven-day cutoff exception.
+# expires 2026-11-15
+packages.python-sbom.uploaded-prior-to = 2026-11-08T17:54:00Z
[tool.simplint]
docstring-style = "rst"
diff --git a/tox.toml b/tox.toml
index d95af14..6fb8b79 100644
--- a/tox.toml
+++ b/tox.toml
@@ -33,12 +33,12 @@ default_base_python = [ "3.15" ]
[env."type-3.15"]
description = "run type check on code base (3.15)"
-deps = [ "gitpython>=3.2", "packaging>=26.2", "ty==0.0.84" ]
+deps = [ "gitpython>=3.2", "hatchling>=1.27", "packaging>=26.2", "python-sbom[hatch]>=0.1.2; python_version>='3.11'", "ty==0.0.84" ]
commands = [ [ "ty", "check", "--output-format", "concise", "--error-on-warning", "--python-version", "3.15", "." ] ]
[env."type-3.9"]
description = "run type check on code base (3.9)"
-deps = [ "gitpython>=3.2", "packaging>=26.2", "ty==0.0.84" ]
+deps = [ "gitpython>=3.2", "hatchling>=1.27", "packaging>=26.2", "python-sbom[hatch]>=0.1.2; python_version>='3.11'", "ty==0.0.84" ]
commands = [ [ "ty", "check", "--output-format", "concise", "--error-on-warning", "--python-version", "3.9", "." ] ]
[env.docs]