Software Bill of Materials (SBOM)¶
What an SBOM is¶
A Software Bill of Materials is a machine-readable inventory of every third-party component your project depends on, along with each component's version and license. It is the supply-chain equivalent of an ingredient list on a food package.
For Dotty specifically, "every component" spans three very different worlds:
- Server-side Python — the FastAPI bridge, custom xiaozhi providers,
pinned in
bridge/requirements.txt. - Container images — the
xiaozhi-esp32-serverimage (and any sidecars) referenced fromdocker-compose.yml, ideally pinned by@sha256:...digest for reproducibility. - Firmware components — every Espressif/community ESP-IDF managed component pulled in by the StackChan firmware build.
Why it matters¶
- Supply-chain security. When CVE-2024-XXXX drops on a component you've never heard of, the SBOM tells you in 30 seconds whether you ship it.
- License compliance. Mixing GPL/AGPL code into a permissive distribution can quietly create obligations you did not intend. The SBOM surfaces every license up-front.
- Reproducibility. Pinned digests + a snapshotted SBOM let a future contributor (or you in six months) rebuild the exact tree shipped at a given tag.
- Trust. Downstream users can audit what runs on the device that listens to their family.
This is a LEVEL-2 polish item. The scaffold exists today; full CycloneDX 1.5 conformance + automated CI generation are tracked as follow-ups.
How to generate¶
One-shot¶
The script writes sbom.json at the repo root with three sections — server,
containers, firmware. A summary table is printed to stdout.
The output file is gitignored so each run reflects current dependencies.
Server section — pip-licenses¶
The server section requires pip-licenses installed in a Python environment
that has the bridge's dependencies present. Recommended setup:
python -m venv .venv-sbom
source .venv-sbom/bin/activate
pip install -r bridge/requirements.txt pip-licenses
./scripts/generate-sbom.sh
If pip-licenses is not installed, the script logs a warning and emits an
{"available": false, ...} placeholder for the server section so the rest of
the SBOM (containers + firmware) still populates.
Firmware section — submodule required¶
The firmware lives at firmware/firmware/ and pulls in 60+ ESP-IDF managed
components on first build. If the firmware submodule is uninitialised, the
firmware section will be marked unavailable. Check it out first:
Sample output¶
{
"bomFormat": "CycloneDX-ish",
"specVersion": "1.5-loose",
"metadata": {
"timestamp": "2026-04-25T12:00:00Z",
"tool": "scripts/generate-sbom.sh"
},
"server": [
{ "Name": "fastapi", "Version": "0.115.4", "License": "MIT" },
{ "Name": "uvicorn", "Version": "0.34.0", "License": "BSD-3-Clause" }
],
"containers": [
{
"image": "xiaozhi-esp32-server-piper:local",
"digest": null
}
],
"firmware": {
"available": true,
"count": 61,
"components": [
{
"name": "espressif__button",
"version": "4.1.6",
"repository": "git://github.com/espressif/esp-iot-solution.git"
}
]
}
}
Component count¶
A snapshot from main at the time of writing:
| Section | Approx. count | Source |
|---|---|---|
| server | ~10 | bridge/requirements.txt + transitive deps |
| containers | 1 | docker-compose.yml |
| firmware | ~60+ | firmware/firmware/managed_components/ |
License diversity¶
The expected mix on the server side is MIT and Apache-2.0 with a few BSD-3-Clause entries (FastAPI/Starlette, uvicorn, pydantic). On the firmware side, Apache-2.0 dominates (Espressif convention).
If the SBOM ever surfaces a GPL-2.0, GPL-3.0, or AGPL-3.0 entry,
treat that as a flag to investigate — those licenses can carry copyleft
obligations that are incompatible with how this project is distributed.
File an issue and check LICENSE + COMPATIBILITY.md before merging.
Cross-references¶
COMPATIBILITY.md— versioning + release process the SBOM snapshots against.SECURITY.md— threat model + how to report security issues that an SBOM scan might surface.docs/signed-releases.md— companion scaffold for GPG-signing release artifacts.
Follow-ups¶
- Full CycloneDX 1.5 conformance (currently the output is loosely shaped
in that style;
bomFormatis"CycloneDX-ish"). - CI integration — generate the SBOM as part of the firmware-release workflow and attach it to the GitHub Release alongside the binaries.
- CVE scanning — feed the SBOM into a tool such as
grypeortrivy fs --format cyclonedx-jsonto flag known vulnerabilities.