docs/7-DEVELOPMENT/decisions/ADR-007-optin-runtimes.md
The Content Core 2.x upgrade (#939) exposed two selectable extraction runtimes that are far heavier than the rest of the stack:
content-core[docling]) — layout-aware document parsing and OCR. Pulls a large machine-learning stack (torch/transformers and models), from several hundred MB to multiple GB.content-core[crawl4ai]) — JavaScript rendering via a bundled Chromium browser (~300 MB plus system libraries).The initial 2.x work bundled Crawl4AI into every image (#1118) and shipped an OCR toggle (#1120) that depended on Docling — which was never actually installed, making the toggle a silent no-op and image sources unsupported. So the default image was simultaneously too heavy (Chromium for everyone) and missing a runtime its own UI advertised.
Baking both into the default image would push it into the multi-GB range and contradict Open Notebook's lean, privacy-first, self-hostable posture (ADR-002 keeps this repo focused on the knowledge layer, delegating extraction to Content Core). Most users need neither runtime.
Keep a single lean default image. Make Docling and local Crawl4AI opt-in via environment variables; when enabled, install them at container startup and cache the downloads on the data volume.
OPEN_NOTEBOOK_ENABLE_DOCLING and OPEN_NOTEBOOK_ENABLE_CRAWL4AI. A remote Crawl4AI server (CRAWL4AI_API_URL) needs no local install.scripts/docker-entrypoint.sh) runs the install before api/worker/frontend start (blocking), pinning each extra to the base image's content-core version for dependency compatibility.UV_CACHE_DIR, PLAYWRIGHT_BROWSERS_PATH and HF_HOME live under /app/data (the user's volume), so a second boot reinstalls from cache without re-downloading. The virtualenv stays in the image layer and is repopulated from the cache each boot — immune to Python-version bumps in the base image. Chromium's apt system libraries live in the container filesystem and are reinstalled once per container.GET /api/capabilities) reports actual importability, and the Settings UI disables unavailable engines/OCR with an env-var hint — so the UI never advertises a runtime that isn't there (including mid-install).content-core version, done in the entrypoint.extract_content() boundary, adding an opt-in install path touched infra + one probe endpoint, not the extraction code.