From be8cd44c8011e03dd17a49a73b7d6288efa0437d Mon Sep 17 00:00:00 2001 From: Bowen Deng Date: Tue, 27 Oct 2026 11:37:03 -0700 Subject: [PATCH] docs(docker): ship a NOTICE recording what the images contain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The images carry no model weights: every MLIP checkpoint is fetched at run time into the mounted model cache. That is load-bearing for FAIRChem, whose UMA checkpoints are a gated repository (manual approval, FAIR Chemistry licence) that fairchem-core downloads with the caller's own HF_TOKEN — an image shipping them would be redistributing them. What the images do carry is software from PyPI, most of it permissive, plus three strong-copyleft packages: pymupdf (AGPL-3.0 or commercial) and openbabel-wheel (GPL) in cpu, mlip and fairchem, and python_hostlist (GPL-2.0+) in mlip. Record that inventory per image, say where the corresponding source is, and state the rule that no build step may pre-download weights. docker/NOTICE.md is copied into every image next to the server map, so the text travels with the artefact it describes rather than living only in the repository. The README gains a section pointing at it, with a command that verifies a given image carries no checkpoints. --- docker/Dockerfile | 2 +- docker/Dockerfile.cuda | 2 +- docker/NOTICE.md | 83 ++++++++++++++++++++++++++++++++++++++++++ docker/README.md | 23 ++++++++++++ 4 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 docker/NOTICE.md diff --git a/docker/Dockerfile b/docker/Dockerfile index 0caf4e18..84ad8f34 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -77,7 +77,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ COPY src/ ${REPO_DIR}/src/ COPY skills/ ${REPO_DIR}/skills/ COPY venv/run venv/servers.tsv venv/platforms.tsv ${REPO_DIR}/venv/ -COPY docker/entrypoint.sh docker/images.json docker/render.py ${REPO_DIR}/docker/ +COPY docker/entrypoint.sh docker/images.json docker/render.py docker/NOTICE.md ${REPO_DIR}/docker/ RUN --mount=type=cache,target=/root/.cache/uv \ extras="--all-extras"; \ if grep -q '^cu126 = ' venv/${VENV}/pyproject.toml; then extras="${extras} --no-extra cu126"; fi; \ diff --git a/docker/Dockerfile.cuda b/docker/Dockerfile.cuda index eadbc766..632e1b0d 100644 --- a/docker/Dockerfile.cuda +++ b/docker/Dockerfile.cuda @@ -77,7 +77,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \ COPY src/ ${REPO_DIR}/src/ COPY skills/ ${REPO_DIR}/skills/ COPY venv/run venv/servers.tsv venv/platforms.tsv ${REPO_DIR}/venv/ -COPY docker/entrypoint.sh docker/images.json docker/render.py ${REPO_DIR}/docker/ +COPY docker/entrypoint.sh docker/images.json docker/render.py docker/NOTICE.md ${REPO_DIR}/docker/ RUN --mount=type=cache,target=/root/.cache/uv \ set -eux; \ for v in ${VENVS}; do \ diff --git a/docker/NOTICE.md b/docker/NOTICE.md new file mode 100644 index 00000000..bfcf709d --- /dev/null +++ b/docker/NOTICE.md @@ -0,0 +1,83 @@ +# NOTICE: third-party software in the container images + +The images published under `ghcr.io/learningmatter-mit` package this repository's +own code (MIT, see `LICENSE`) together with the software each +`venv//uv.lock` pins. This file records what that means, and the one thing +the images deliberately do not carry. + +## The images carry no model weights + +Every MLIP checkpoint is fetched at run time by the code that needs it, into the +mounted model cache (`ATOMISTIC_MODEL_CACHE`, `HF_HOME`, `TORCH_HOME`, +`MATGL_CACHE`), never into an image layer. Three things hold that: + +- `.dockerignore` excludes `*.pt`, `*.pth`, `*.ckpt`, `*.model`, `*.traj` and + `*.h5` from the build context, along with `**/.venv/`; +- the Dockerfiles copy only `src/`, `skills/`, `venv/` and `docker/`, then run + `uv sync --frozen` from the locks; +- `/opt/model-cache` is a `VOLUME` that is empty at build. + +The FAIRChem UMA checkpoints are why this matters. `facebook/UMA` is a gated +repository (`gated: manual`, licence `other` — the FAIR Chemistry licence), and +`fairchem-core` downloads the weights at run time with the **user's own** +`HF_TOKEN`, for a user who has been granted access. An image carrying those +weights would be redistributing them; these do not. Without a token a +`load_model` call fails with 401, which is the intended behaviour, not a bug. + +The two `.pt` files inside `fairchem-core` — `models/uma/Jd.pt` (22 KB) and +`models/uma/common/quaternion/wigner_d_coefficients.pt` (30 KB) — are precomputed +Wigner-D constant tables distributed in the MIT-licensed wheel. They are not +model weights. + +**Do not add a build step that pre-downloads weights.** It would turn a permitted +run-time download into a redistribution the model licences do not allow, and +would grow every image by gigabytes. Check an image with: + +```bash +docker run --rm --entrypoint sh -c \ + 'ls -A /opt/model-cache; find / -xdev \( -name "*.ckpt" -o -name "*.safetensors" \) -size +20M' +``` + +A correct image prints nothing but build-time placeholders. + +## Strong-copyleft packages + +Most of the pinned software is permissive (MIT, BSD, Apache-2.0). These are the +exceptions, as installed in each image: + +| Package | Version | Licence | Images | +| ----------------- | -------- | --------------------------- | ----------------------------------- | +| `pymupdf` | 1.28.2 | AGPL-3.0 or Artifex commercial | cpu, mlip, fairchem | +| `openbabel-wheel` | 3.1.1.23 | GPL | cpu, mlip, fairchem, generative | +| `python_hostlist` | 2.3.0 | GPL-2.0+ | mlip | + +They arrive as dependencies of the environments in `venv/` — `pymupdf` extracts +PDF text in the paper-download and synthesis-extraction paths, and +`openbabel-wheel` ships the `obabel` converter that the docking skills invoke as +a command — not as modifications of this repository's code, and the images do +not alter them. In the generative image only the `adit` environment pulls one in. + +## Corresponding source + +For an image tag, the corresponding source is: + +- this repository at tag `v` — `src/`, `skills/`, `docker/` and `venv/run`; +- the exact package versions in `venv//uv.lock`, for which PyPI serves the + source distributions. To fetch one: + + ```bash + pip download --no-binary :all: --no-deps -d /tmp/src pymupdf==1.28.2 + ``` + +- the base images: `ubuntu:24.04`, and for the generative image + `nvidia/cuda:13.0.1-devel-ubuntu24.04`, redistributed under NVIDIA's container + licence. + +If you redistribute an image and need a written offer of corresponding source, +open an issue on the repository. + +## Scope + +This file is an inventory of what the images contain, kept so that whoever +distributes them can make the call knowingly. It is not legal advice, and it +makes no claim about obligations that apply to a given form of distribution. diff --git a/docker/README.md b/docker/README.md index 13b9771f..9f6ab13d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -115,6 +115,29 @@ On HPC clusters without a Docker daemon, set `ATOMISTIC_RUNTIME: apptainer` in ` venv/run --setup ``` +## Licences: What the Images Contain + +An image ships this repository's MIT code plus the software its +`venv//uv.lock` pins. It carries **no model weights**: every checkpoint is +fetched at run time into the mounted model cache under the caller's own +credentials. FAIRChem's UMA is the case that makes this a rule rather than a +convenience — `facebook/UMA` is a gated repository (manual approval, the FAIR +Chemistry licence), so weights are downloaded by `fairchem-core` with the user's +`HF_TOKEN` after access is granted. An image without a token cannot obtain them, +and an image that shipped them would be redistributing them. + +Never add a build step that pre-downloads weights. Verify an image with: + +```bash +docker run --rm --entrypoint sh -c 'ls -A /opt/model-cache; \ + find / -xdev \( -name "*.ckpt" -o -name "*.safetensors" \) -size +20M' +``` + +A few dependencies are strongly copyleft — `pymupdf` (AGPL-3.0 or commercial) and +`openbabel-wheel` (GPL) in `cpu`, `mlip` and `fairchem`, plus `python_hostlist` +(GPL-2.0+) in `mlip`. `docker/NOTICE.md` lists them per image and says where the +corresponding source is; it is copied into every image. + ## Known Limitations - **Generative servers on x86_64** run natively where glibc ≥ 2.32 (PyG's wheels) and from the amd64 `generative` image elsewhere.