# syntax=docker/dockerfile:1 # # Single image, all six daemons. docker-compose runs one container per daemon # off this image with a different command — the native-lib + toolchain surface # is shared, so separate images would duplicate ~all of it. Isolation still # holds: each daemon is its own container/namespace, only mavend mounts the key # and the db volume. # # Native deps are the prebuilt artifacts the repo already carries under deps/ # (libwhisper+ggml-vulkan, onnxruntime, piper/espeak). We do NOT build # whisper.cpp from source here — COPY the prebuilt .so and headers. # ponytail: prebuilt-lib copy, not a from-source build. Add a whisper.cpp build # stage if you ever need reproducibility / a different arch than the host libs. # trixie, not bookworm: the prebuilt deps/lib/*.so are linked against # glibc 2.38 + GLIBCXX 3.4.32 (built on the Arch host). bookworm's glibc 2.36 # is too old and the link fails on missing symbols. trixie ships glibc 2.40. # 1.25 to match go.mod's `go 1.25.5` directive: the official golang images pin # GOTOOLCHAIN=local, so a 1.24 builder refuses the module instead of fetching a # newer toolchain. Bump this in lockstep with the go.mod directive. FROM golang:1.25-trixie AS build WORKDIR /src # libvulkan-dev: libggml-vulkan.so needs libvulkan.so.1 at link time. RUN apt-get update && apt-get install -y --no-install-recommends libvulkan-dev \ && rm -rf /var/lib/apt/lists/* # native build inputs (prebuilt libs + headers), then module cache, then source COPY deps/lib/ /src/deps/lib/ COPY deps/piper/ /src/deps/piper/ COPY deps/include/ /src/deps/include/ COPY deps/whisper.cpp/ggml/include/ /src/deps/whisper.cpp/ggml/include/ COPY go.mod go.sum ./ # vendored deps: the ecosystem clients (hexis) are replace-directed at sibling # repos outside the build context, so we can't `go mod download` them here. # `go mod vendor` (run on a host where the siblings exist) bakes them into # vendor/; with it present, go build uses it and never touches the replace # paths. Regenerate with `go mod vendor` whenever a sibling client API changes. COPY vendor/ ./vendor/ COPY cmd/ ./cmd/ # force rebuild 2026-07-10T16:50 COPY internal/ ./internal/ # CGO wiring mirrors the Makefile; rpath points at the RUNTIME lib location so # the binaries find their .so at /opt/maven/lib regardless of LD_LIBRARY_PATH. ENV CGO_ENABLED=1 \ CGO_CFLAGS="-I/src/deps/include -I/src/deps/whisper.cpp/ggml/include" \ CGO_LDFLAGS="-L/src/deps/lib -L/src/deps/piper -Wl,-rpath,/opt/maven/lib" RUN go build -o /out/mavend ./cmd/mavend && \ go build -o /out/mavsttd ./cmd/mavsttd && \ go build -o /out/mavttsd ./cmd/mavttsd && \ go build -o /out/mavweb ./cmd/mavweb && \ go build -o /out/mavpoll ./cmd/mavpoll && \ go build -o /out/mavcaldav ./cmd/mavcaldav # llama.cpp Vulkan build — the phraser/router LFM engine (llama-server). Built # from source (not a prebuilt vendored blob) so the binary's glibc/GLIBCXX match # the trixie runtime and GPU offload rides mesa's RADV Vulkan driver — the same # path whisper already uses on homesrv's AMD iGPU (RADV RENOIR). Pinned to b9601 (parity with # the host's known-good build). Static (BUILD_SHARED_LIBS=OFF) ⇒ one self- # contained binary, no libggml/libllama .so to juggle in the runtime; only # libvulkan.so.1 + libgomp (both already in the runtime) are needed at load. FROM debian:trixie-slim AS llama ARG LLAMA_REF=b9601 RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates git cmake build-essential libvulkan-dev \ glslc glslang-tools spirv-headers spirv-tools \ && rm -rf /var/lib/apt/lists/* RUN git clone --depth 1 --branch ${LLAMA_REF} \ https://github.com/ggml-org/llama.cpp /src/llama.cpp WORKDIR /src/llama.cpp RUN cmake -B build \ -DCMAKE_BUILD_TYPE=Release \ -DGGML_VULKAN=ON \ -DBUILD_SHARED_LIBS=OFF \ -DLLAMA_CURL=OFF \ -DLLAMA_BUILD_SERVER=ON \ && cmake --build build --config Release -j"$(nproc)" --target llama-server FROM debian:trixie-slim AS runtime # tzdata so the TZ env (set in compose) resolves — otherwise Go can't load the # zone and time.Now() stays UTC, and mavend answers clock/date queries and # evaluates quiet-hours in UTC. RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates libvulkan1 mesa-vulkan-drivers libgomp1 tzdata \ python3 python3-pip && \ pip3 install --no-cache-dir --break-system-packages 'dateparser==1.4.1' && \ apt-get purge -y --auto-remove python3-pip && \ rm -rf /var/lib/apt/lists/* # runtime native libs: whisper/ggml (incl. vulkan) are real files in deps/lib. COPY deps/lib/ /opt/maven/lib/ COPY deps/piper/ /opt/maven/piper/ # The router embedder needs onnxruntime 1.26, but deps/lib only carries dangling # symlinks to it (they point at an absolute HOST path that isn't in the image). # Ship the real 1.26 lib and repoint the symlinks. Piper keeps its OWN # onnxruntime 1.14 under /opt/maven/piper (found via piper's $ORIGIN runpath + # its exact soname libonnxruntime.so.1.14.1) — do NOT copy piper's .so into # /opt/maven/lib, that clobbers 1.26 and mavend dies with "ORT API base: 2". COPY deps/onnxruntime-linux-x64-1.26.0/lib/libonnxruntime.so.1.26.0 /opt/maven/lib/ RUN cd /opt/maven/lib \ && ln -sf libonnxruntime.so.1.26.0 libonnxruntime.so \ && ln -sf libonnxruntime.so.1.26.0 libonnxruntime.so.1 COPY --from=build /out/ /opt/maven/bin/ # the LFM engine: static Vulkan llama-server on PATH; the phraser spawns it by # name (bin_path "llama-server"). GPU offload needs /dev/dri passed to mavend. COPY --from=llama /src/llama.cpp/build/bin/llama-server /opt/maven/bin/ ENV LD_LIBRARY_PATH=/opt/maven/lib PATH=/opt/maven/bin:$PATH # unprivileged; core owns the key + db, modules own nothing. RUN useradd -r -u 10001 -m maven \ && mkdir -p /run/maven /var/lib/maven \ && chown maven:maven /run/maven /var/lib/maven USER maven WORKDIR /opt/maven