ADR-003: Host-native processing with Docker projections
Status: accepted · Date: 2026-09-10
Context
The host is an Apple M4 Pro with 48 GB RAM. Apple Vision/PDFKit and Metal acceleration are host capabilities; Docker Desktop on macOS is not the right place to depend on GPU access.
Decision
Run Python orchestration, Swift OCR, Codex CLI, and Hugging Face inference on the host. Run LM Studio on the host for embeddings only. Run only the FastAPI artifact service and Neo4j in Compose. Persist artifacts, state, logs, and graph data in separate named volumes. Bind all ports to loopback.
Mechanics and tests
The host uploads content-addressed bundles to the artifact API. The API has a bearer secret, bounded streaming chunks, checksum validation, and no filesystem or shell endpoint. Compose health checks, resource limits, restart policy, and log rotation are required. Tests run docker compose config and API contract tests without requiring source data.
Alternatives and consequences
Containerized model servers would complicate host GPU access. A host-only Neo4j would complicate reset and handoff. Codex CLI is run read-only and ephemeral so distillation cannot mutate the project. The split requires documented host setup and port checks, but keeps expensive and native concerns where they belong. Revisit for Linux/NVIDIA deployment as a separate profile.
Alternatives considered
Putting OCR and inference in containers was rejected because Docker Desktop does not provide the same Apple Vision or Metal path and would make performance and permissions opaque. Running Neo4j directly on the host was rejected because a named volume makes reset, backup, and handoff deterministic. Mounting the Docker socket into the artifact service was rejected because it would grant an unnecessary control plane.
Implementation detail and example
The host pipeline reads an iCloud path, invokes the Swift helper for PDF extraction/OCR, and uploads an immutable bundle to FastAPI over loopback. Neo4j receives only approved projections. LM Studio embeddings listen on host port 1234; the artifact API listens on 8765; Neo4j uses 7474 and 7687. Compose publishes each port to 127.0.0.1 and the API accepts a bearer secret from a file.
Configuration and failure behavior
Artifact service limits are 1 CPU and 1 GiB; Neo4j is limited to 2 CPUs and 4 GiB with a 1 GiB initial heap, 2 GiB maximum heap, and 1 GiB page cache. Health checks gate dependent work, restart policies are conservative, and JSON logs rotate at a bounded size. A failed health check pauses the stage and preserves its input; it does not cause the host pipeline to write directly into a Docker volume or execute a recovery shell command.
Verification and revisit trigger
Validate with docker compose config --quiet, service health checks, authenticated upload/idempotent replay, and a graph rebuild from canonical fixtures. Revisit for a Linux/NVIDIA profile only as a separately documented topology with equivalent privacy and reproducibility tests.