# CanLex MCP server -- remote (streamable-HTTP) image. # # Builds anywhere: Hugging Face Spaces, Google Cloud Run, Fly.io, plain Docker. # Retrieval is fully local and key-free; the optional CanLII case citator reads # its key from the CANLII_API_KEY environment variable (supplied as a host # secret -- the key is never copied into the image). FROM python:3.12-slim # libgomp1 is the OpenMP runtime that onnxruntime (the reranker) links against. RUN apt-get update \ && apt-get install -y --no-install-recommends libgomp1 \ && rm -rf /var/lib/apt/lists/* # Run as a non-root user (UID 1000) -- required by Hugging Face Spaces. RUN useradd --create-home --home-dir /app --uid 1000 app WORKDIR /app # Python dependencies first, so this layer caches across code changes. COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Application code and the processed corpus (section-chunk JSON). COPY --chown=app:app canlex/ ./canlex/ COPY --chown=app:app data/processed/*.json ./data/processed/ USER app ENV HOME=/app \ HF_HOME=/app/.hf_cache \ CANLEX_HTTP=1 \ PORT=7860 \ PYTHONUNBUFFERED=1 # Build the semantic embeddings and pre-fetch the cross-encoder model, so the # model cache is baked into the image and the first request needs no network. RUN python -m canlex.embed \ && python -c "from canlex.rerank import Reranker; Reranker()" # From here on, model files are served from the baked cache, never fetched. ENV HF_HUB_OFFLINE=1 EXPOSE 7860 CMD ["python", "-m", "canlex.server"]