# Alpine-based MUSL static binary builder for C-Relay-PG # Produces truly portable binaries with zero runtime dependencies ARG DEBUG_BUILD=false ARG DB_BACKEND=sqlite FROM alpine:3.19 AS builder # Re-declare build arguments in this stage ARG DEBUG_BUILD=false ARG DB_BACKEND=sqlite # 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 \ postgresql-dev \ linux-headers \ wget \ bash \ openssh-client # 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 # Submodules are provided in the local build context and copied explicitly below. # Avoid network/SSH dependency inside Docker image build. # 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 static library for relay logging utilities # (build only debug.c to avoid broken/missing version header surface in submodule revision) RUN cd c_utils_lib && \ mkdir -p build && \ gcc -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -O2 -g \ -Isrc -Iinclude -c src/debug.c -o build/debug.o && \ ar rcs libc_utils.a build/debug.o # 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), 004(Encryption), 006(Keys), 013(PoW), 017(DMs), 019(Bech32), 044(Encryption), 059(Gift Wrap - required by NIP-17) # NIP-04 is required by nostr_signer.c (NIP-46 bunker signer uses NIP-04 encryption) 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,4,6,13,17,19,44,59 # Copy c-relay-pg source files LAST (only this layer rebuilds on source changes) COPY src/ /build/src/ COPY Makefile /build/Makefile # Build c-relay-pg 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 and DB_BACKEND build args RUN if [ "$DEBUG_BUILD" = "true" ]; then \ CFLAGS="-g -O2 -DDEBUG"; \ STRIP_CMD="echo 'Keeping debug symbols'"; \ echo "Building with DEBUG symbols enabled (optimized with -O2)"; \ else \ CFLAGS="-O2"; \ STRIP_CMD="strip /build/c_relay_pg_static"; \ echo "Building optimized production binary (symbols stripped)"; \ fi && \ if [ "$DB_BACKEND" = "postgres" ]; then \ DB_FLAGS="-DDB_BACKEND_POSTGRES -DHAVE_LIBPQ"; \ DB_LIBS="-lpq -lpgcommon -lpgport"; \ echo "Compiling with PostgreSQL backend"; \ else \ DB_FLAGS=""; \ DB_LIBS=""; \ echo "Compiling with SQLite backend"; \ fi && \ gcc -static $CFLAGS $DB_FLAGS -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 -I/usr/include/postgresql \ 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 src/ip_ban.c \ src/caching_inbox_poller.c src/caching_service_launcher.c \ src/db_ops.c src/db_ops_sqlite.c src/db_ops_postgres.c src/thread_pool.c \ -o /build/c_relay_pg_static \ c_utils_lib/libc_utils.a \ nostr_core_lib/libnostr_core_x64.a \ -lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \ -lcurl -lz -lpthread -lm -ldl $DB_LIBS && \ eval "$STRIP_CMD" # Verify it's truly static RUN echo "=== Binary Information ===" && \ file /build/c_relay_pg_static && \ ls -lh /build/c_relay_pg_static && \ echo "=== Checking for dynamic dependencies ===" && \ (ldd /build/c_relay_pg_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_pg_static /c_relay_pg_static