Skip to content

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.