`Dockerfile:70` and `REMAPPING/cicd/Dockerfile:103` built
`-p meridian-relay --bin meridian-relay` with no `--features zenoh`, and the
adapter sits behind `zenoh = ["meridian-pubsub/zenoh"]`, off by default. So in
every image this repo can build, `bus_backend_from_str` in
`crates/meridian-relay/src/config.rs` takes its `#[cfg(not(feature = "zenoh"))]`
arm and answers `MERIDIAN_BUS=zenoh|shadow` with
MERIDIAN_BUS=`zenoh` needs a relay built with the `zenoh` feature;
this binary constructs `redis` only
That is a boot failure, not a bus. Verified against both binaries built from
this tree: the feature-off relay prints exactly the line above and exits 1; the
feature-on relay accepts the name and moves on to demand
`MERIDIAN_ZENOH_ENDPOINTS`, and its accepted set is `redis, zenoh, shadow`
rather than `redis`. Found by the Provenance Auditor seat, Security & Identity
council, and recorded as meridian-nok3.
The flag goes on `cargo chef cook` as well as `cargo build`, because the two
layers must agree. Measured with `cargo tree -e normal,build` on this lockfile:
the feature takes the workspace graph the cook layer builds from 565 to 642
packages (+77) and the three-package selection from 505 to 596 (+91), and every
one of those lands in the source layer — invalidated by any edit under
`crates/` — if only the build line carries it. The symptom would be a build that
got slow rather than one that broke, which is why it is a comment between the
two lines rather than a note in a commit nobody re-reads.
Package-qualified (`meridian-relay/zenoh`) because three packages are selected;
a bare `--features zenoh` does not say which one it belongs to. `cargo chef`
0.1.71 — the version both Dockerfiles pin — forwards `--features` verbatim to
`cargo build`, and its skeleton preserves member `[features]` tables, so the
qualified name resolves there too.
Both published targets derive from this stage (`runtime` via
`stripped-binaries`, and `runtime-debug`), so both carry the adapter.
`MERIDIAN_BUS` still defaults to `redis`: this commit makes the variable
readable, not active.
Gates: just zenoh-check 0, just check-docker-context 0.
Signed-off-by: Joshua Belke <admin@aipowergrid.io>
Signed-off-by: Joshua Belke <joshua@innovationhub-act.org>
205 lines
11 KiB
Docker
205 lines
11 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
#
|
|
# Public Meridian relay image — published as registry.r2d2.office.ilab.zone/meridian:<tag>.
|
|
#
|
|
# Builds the `meridian-relay` binary (Rust 1.95) and the `meridian-web` static bundle
|
|
# (pnpm + vite), then assembles them into a small debian-slim runtime with
|
|
# `git` available (the relay shells out to git for repo hydrate / receive-pack
|
|
# / upload-pack — see crates/meridian-relay/src/api/git).
|
|
#
|
|
# Multi-arch is handled by running this same Dockerfile on native amd64 and
|
|
# native arm64 runners (see .github/workflows/docker.yml). The Dockerfile
|
|
# itself is platform-agnostic; do not add --platform pins.
|
|
|
|
ARG RUST_VERSION=1.95
|
|
ARG NODE_VERSION=24
|
|
ARG DEBIAN_VERSION=bookworm
|
|
|
|
# Optional extra CA bundle for builds behind a TLS-intercepting corporate proxy
|
|
# (e.g. a Cloudflare/Zscaler gateway that re-signs TLS). Empty by default, so
|
|
# public CI builds are unaffected. Point it at a PEM file in the build context:
|
|
# docker build --build-arg EXTRA_CA_CERTS=path/to/proxy-ca.pem ...
|
|
# Consumed by the network-touching stages below (cargo + pnpm).
|
|
ARG EXTRA_CA_CERTS=
|
|
|
|
# Optional npm registry for builds where the public registry is unreachable or
|
|
# policy-blocked (e.g. a corporate mirror / Artifactory). Empty default = public
|
|
# npmjs, so public CI builds are unaffected. Consumed by the web-builder stage.
|
|
ARG NPM_REGISTRY=
|
|
|
|
# ─── Stage 1: cargo-chef base ───────────────────────────────────────────────
|
|
FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS chef
|
|
# Trust an optional corporate-proxy CA before any network fetch (no-op if unset).
|
|
ARG EXTRA_CA_CERTS
|
|
COPY --chmod=0644 ${EXTRA_CA_CERTS:-Dockerfile} /tmp/extra-ca/src
|
|
RUN if [ -n "${EXTRA_CA_CERTS}" ]; then \
|
|
cp /tmp/extra-ca/src /usr/local/share/ca-certificates/extra-proxy-ca.crt \
|
|
&& update-ca-certificates \
|
|
&& echo "CARGO_HTTP_CAINFO=/etc/ssl/certs/ca-certificates.crt" >> /etc/environment; \
|
|
fi
|
|
ENV CARGO_HTTP_CAINFO=/etc/ssl/certs/ca-certificates.crt
|
|
RUN cargo install cargo-chef --locked --version 0.1.71
|
|
WORKDIR /build
|
|
|
|
# ─── Stage 2: plan dependency graph ─────────────────────────────────────────
|
|
# Only the manifests are needed to compute the recipe; this layer rebuilds
|
|
# only when Cargo.{toml,lock} or crate manifests change, not on every source
|
|
# edit.
|
|
FROM chef AS planner
|
|
COPY . .
|
|
RUN cargo chef prepare --recipe-path recipe.json
|
|
|
|
# ─── Stage 3: cook dependencies, then build the binary ──────────────────────
|
|
FROM chef AS builder
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
build-essential \
|
|
pkg-config \
|
|
libssl-dev \
|
|
ca-certificates \
|
|
git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
# Keep enough DWARF for native profilers to resolve optimized code to source
|
|
# locations. The normal runtime strips it below; runtime-debug retains it.
|
|
ENV CARGO_PROFILE_RELEASE_DEBUG=line-tables-only
|
|
COPY --from=planner /build/recipe.json recipe.json
|
|
# Cook the full workspace recipe — relay deps include workspace siblings, so
|
|
# scoping to -p meridian-relay misses transitive deps and re-builds them later.
|
|
#
|
|
# The feature list here MUST match the one on `cargo build` below. Measured
|
|
# with `cargo tree -e normal,build` on this lockfile: the feature takes the
|
|
# workspace graph this layer cooks from 565 to 642 packages (+77), and the
|
|
# three-package selection built below from 505 to 596 (+91). Cook without it
|
|
# and every one of those compiles in the source layer instead, where any edit
|
|
# under crates/ invalidates them. The failure is a build that got slow, not a
|
|
# build that broke, so it is the kind that survives review — hence both lines,
|
|
# and this note between them.
|
|
RUN cargo chef cook --release --recipe-path recipe.json --features meridian-relay/zenoh
|
|
COPY . .
|
|
# `--features meridian-relay/zenoh` is load-bearing, not an optimisation.
|
|
# `MERIDIAN_BUS=zenoh|shadow` is a HARD startup error in a relay built without
|
|
# it — see `bus_backend_from_str` in crates/meridian-relay/src/config.rs, whose
|
|
# `#[cfg(not(feature = "zenoh"))]` arm answers ConfigError::InvalidValue rather
|
|
# than falling back to Redis. Shipping a deploy artifact that names a variable
|
|
# this binary cannot read is a guaranteed crash loop, so the image carries the
|
|
# adapter even while `MERIDIAN_BUS` defaults to `redis`.
|
|
#
|
|
# Package-qualified because three packages are selected here; a bare
|
|
# `--features zenoh` does not name which one it belongs to.
|
|
RUN cargo build --release --locked -p meridian-relay --bin meridian-relay \
|
|
-p meridian-admin --bin meridian-admin \
|
|
-p meridian-pair-relay --bin meridian-pair-relay \
|
|
--features meridian-relay/zenoh
|
|
|
|
# Derive the normal release binaries from the same optimized ELF files as the
|
|
# debug image so the two variants cannot drift at code-generation time.
|
|
FROM builder AS stripped-binaries
|
|
RUN strip target/release/meridian-relay \
|
|
&& strip target/release/meridian-admin \
|
|
&& strip target/release/meridian-pair-relay
|
|
|
|
# ─── Stage 4: web bundle (pnpm + vite) ──────────────────────────────────────
|
|
# Independent of the Rust layers so a CSS change doesn't bust Rust cache and
|
|
# vice versa.
|
|
FROM node:${NODE_VERSION}-${DEBIAN_VERSION}-slim AS web-builder
|
|
WORKDIR /build
|
|
# Trust an optional corporate-proxy CA so corepack + pnpm can fetch over an
|
|
# intercepting TLS gateway (no-op if EXTRA_CA_CERTS is unset).
|
|
ARG EXTRA_CA_CERTS
|
|
COPY --chmod=0644 ${EXTRA_CA_CERTS:-Dockerfile} /tmp/extra-ca/src
|
|
RUN if [ -n "${EXTRA_CA_CERTS}" ]; then \
|
|
apt-get update && apt-get install -y --no-install-recommends ca-certificates \
|
|
&& cp /tmp/extra-ca/src /usr/local/share/ca-certificates/extra-proxy-ca.crt \
|
|
&& update-ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*; \
|
|
fi
|
|
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
|
|
# Point npm + corepack at an optional mirror (no-op when NPM_REGISTRY is unset).
|
|
# corepack reads COREPACK_NPM_REGISTRY to fetch the pinned pnpm; pnpm/npm read
|
|
# the .npmrc registry for dependency installs.
|
|
ARG NPM_REGISTRY
|
|
ENV COREPACK_NPM_REGISTRY=${NPM_REGISTRY}
|
|
# When using a mirror, disable corepack's npmjs signature check: the mirror
|
|
# republishes tarballs without the public registry's provenance signatures, so
|
|
# strict verification fails ("No compatible signature found"). Only relaxed on
|
|
# the mirror path — public builds (NPM_REGISTRY unset) keep strict verification.
|
|
RUN if [ -n "${NPM_REGISTRY}" ]; then \
|
|
echo "registry=${NPM_REGISTRY}" > /build/.npmrc \
|
|
&& echo "COREPACK_INTEGRITY_KEYS=0" >> /etc/environment; \
|
|
fi
|
|
ENV COREPACK_INTEGRITY_KEYS=${NPM_REGISTRY:+0}
|
|
RUN corepack enable
|
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
COPY patches/ patches/
|
|
COPY REMAPPING/meridian-web/package.json REMAPPING/meridian-web/
|
|
COPY REMAPPING/meridian-admin-web/package.json REMAPPING/meridian-admin-web/
|
|
RUN pnpm install --frozen-lockfile --filter meridian-web --filter meridian-admin-web
|
|
COPY REMAPPING/meridian-web/ REMAPPING/meridian-web/
|
|
COPY REMAPPING/meridian-admin-web/ REMAPPING/meridian-admin-web/
|
|
# REMAPPING/meridian-web/ and REMAPPING/meridian-admin-web/ map `@brand/*` onto `../meridian-desktop/src/shared/ui/*` (see
|
|
# REMAPPING/meridian-web/vite.config.ts and REMAPPING/meridian-web/tsconfig.json), so this one shared module is a
|
|
# build input for the relay image even though the desktop app is not. Without
|
|
# it `tsc` fails with TS2307 on `@brand/meridian-globe/mountGlobe`. The module
|
|
# imports only itself and react, so this is its whole dependency closure.
|
|
# `.dockerignore` re-includes the same path; both are required.
|
|
COPY REMAPPING/meridian-desktop/src/shared/ui/meridian-globe/ REMAPPING/meridian-desktop/src/shared/ui/meridian-globe/
|
|
RUN pnpm -C REMAPPING/meridian-web build && pnpm -C REMAPPING/meridian-admin-web build
|
|
|
|
# ─── Stage 5: shared runtime ────────────────────────────────────────────────
|
|
FROM debian:${DEBIAN_VERSION}-slim AS runtime-base
|
|
|
|
# OCI annotations: required for GHCR to auto-link the image to this repo and
|
|
# inherit its visibility. org.opencontainers.image.source is the load-bearing
|
|
# one — without it GHCR keeps the image private even when the repo is public.
|
|
LABEL org.opencontainers.image.title="Meridian" \
|
|
org.opencontainers.image.description="WebSocket relay server for the Meridian communications platform" \
|
|
org.opencontainers.image.source="https://git.office.ilab.zone/RAID/R2D2-MERIDIAN" \
|
|
org.opencontainers.image.url="https://git.office.ilab.zone/RAID/R2D2-MERIDIAN" \
|
|
org.opencontainers.image.documentation="https://git.office.ilab.zone/RAID/R2D2-MERIDIAN#readme" \
|
|
org.opencontainers.image.licenses="Apache-2.0"
|
|
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
curl \
|
|
git \
|
|
openssl \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& groupadd --system --gid 1000 meridian \
|
|
&& useradd --system --uid 1000 --gid 1000 --home-dir /var/lib/meridian \
|
|
--create-home --shell /usr/sbin/nologin meridian
|
|
|
|
COPY --from=web-builder /build/REMAPPING/meridian-web/dist /srv/meridian/web
|
|
COPY --from=web-builder /build/REMAPPING/meridian-admin-web/dist /srv/meridian/admin-web
|
|
|
|
# The invite landing page is always served from the bundled web UI. Repository
|
|
# browser routes require the separate MERIDIAN_SERVE_GIT_WEB_GUI=true opt-in. The
|
|
# admin bundle is inert until MERIDIAN_ADMIN_HOST is configured.
|
|
ENV MERIDIAN_WEB_DIR=/srv/meridian/web \
|
|
MERIDIAN_ADMIN_WEB_DIR=/srv/meridian/admin-web
|
|
|
|
# 3000: app (WS + REST) · 8080: /_liveness, /_readiness · 9102: /metrics
|
|
EXPOSE 3000 8080 9102
|
|
|
|
# deploy/compose mounts a volume here; pre-created so it inherits meridian:meridian.
|
|
RUN mkdir -p /data/git && chown meridian:meridian /data/git
|
|
|
|
USER meridian:meridian
|
|
WORKDIR /var/lib/meridian
|
|
|
|
ENTRYPOINT ["/usr/local/bin/meridian-relay"]
|
|
|
|
# Optimized binaries with line-table debug information for native profiling.
|
|
# Published under debug-* tags; runtime behavior otherwise matches the normal
|
|
# image exactly.
|
|
FROM runtime-base AS runtime-debug
|
|
COPY --from=builder /build/target/release/meridian-relay /usr/local/bin/meridian-relay
|
|
COPY --from=builder /build/target/release/meridian-admin /usr/local/bin/meridian-admin
|
|
COPY --from=builder /build/target/release/meridian-pair-relay /usr/local/bin/meridian-pair-relay
|
|
|
|
# Keep the stripped runtime as the final/default Dockerfile target so existing
|
|
# `docker build .` callers and release tags retain their current behavior.
|
|
FROM runtime-base AS runtime
|
|
COPY --from=stripped-binaries /build/target/release/meridian-relay /usr/local/bin/meridian-relay
|
|
COPY --from=stripped-binaries /build/target/release/meridian-admin /usr/local/bin/meridian-admin
|
|
COPY --from=stripped-binaries /build/target/release/meridian-pair-relay /usr/local/bin/meridian-pair-relay
|