# Alpine-based MUSL static binary builder for C-Relay # Produces truly portable binaries with zero runtime dependencies ARG DEBUG_BUILD=false FROM alpine:3.19 AS builder # Re-declare build argument in this stage ARG DEBUG_BUILD=false # Install build dependencies RUN apk add --no-cache \ build-base \ musl-dev \ git \ cmake \ pkgconfig \ autoconf \ automake \ libtool \ openssl-dev \ openssl-libs-static \ zlib-dev \ zlib-static \ curl-dev \ curl-static \ sqlite-dev \ sqlite-static \ linux-headers \ wget \ bash # Set working directory WORKDIR /build # Build libsecp256k1 static (cached layer - only rebuilds if Alpine version changes) RUN cd /tmp && \ git clone https://github.com/bitcoin-core/secp256k1.git && \ cd secp256k1 && \ ./autogen.sh && \ ./configure --enable-static --disable-shared --prefix=/usr \ CFLAGS="-fPIC" && \ make -j$(nproc) && \ make install && \ rm -rf /tmp/secp256k1 # Build libwebsockets static with minimal features (cached layer) RUN cd /tmp && \ git clone --depth 1 --branch v4.3.3 https://github.com/warmcat/libwebsockets.git && \ cd libwebsockets && \ mkdir build && cd build && \ cmake .. \ -DLWS_WITH_STATIC=ON \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_SSL=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITHOUT_TEST_SERVER=ON \ -DLWS_WITHOUT_TEST_CLIENT=ON \ -DLWS_WITHOUT_TEST_PING=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_LIBUV=OFF \ -DLWS_WITH_LIBEVENT=OFF \ -DLWS_IPV6=ON \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_C_FLAGS="-fPIC" && \ make -j$(nproc) && \ make install && \ rm -rf /tmp/libwebsockets # Copy only submodule configuration and git directory COPY .gitmodules /build/.gitmodules COPY .git /build/.git # Clean up any stale submodule references (nips directory is not a submodule) RUN git rm --cached nips 2>/dev/null || true # Initialize submodules (cached unless .gitmodules changes) RUN git submodule update --init --recursive # Copy nostr_core_lib source files (cached unless nostr_core_lib changes) COPY nostr_core_lib /build/nostr_core_lib/ # Copy c_utils_lib source files (cached unless c_utils_lib changes) COPY c_utils_lib /build/c_utils_lib/ # Build c_utils_lib with MUSL-compatible flags (cached unless c_utils_lib changes) RUN cd c_utils_lib && \ sed -i 's/CFLAGS = -Wall -Wextra -std=c99 -O2 -g/CFLAGS = -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -O2 -g/' Makefile && \ make clean && \ make # Build nostr_core_lib with required NIPs (cached unless nostr_core_lib changes) # Disable fortification in build.sh to prevent __*_chk symbol issues # NIPs: 001(Basic), 006(Keys), 013(PoW), 017(DMs), 019(Bech32), 044(Encryption), 059(Gift Wrap - required by NIP-17) RUN cd nostr_core_lib && \ chmod +x build.sh && \ sed -i 's/CFLAGS="-Wall -Wextra -std=c99 -fPIC -O2"/CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -fPIC -O2"/' build.sh && \ rm -f *.o *.a 2>/dev/null || true && \ ./build.sh --nips=1,6,13,17,19,44,59 # Copy c-relay source files LAST (only this layer rebuilds on source changes) COPY src/ /build/src/ COPY Makefile /build/Makefile # Build c-relay with full static linking (only rebuilds when src/ changes) # Disable fortification to avoid __*_chk symbols that don't exist in MUSL # Use conditional compilation flags based on DEBUG_BUILD argument RUN if [ "$DEBUG_BUILD" = "true" ]; then \ CFLAGS="-g -O0 -DDEBUG"; \ STRIP_CMD=""; \ echo "Building with DEBUG symbols enabled"; \ else \ CFLAGS="-O2"; \ STRIP_CMD="strip /build/c_relay_static"; \ echo "Building optimized production binary"; \ fi && \ gcc -static $CFLAGS -Wall -Wextra -std=c99 \ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \ -I. -Ic_utils_lib/src -Inostr_core_lib -Inostr_core_lib/nostr_core \ -Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \ src/main.c src/config.c src/dm_admin.c src/request_validator.c \ src/nip009.c src/nip011.c src/nip013.c src/nip040.c src/nip042.c \ src/websockets.c src/subscriptions.c src/api.c src/embedded_web_content.c \ -o /build/c_relay_static \ c_utils_lib/libc_utils.a \ nostr_core_lib/libnostr_core_x64.a \ -lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \ -lcurl -lz -lpthread -lm -ldl && \ eval "$STRIP_CMD" # Verify it's truly static RUN echo "=== Binary Information ===" && \ file /build/c_relay_static && \ ls -lh /build/c_relay_static && \ echo "=== Checking for dynamic dependencies ===" && \ (ldd /build/c_relay_static 2>&1 || echo "Binary is static") && \ echo "=== Build complete ===" # Output stage - just the binary FROM scratch AS output COPY --from=builder /build/c_relay_static /c_relay_static