From dbc7980e97b98c8fd6fbaa2198edaa91724620ac Mon Sep 17 00:00:00 2001 From: Brandon Schneider Date: Tue, 19 May 2026 14:14:17 +0000 Subject: [PATCH] flake.nix: add rs-surface binary + OCI image outputs; fix Dockerfile base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit flake.nix: - rsSurface: pkgs.rustPlatform.buildRustPackage for 4-Infrastructure/infra/embedded_surface/rs-surface — uses cargoLock.lockFile (no cargoHash/cargoSha256 for Cargo.lock-based fetching). - rsSurfaceImage: pkgs.dockerTools.buildLayeredImage — pure Nix OCI tarball, no Alpine, no musl. Closure: rs-surface binary + cacert + coreutils. Healthcheck, ExposedPorts, Env, Labels all set. - packages.${system}.rs-surface and .rs-surface-image registered. Build: nix build .#rs-surface # binary only nix build .#rs-surface-image # OCI tarball → docker load < result Dockerfile: - Switch fallback runtime from alpine:3.20 → debian:bookworm-slim (glibc, no musl compat issues). Builder stage debian:bookworm-slim with libssl-dev. - Added comment directing users to `nix build .#rs-surface-image` as the canonical build path on NixOS. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../infra/embedded_surface/Dockerfile | 35 +++++--- flake.nix | 79 +++++++++++++++++++ 2 files changed, 104 insertions(+), 10 deletions(-) diff --git a/4-Infrastructure/infra/embedded_surface/Dockerfile b/4-Infrastructure/infra/embedded_surface/Dockerfile index ae5a0e21..57529f0d 100644 --- a/4-Infrastructure/infra/embedded_surface/Dockerfile +++ b/4-Infrastructure/infra/embedded_surface/Dockerfile @@ -1,18 +1,29 @@ +# Dockerfile — fallback / quick-iteration path for rs-surface. +# +# The CANONICAL build is the Nix flake target: +# nix build .#rs-surface-image # produces a deterministic OCI tarball +# docker load < result +# +# This Dockerfile exists for environments where `nix build` is unavailable +# (e.g. raw Docker CI, RackNerd VPS before Nix is set up). It uses +# debian:bookworm-slim for glibc compatibility with the standard Rust toolchain. +# # ── Stage 1: build the Rust surface binary ───────────────────────────────── -FROM rust:1.82-alpine AS builder +FROM rust:1.82-bookworm AS builder -RUN apk add --no-cache musl-dev openssl-dev openssl-libs-static pkgconfig +RUN apt-get update -qq && apt-get install -y --no-install-recommends \ + pkg-config libssl-dev && rm -rf /var/lib/apt/lists/* WORKDIR /build COPY rs-surface/Cargo.toml rs-surface/Cargo.lock ./ -# Pre-fetch deps (layer cache) -RUN mkdir src && echo 'fn main(){}' > src/main.rs && cargo build --release 2>/dev/null || true +RUN mkdir src && echo 'fn main(){}' > src/main.rs \ + && cargo build --release 2>/dev/null; rm -f target/release/rs-surface COPY rs-surface/src ./src RUN touch src/main.rs && cargo build --release -# ── Stage 2: minimal runtime image ───────────────────────────────────────── -FROM alpine:3.20 +# ── Stage 2: minimal glibc runtime ───────────────────────────────────────── +FROM debian:bookworm-slim ENV RS_SURFACE_PROFILE=/etc/rs-surface/node.json ENV RS_SURFACE_STATE=/var/lib/rs-surface @@ -20,14 +31,18 @@ ENV RS_SURFACE_MOUNT=/mnt/topological-storage ENV RS_SURFACE_HOST=0.0.0.0 ENV RS_SURFACE_PORT=8080 -RUN adduser -D -H -u 988 surface \ - && mkdir -p /opt/rs-surface /etc/rs-surface /var/lib/rs-surface /var/log/rs-surface /run/rs-surface /mnt/topological-storage \ - && chown -R surface:surface /var/lib/rs-surface /var/log/rs-surface /run/rs-surface /mnt/topological-storage +RUN apt-get update -qq && apt-get install -y --no-install-recommends \ + ca-certificates wget && rm -rf /var/lib/apt/lists/* \ + && adduser --system --no-create-home --uid 988 surface \ + && mkdir -p /opt/rs-surface /etc/rs-surface /var/lib/rs-surface \ + /var/log/rs-surface /run/rs-surface /mnt/topological-storage \ + && chown -R surface:surface /var/lib/rs-surface /var/log/rs-surface \ + /run/rs-surface /mnt/topological-storage COPY --from=builder /build/target/release/rs-surface /opt/rs-surface/rs-surface COPY profiles/racknerd-surface.json /etc/rs-surface/node.json -# Keep the Python server alongside for fallback / debugging +# Python server kept alongside as a debugging/fallback reference COPY server.py /opt/rs-surface/server.py USER surface diff --git a/flake.nix b/flake.nix index ef9a8510..444150c9 100644 --- a/flake.nix +++ b/flake.nix @@ -188,6 +188,81 @@ mkdir -p -m 1777 $out/tmp ''; + # ── rs-surface binary ─────────────────────────────────────────────────── + # Rust port of 4-Infrastructure/infra/embedded_surface/server.py. + # Build the binary hermetically via rustPlatform.buildRustPackage. + # + # cargoHash: run `nix build .#rs-surface 2>&1 | grep "got:"` to get the + # real hash after any Cargo.lock change, then replace lib.fakeHash below. + rsSurface = pkgs.rustPlatform.buildRustPackage { + pname = "rs-surface"; + version = "0.1.0"; + + src = pkgs.lib.cleanSource ./4-Infrastructure/infra/embedded_surface/rs-surface; + + cargoLock.lockFile = ./4-Infrastructure/infra/embedded_surface/rs-surface/Cargo.lock; + + nativeBuildInputs = [ pkgs.pkg-config ]; + buildInputs = [ pkgs.openssl ]; + + meta = { + description = "Embedded node surface daemon (Rust)"; + license = pkgs.lib.licenses.mit; + mainProgram = "rs-surface"; + }; + }; + + # ── Minimal OCI image for the embedded node surface ──────────────────── + # Built entirely from the Nix store — no Dockerfile, no Alpine, no musl. + # The closure contains only the binary + its glibc runtime deps. + # + # Build: nix build .#rs-surface-image + # Load: docker load < result + # Run: docker run --rm -p 8080:8080 -v /etc/rs-surface:/etc/rs-surface rs-surface:latest + rsSurfaceImage = pkgs.dockerTools.buildLayeredImage { + name = "rs-surface"; + tag = "latest"; + + contents = [ + rsSurface + pkgs.cacert # TLS roots (for any outbound HTTPS) + pkgs.coreutils # minimal shell utilities for healthcheck + ]; + + # /etc/rs-surface/node.json is volume-mounted at runtime; create the + # directory so the mount point exists in the image. + extraCommands = '' + mkdir -p etc/rs-surface var/lib/rs-surface mnt/topological-storage + ''; + + config = { + Entrypoint = [ "${rsSurface}/bin/rs-surface" ]; + Env = [ + "RS_SURFACE_PROFILE=/etc/rs-surface/node.json" + "RS_SURFACE_STATE=/var/lib/rs-surface" + "RS_SURFACE_MOUNT=/mnt/topological-storage" + "RS_SURFACE_HOST=0.0.0.0" + "RS_SURFACE_PORT=8080" + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + ]; + ExposedPorts = { "8080/tcp" = {}; }; + Healthcheck = { + Test = [ "CMD" "${pkgs.coreutils}/bin/sh" "-c" + "wget -qO- http://127.0.0.1:8080/health || exit 1" ]; + Interval = 10000000000; # 10 s in nanoseconds + Timeout = 3000000000; # 3 s + StartPeriod = 3000000000; # 3 s + Retries = 3; + }; + Labels = { + "org.opencontainers.image.description" = "rs-surface embedded node daemon"; + "org.opencontainers.image.source" = "4-Infrastructure/infra/embedded_surface/rs-surface"; + }; + }; + + maxLayers = 10; + }; + in { # ── Layered OCI image for the devcontainer ────────────────────────────── packages.${system}.devcontainer = pkgs.dockerTools.buildLayeredImage { @@ -226,6 +301,10 @@ maxLayers = 120; }; + # rs-surface binary and OCI image + packages.${system}.rs-surface = rsSurface; + packages.${system}.rs-surface-image = rsSurfaceImage; + # Convenience alias: `nix build .#devcontainer` defaultPackage.${system} = self.packages.${system}.devcontainer;