## ── Builder stage ──────────────────────────────────────────────────────────── FROM python:3.14.8-alpine3.23@sha256:e7cff362a12454395f8dc99607c21a602ea880cdce03538877b90c2ba932b16f AS builder WORKDIR /app # CPython 3.14.8 predates the upstream tarfile filter-rejection repair. # Verify the complete upstream source before and after this bounded overlay. COPY deploy/docker/vendor/cpython-3.14.8/apply_tarfile_fix.py /tmp/apply_tarfile_fix.py RUN python /tmp/apply_tarfile_fix.py && rm /tmp/apply_tarfile_fix.py ARG HTTP_PROXY ARG HTTPS_PROXY ARG NO_PROXY ARG SSL_CERT_FILE ARG REQUESTS_CA_BUNDLE ARG CURL_CA_BUNDLE ARG PIP_CERT ENV HTTP_PROXY=${HTTP_PROXY} \ HTTPS_PROXY=${HTTPS_PROXY} \ NO_PROXY=${NO_PROXY} \ SSL_CERT_FILE=${SSL_CERT_FILE} \ REQUESTS_CA_BUNDLE=${REQUESTS_CA_BUNDLE} \ CURL_CA_BUNDLE=${CURL_CA_BUNDLE} \ PIP_CERT=${PIP_CERT} # Build-time deps for wheel compilation when musllinux wheels are unavailable. # Pins track the base-image digest above; refresh both together. RUN apk add --no-cache build-base~=0.5 ca-certificates~=20260909 git~=2.52 libffi-dev~=3.5 linux-headers~=6.16 \ && apk upgrade --no-cache --available \ && update-ca-certificates COPY --from=ghcr.io/astral-sh/uv:0.13.5@sha256:32a7bb39b496babbd997b0a39a4d66c126bdaf5661b302bf6f06abda8deca6ff /uv /uvx /bin/ COPY pyproject.toml uv.lock README.md LICENSE ./ COPY docs/registry/PYPI_README.md ./docs/registry/ COPY src/ ./src/ COPY deploy/supabase/postgres/ ./deploy/supabase/postgres/ COPY deploy/docker/runtime-security-requirements.txt ./deploy/docker/runtime-security-requirements.txt COPY deploy/docker/tornado-security-requirements.txt ./deploy/docker/tornado-security-requirements.txt # tree-sitter-typescript 0.23.2 has no musllinux aarch64 wheel and its PyPI # sdist omits internal headers needed to compile the bundled grammars. # Keep the exact upstream v0.23.2 headers in the build context so Python 3.14 # arm64 builds do not depend on an incomplete sdist. See the adjacent README # and license for provenance and the pinned source digests. COPY deploy/docker/vendor/tree-sitter-typescript-0.23.2/tree_sitter/parser.h \ /usr/local/include/tree_sitter/parser.h COPY deploy/docker/vendor/tree-sitter-typescript-0.23.2/common/scanner.h \ /usr/local/include/tree-sitter-typescript/common/scanner.h # Extras baked into the published control-plane image. Cloud SDKs (aws/azure/gcp) # ship by default so self-hosted BYOC (connect an AWS/Azure/GCP account read-only, # incl. IRSA/workload-identity) works out of the box (#3832). Override at build # time for a lean image, e.g. --build-arg AGENT_BOM_EXTRAS=api,snowflake,postgres. ARG AGENT_BOM_EXTRAS=api,snowflake,postgres,aws,azure,gcp RUN set -eu; \ test "$(sha256sum /usr/local/include/tree_sitter/parser.h | cut -d ' ' -f 1)" = \ "a1f6ef161fbaf48a0e10fca90ef5290a062462b307b3898aa562993853b9f80a"; \ test "$(sha256sum /usr/local/include/tree-sitter-typescript/common/scanner.h | cut -d ' ' -f 1)" = \ "da66ef2bd14a3f7ea743e25ba068c6c9aae2c3509db200ff80c4a0e6116e564c"; \ mkdir -p /usr/local/include/tree-sitter-typescript/tsx/src; \ sync_args=""; \ for extra in $(printf '%s' "${AGENT_BOM_EXTRAS}" | tr ',' ' '); do \ case "${extra}" in *[!a-zA-Z0-9_-]*|'') exit 2 ;; esac; \ sync_args="${sync_args} --extra ${extra}"; \ done; \ CFLAGS="-I/usr/local/include/tree-sitter-typescript/tsx/src" \ uv sync --locked --no-dev --no-editable ${sync_args}; \ uv pip install --no-cache --no-config --python /app/.venv/bin/python --no-deps --require-hashes \ -r deploy/docker/runtime-security-requirements.txt; \ if /app/.venv/bin/python -c "import importlib.util, sys; sys.exit(importlib.util.find_spec('tornado') is None)"; then \ uv pip install --no-cache --no-config --python /app/.venv/bin/python --no-deps --require-hashes \ --only-binary=:all: -r deploy/docker/tornado-security-requirements.txt; \ fi ## ── Runtime stage ──────────────────────────────────────────────────────────── FROM python:3.14.8-alpine3.23@sha256:e7cff362a12454395f8dc99607c21a602ea880cdce03538877b90c2ba932b16f ARG VERSION=0.114.0 ARG HTTP_PROXY ARG HTTPS_PROXY ARG NO_PROXY ARG SSL_CERT_FILE ARG REQUESTS_CA_BUNDLE ARG CURL_CA_BUNDLE ARG PIP_CERT LABEL maintainer="W S <34316639+msaad00@users.noreply.github.com>" LABEL description="AI supply-chain BOM: every agent, MCP server and AI package, and what each one can reach." LABEL org.opencontainers.image.version="${VERSION}" LABEL org.opencontainers.image.source="https://github.com/koda-ai-studio/agent-bom" LABEL org.opencontainers.image.licenses="Apache-2.0" # Copy the frozen environment from the identical Python base image. Keeping # the same absolute path preserves console-script shebangs generated by uv. COPY --from=builder /app/.venv /app/.venv COPY --from=builder /usr/local/lib/python3.14/tarfile.py /usr/local/lib/python3.14/tarfile.py COPY --from=builder /app/LICENSE /app/LICENSE COPY --from=builder /app/deploy/supabase/postgres /opt/agent-bom/deploy/supabase/postgres # Repository URL scans shallow-clone with git at runtime as the non-root user. RUN apk add --no-cache ca-certificates~=20260909 git~=2.52 libffi~=3.5 libgcc~=15.2 libstdc++~=15.2 \ && apk upgrade --no-cache --available \ && update-ca-certificates # The runtime never installs packages. Remove packaging tools and pip's vendored # dependency tree so scanner-visible build utilities cannot become runtime CVEs. RUN python -m pip uninstall --yes setuptools wheel pip \ && rm -rf /root/.cache/pip # Create non-root user for least-privilege execution RUN addgroup -S abom && adduser -S -G abom abom WORKDIR /workspace RUN chown abom:abom /workspace # Pre-create the state dir and hand ownership to abom (uid/gid of the abom # user) so a named volume or bind mount at /home/abom/.agent-bom is writable. # Without this the mount lands root-owned and abom (non-root) cannot create # jobs.db / vulns.db, crash-looping the API. RUN mkdir -p /home/abom/.agent-bom && chown -R abom:abom /home/abom USER abom ENV PYTHONUNBUFFERED=1 ENV TERM=xterm-256color ENV VIRTUAL_ENV=/app/.venv ENV PATH="/app/.venv/bin:${PATH}" ENV HTTP_PROXY=${HTTP_PROXY} ENV HTTPS_PROXY=${HTTPS_PROXY} ENV NO_PROXY=${NO_PROXY} ENV SSL_CERT_FILE=${SSL_CERT_FILE} ENV REQUESTS_CA_BUNDLE=${REQUESTS_CA_BUNDLE} ENV CURL_CA_BUNDLE=${CURL_CA_BUNDLE} ENV PIP_CERT=${PIP_CERT} # This is a generic CLI image whose default command exits. Long-lived API, # gateway, proxy, and MCP deployments define process-aware probes in their # Compose, Helm, or dedicated runtime-image contracts. HEALTHCHECK NONE ENTRYPOINT ["agent-bom"] CMD ["--help"]