# Alpine-based MUSL static binary builder for Didactyl # 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 (with retry logic for transient mirror/network failures) RUN set -eux; \ printf '%s\n' \ 'https://dl-cdn.alpinelinux.org/alpine/v3.19/main' \ 'https://dl-cdn.alpinelinux.org/alpine/v3.19/community' \ > /etc/apk/repositories; \ APK_OK=0; \ for attempt in 1 2 3 4 5; do \ if apk update && 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 \ nghttp2-dev \ nghttp2-static \ c-ares-dev \ c-ares-static \ libpsl-dev \ libpsl-static \ libidn2-dev \ libidn2-static \ libunistring-dev \ libunistring-static \ brotli-dev \ brotli-static \ zstd-dev \ zstd-static \ sqlite-dev \ sqlite-static \ linux-headers \ wget \ bash; then \ APK_OK=1; \ break; \ fi; \ echo "apk dependency install failed (attempt ${attempt}/5), retrying..."; \ sleep $((attempt * 2)); \ done; \ if [ "$APK_OK" -ne 1 ]; then \ echo "ERROR: failed to install Alpine dependencies after 5 attempts"; \ exit 33; \ fi # 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 # Copy nostr_core_lib source files (cached unless nostr_core_lib changes) # NOTE: nostr_core_lib currently compiles request_validator, which includes , # so sqlite-dev/sqlite-static are required in this image even if didactyl does not use sqlite directly. COPY nostr_core_lib /build/nostr_core_lib/ # Build nostr_core_lib with all NIPs # Disable fortification in build.sh to prevent __*_chk symbol issues 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 && \ sed -i 's/CC="aarch64-linux-gnu-gcc"/CC="gcc"/' build.sh && \ rm -f *.o *.a 2>/dev/null || true && \ ./build.sh --nips=all # Copy didactyl source files LAST (only this layer rebuilds on source changes) COPY src/ /build/src/ COPY Makefile /build/Makefile # Build didactyl 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 NOSTR_LIB=$(ls /build/nostr_core_lib/libnostr_core_*.a 2>/dev/null | head -1) && \ if [ -z "$NOSTR_LIB" ]; then echo "ERROR: nostr_core_lib .a not found"; exit 1; fi && \ echo "Using nostr library: $NOSTR_LIB" && \ 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/didactyl_static"; \ echo "Building optimized production binary (symbols stripped)"; \ fi && \ CURL_LIBS="$(pkg-config --static --libs libcurl)" && \ OPENSSL_LIBS="$(pkg-config --static --libs openssl)" && \ gcc -static $CFLAGS -Wall -Wextra -std=c99 \ -D_GNU_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -DMG_TLS=MG_TLS_BUILTIN \ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \ -I. -Isrc -Isrc/tools -Inostr_core_lib -Inostr_core_lib/nostr_core \ -Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \ src/main.c src/config.c src/context.c src/llm.c \ src/nostr_handler.c src/agent.c src/tools/tools_common.c src/tools/tools_schema.c src/tools/tools_dispatch.c \ src/tools/tool_agent.c src/tools/tool_meta.c src/tools/tool_model.c \ src/tools/tool_nostr_query.c src/tools/tool_nostr_my_events.c src/tools/tool_nostr_identity.c src/tools/tool_nostr_social.c \ src/tools/tool_nostr_relay.c src/tools/tool_nostr_dm.c src/tools/tool_admin.c \ src/tools/tool_task.c src/tools/tool_nostr_list.c src/tools/tool_nostr_block.c src/tools/tool_local.c \ src/tools/tool_skill.c src/tools/tool_nostr_post.c src/tools/tool_memory.c src/tools/tool_config.c src/tools/tool_cashu_wallet.c src/tools/tool_blossom.c src/trigger_manager.c \ src/cashu_wallet.c src/nostr_block_list.c src/prompt_template.c src/http_api.c src/setup_wizard.c src/mongoose.c src/debug.c \ src/json_to_markdown.c src/context_roles.c src/context_format.c \ -o /build/didactyl_static \ $NOSTR_LIB \ -lsecp256k1 \ $OPENSSL_LIBS \ $CURL_LIBS \ -lpthread -lm -ldl && \ eval "$STRIP_CMD" # Verify it's truly static RUN echo "=== Binary Information ===" && \ file /build/didactyl_static && \ ls -lh /build/didactyl_static && \ echo "=== Checking for dynamic dependencies ===" && \ (ldd /build/didactyl_static 2>&1 || echo "Binary is static") && \ echo "=== Build complete ===" # Output stage - just the binary FROM scratch AS output COPY --from=builder /build/didactyl_static /didactyl_static