proximo

The stack's Go services ship as one published image, pinned to the CLI version

Until now every host compiled the stack for itself: dns, watcher and inspector each carried a build: stanza that ran go install github.com/filippolmt/proximo/cmd/<x>@${PROXIMO_REF} inside golang:alpine, so a first proximo up pulled a ~350 MB toolchain image and ran three module downloads and three compilations before anything served a request. Instead, the release pipeline builds one multi-architecture image, ghcr.io/filippolmt/proximo, holding all three binaries, and compose selects the service with a per-service entrypoint:. The image tag is the CLI's own version — the same devmain, 0.4.0v0.4.0 mapping moduleRef() already applied to the module ref, moved to the image ref.

Pinning to the CLI version rather than tracking latest is what keeps the two halves of a release honest. The binary embeds its own copy of the compose assets and stamps proximo.version on every stack service, and proximo update uses that label to detect skew. A floating tag would let a binary installed weeks ago pull a watcher that speaks a label contract it has never seen, and would do it precisely where the existing skew detection cannot look: between the binary and the image, not between the binary and the materialised stack.

One image rather than three, and the usual argument for that is wrong here. The bytes are a wash: with three images the containers share the alpine base layer by digest, so the download is 5.374 + 6.554 + 7.275 MB of binaries either way (measured, -trimpath -ldflags "-s -w", linux/amd64). What actually decides it is publishing cost — three GHCR packages, three BuildKit cache refs, and a matrix of 3 components × 2 architectures instead of 2 build legs and one manifest merge — and version coherence: watcher and inspector hold a live contract, since the watcher routes inspected hosts at the hop and hands it the real backend in X-Proximo-Backend while the hop mints the correlation id the watcher must find again in the Access record. Per-component images would make it easy to run a combination no CI has ever built, and to break it silently on exactly that header.

Considered Options

Consequences