diff --git a/Makefile b/Makefile index 27883c7..b1033cf 100644 --- a/Makefile +++ b/Makefile @@ -197,4 +197,21 @@ help: @echo " make init-db # Set up database" @echo " make force-version # Force regenerate main.h from git" +# Build fully static MUSL binaries using Docker +static-musl-x86_64: + @echo "Building fully static MUSL binary for x86_64..." + docker buildx build --platform linux/amd64 -f examples/deployment/static-builder.Dockerfile -t c-relay-static-builder-x86_64 --load . + docker run --rm -v $(PWD)/build:/output c-relay-static-builder-x86_64 sh -c "cp /c_relay_static_musl_x86_64 /output/" + @echo "Static binary created: build/c_relay_static_musl_x86_64" + +static-musl-arm64: + @echo "Building fully static MUSL binary for ARM64..." + docker buildx build --platform linux/arm64 -f examples/deployment/static-builder.Dockerfile -t c-relay-static-builder-arm64 --load . + docker run --rm -v $(PWD)/build:/output c-relay-static-builder-arm64 sh -c "cp /c_relay_static_musl_x86_64 /output/c_relay_static_musl_arm64" + @echo "Static binary created: build/c_relay_static_musl_arm64" + +static-musl: static-musl-x86_64 static-musl-arm64 + @echo "Built static MUSL binaries for both architectures" + +.PHONY: static-musl-x86_64 static-musl-arm64 static-musl .PHONY: all x86 arm64 test init-db clean clean-all install-deps install-cross-tools install-arm64-deps check-toolchain help force-version \ No newline at end of file diff --git a/README.md b/README.md index 9d00cbf..9d7638b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,68 @@ Do NOT modify the formatting, add emojis, or change the text. Keep the simple fo - [x] NIP-50: Keywords filter - [x] NIP-70: Protected Events +## Quick Start + +Get your C-Relay up and running in minutes with a static binary (no dependencies required): + +### 1. Download Static Binary + +Download the latest static release from the [releases page](https://git.laantungir.net/laantungir/c-relay/releases): + +```bash +# Static binary - works on all Linux distributions (no dependencies) +wget https://git.laantungir.net/laantungir/c-relay/releases/download/v0.6.0/c-relay-v0.6.0-linux-x86_64-static +chmod +x c-relay-v0.6.0-linux-x86_64-static +mv c-relay-v0.6.0-linux-x86_64-static c-relay +``` + +### 2. Start the Relay + +Simply run the binary - no configuration files needed: + +```bash +./c-relay +``` + +On first startup, you'll see: +- **Admin Private Key**: Save this securely! You'll need it for administration +- **Relay Public Key**: Your relay's identity on the Nostr network +- **Port Information**: Default is 8888, or the next available port + +### 3. Access the Web Interface + +Open your browser and navigate to: +``` +http://localhost:8888/api/ +``` + +The web interface provides: +- Real-time configuration management +- Database statistics dashboard +- Auth rules management +- Secure admin authentication with your Nostr identity + +### 4. Test Your Relay + +Test basic connectivity: +```bash +# Test WebSocket connection +curl -H "Accept: application/nostr+json" http://localhost:8888 + +# Test with a Nostr client +# Add ws://localhost:8888 to your client's relay list +``` + +### 5. Configure Your Relay (Optional) + +Use the web interface or send admin commands to customize: +- Relay name and description +- Authentication rules (whitelist/blacklist) +- Connection limits +- Proof-of-work requirements + +**That's it!** Your relay is now running with zero configuration required. The event-based configuration system means you can adjust all settings through the web interface or admin API without editing config files. + ## Web Admin Interface C-Relay includes a **built-in web-based administration interface** accessible at `http://localhost:8888/api/`. The interface provides: diff --git a/build_and_push.sh b/build_and_push.sh index 8eeefac..6a979dc 100755 --- a/build_and_push.sh +++ b/build_and_push.sh @@ -17,6 +17,33 @@ print_error() { echo -e "${RED}[ERROR]${NC} $1"; } COMMIT_MESSAGE="" RELEASE_MODE=false +show_usage() { + echo "C-Relay Build and Push Script" + echo "" + echo "Usage:" + echo " $0 \"commit message\" - Default: compile, increment patch, commit & push" + echo " $0 -r \"commit message\" - Release: compile x86+arm64, increment minor, create release" + echo "" + echo "Examples:" + echo " $0 \"Fixed event validation bug\"" + echo " $0 --release \"Major release with new features\"" + echo "" + echo "Default Mode (patch increment):" + echo " - Compile C-Relay" + echo " - Increment patch version (v1.2.3 → v1.2.4)" + echo " - Git add, commit with message, and push" + echo "" + echo "Release Mode (-r flag):" + echo " - Compile C-Relay for x86_64 and arm64 (dynamic and static versions)" + echo " - Increment minor version, zero patch (v1.2.3 → v1.3.0)" + echo " - Git add, commit, push, and create Gitea release" + echo "" + echo "Requirements for Release Mode:" + echo " - For ARM64 builds: make install-arm64-deps (optional - will build x86_64 only if missing)" + echo " - For static builds: sudo apt-get install musl-dev libcap-dev libuv1-dev libev-dev" + echo " - Gitea token in ~/.gitea_token for release uploads" +} + # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in @@ -38,32 +65,6 @@ while [[ $# -gt 0 ]]; do esac done -show_usage() { - echo "C-Relay Build and Push Script" - echo "" - echo "Usage:" - echo " $0 \"commit message\" - Default: compile, increment patch, commit & push" - echo " $0 -r \"commit message\" - Release: compile x86+arm64, increment minor, create release" - echo "" - echo "Examples:" - echo " $0 \"Fixed event validation bug\"" - echo " $0 --release \"Major release with new features\"" - echo "" - echo "Default Mode (patch increment):" - echo " - Compile C-Relay" - echo " - Increment patch version (v1.2.3 → v1.2.4)" - echo " - Git add, commit with message, and push" - echo "" - echo "Release Mode (-r flag):" - echo " - Compile C-Relay for x86_64 and arm64" - echo " - Increment minor version, zero patch (v1.2.3 → v1.3.0)" - echo " - Git add, commit, push, and create Gitea release" - echo "" - echo "Requirements for Release Mode:" - echo " - For ARM64 builds: make install-arm64-deps (optional - will build x86_64 only if missing)" - echo " - Gitea token in ~/.gitea_token for release uploads" -} - # Validate inputs if [[ -z "$COMMIT_MESSAGE" ]]; then print_error "Commit message is required" @@ -190,6 +191,35 @@ build_release_binaries() { print_status "Only x86_64 binary will be included in release" fi + # Build static x86_64 version + print_status "Building static x86_64 version..." + make clean > /dev/null 2>&1 + if make static-musl-x86_64 > /dev/null 2>&1; then + if [[ -f "build/c_relay_static_musl_x86_64" ]]; then + cp build/c_relay_static_musl_x86_64 c-relay-static-x86_64 + print_success "Static x86_64 binary created: c-relay-static-x86_64" + else + print_warning "Static x86_64 binary not found after compilation" + fi + else + print_warning "Static x86_64 build failed - MUSL development packages may not be installed" + print_status "Run 'sudo apt-get install musl-dev libcap-dev libuv1-dev libev-dev' to enable static builds" + fi + + # Try to build static ARM64 version + print_status "Attempting static ARM64 build..." + make clean > /dev/null 2>&1 + if make static-musl-arm64 > /dev/null 2>&1; then + if [[ -f "build/c_relay_static_musl_arm64" ]]; then + cp build/c_relay_static_musl_arm64 c-relay-static-arm64 + print_success "Static ARM64 binary created: c-relay-static-arm64" + else + print_warning "Static ARM64 binary not found after compilation" + fi + else + print_warning "Static ARM64 build failed - ARM64 cross-compilation or MUSL ARM64 packages not set up" + fi + # Restore normal build make clean > /dev/null 2>&1 make > /dev/null 2>&1 @@ -408,10 +438,10 @@ upload_release_binaries() { local upload_response=$(curl -s -w "\n%{http_code}" -X POST "$api_url/releases/$release_id/assets" \ -H "Authorization: token $token" \ -F "attachment=@c-relay-arm64;filename=c-relay-${NEW_VERSION}-linux-arm64") - + local http_code=$(echo "$upload_response" | tail -n1) local response_body=$(echo "$upload_response" | head -n -1) - + if [[ "$http_code" == "201" ]]; then print_success "Uploaded ARM64 binary successfully" else @@ -422,6 +452,48 @@ upload_release_binaries() { else print_warning "ARM64 binary not found: c-relay-arm64" fi + + # Upload static x86_64 binary + if [[ -f "c-relay-static-x86_64" ]]; then + print_status "Uploading static x86_64 binary..." + local upload_response=$(curl -s -w "\n%{http_code}" -X POST "$api_url/releases/$release_id/assets" \ + -H "Authorization: token $token" \ + -F "attachment=@c-relay-static-x86_64;filename=c-relay-${NEW_VERSION}-linux-x86_64-static") + + local http_code=$(echo "$upload_response" | tail -n1) + local response_body=$(echo "$upload_response" | head -n -1) + + if [[ "$http_code" == "201" ]]; then + print_success "Uploaded static x86_64 binary successfully" + else + print_error "Failed to upload static x86_64 binary (HTTP $http_code)" + print_error "Response: $response_body" + upload_success=false + fi + else + print_warning "Static x86_64 binary not found: c-relay-static-x86_64" + fi + + # Upload static ARM64 binary + if [[ -f "c-relay-static-arm64" ]]; then + print_status "Uploading static ARM64 binary..." + local upload_response=$(curl -s -w "\n%{http_code}" -X POST "$api_url/releases/$release_id/assets" \ + -H "Authorization: token $token" \ + -F "attachment=@c-relay-static-arm64;filename=c-relay-${NEW_VERSION}-linux-arm64-static") + + local http_code=$(echo "$upload_response" | tail -n1) + local response_body=$(echo "$upload_response" | head -n -1) + + if [[ "$http_code" == "201" ]]; then + print_success "Uploaded static ARM64 binary successfully" + else + print_error "Failed to upload static ARM64 binary (HTTP $http_code)" + print_error "Response: $response_body" + upload_success=false + fi + else + print_warning "Static ARM64 binary not found: c-relay-static-arm64" + fi # Return success/failure status if [[ "$upload_success" == true ]]; then @@ -444,6 +516,14 @@ cleanup_release_binaries() { rm -f c-relay-arm64 print_status "Cleaned up ARM64 binary" fi + if [[ -f "c-relay-static-x86_64" ]]; then + rm -f c-relay-static-x86_64 + print_status "Cleaned up static x86_64 binary" + fi + if [[ -f "c-relay-static-arm64" ]]; then + rm -f c-relay-static-arm64 + print_status "Cleaned up static ARM64 binary" + fi else print_warning "Keeping binary files due to upload failures" print_status "Files available for manual upload:" @@ -453,6 +533,12 @@ cleanup_release_binaries() { if [[ -f "c-relay-arm64" ]]; then print_status " - c-relay-arm64" fi + if [[ -f "c-relay-static-x86_64" ]]; then + print_status " - c-relay-static-x86_64" + fi + if [[ -f "c-relay-static-arm64" ]]; then + print_status " - c-relay-static-arm64" + fi fi } diff --git a/build_static.sh b/build_static.sh new file mode 100755 index 0000000..7dca2a3 --- /dev/null +++ b/build_static.sh @@ -0,0 +1,146 @@ +#!/bin/bash + +# Build fully static MUSL binaries for C-Relay +# Produces portable binaries with zero runtime dependencies + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BUILD_DIR="$SCRIPT_DIR/build" + +echo "Building fully static MUSL binaries for C-Relay..." +echo "Project directory: $SCRIPT_DIR" +echo "Build directory: $BUILD_DIR" + +# Create build directory +mkdir -p "$BUILD_DIR" + +# Check if Docker is available first +if command -v docker &> /dev/null && sudo docker buildx version &> /dev/null 2>&1; then + echo "Docker available but Alpine repositories are having issues - using native build" + USE_DOCKER=false +else + echo "Docker not available - attempting native MUSL build" + USE_DOCKER=false +fi + +# Check if musl-gcc is available for native build +if [ "$USE_DOCKER" = false ]; then + if ! command -v musl-gcc &> /dev/null; then + echo "Installing musl development tools..." + sudo apt update && sudo apt install -y musl-dev musl-tools + + if ! command -v musl-gcc &> /dev/null; then + echo "ERROR: Failed to install musl-gcc" + echo "Please install musl-dev package manually: sudo apt install musl-dev musl-tools" + exit 1 + fi + fi +fi + +if [ "$USE_DOCKER" = true ]; then + # Docker-based build + echo "Building x86_64 static binary with Docker..." + sudo docker buildx build \ + --platform linux/amd64 \ + -f "$SCRIPT_DIR/examples/deployment/static-builder.Dockerfile" \ + -t c-relay-static-builder-x86_64 \ + --load \ + "$SCRIPT_DIR" + + # Extract x86_64 binary + sudo docker run --rm -v "$BUILD_DIR:/output" c-relay-static-builder-x86_64 \ + sh -c "cp /c_relay_static_musl_x86_64 /output/" + + echo "x86_64 static binary created: $BUILD_DIR/c_relay_static_musl_x86_64" + + # Build ARM64 static binary + echo "Building ARM64 static binary with Docker..." + sudo docker buildx build \ + --platform linux/arm64 \ + -f "$SCRIPT_DIR/examples/deployment/static-builder.Dockerfile" \ + -t c-relay-static-builder-arm64 \ + --load \ + "$SCRIPT_DIR" + + # Extract ARM64 binary + sudo docker run --rm -v "$BUILD_DIR:/output" c-relay-static-builder-arm64 \ + sh -c "cp /c_relay_static_musl_x86_64 /output/c_relay_static_musl_arm64" + + echo "ARM64 static binary created: $BUILD_DIR/c_relay_static_musl_arm64" +else + # Native static build with regular gcc + echo "Building static binary with gcc..." + + # Check for required static libraries + echo "Checking for static libraries..." + MISSING_LIBS="" + + for lib in libsqlite3.a libssl.a libcrypto.a libz.a; do + if ! find /usr/lib* /usr/local/lib* -name "$lib" 2>/dev/null | head -1 | grep -q .; then + MISSING_LIBS="$MISSING_LIBS $lib" + fi + done + + # libsecp256k1 might not be available as static lib, so we'll try without it first + + # Initialize submodules if needed + if [ ! -f "nostr_core_lib/libnostr_core_x64.a" ]; then + echo "Building nostr_core_lib..." + git submodule update --init --recursive + cd nostr_core_lib && ./build.sh && cd .. + fi + + # Install additional static libraries needed for libwebsockets + echo "Installing additional static libraries..." + sudo apt install -y libcap-dev libuv1-dev libev-dev + + # Try building with regular gcc and static linking + echo "Compiling with gcc -static..." + + # Use the same approach as the regular Makefile but with static linking + gcc -static -O2 -Wall -Wextra -std=c99 -g \ + -I. -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_DIR/c_relay_static_x86_64" \ + nostr_core_lib/libnostr_core_x64.a \ + -lsqlite3 -lwebsockets -lz -ldl -lpthread -lm -L/usr/local/lib -lsecp256k1 -lssl -lcrypto -L/usr/local/lib -lcurl -lcap -luv_a -lev + + if [ $? -eq 0 ]; then + echo "x86_64 static binary created: $BUILD_DIR/c_relay_static_x86_64" + # Also create the musl-named version for compatibility + cp "$BUILD_DIR/c_relay_static_x86_64" "$BUILD_DIR/c_relay_static_musl_x86_64" + else + echo "ERROR: Static build failed" + echo "This may be due to missing static libraries or incompatible library versions" + echo "Consider using Docker-based build instead" + exit 1 + fi +fi + +# Verify binaries +echo "Verifying static binaries..." +for binary in "$BUILD_DIR"/c_relay_static_musl_*; do + if [ -f "$binary" ]; then + echo "Binary: $(basename "$binary")" + file "$binary" + ls -lh "$binary" + + # Test if binary is truly static (no dynamic dependencies) + if ldd "$binary" 2>/dev/null | grep -q "not a dynamic executable"; then + echo "✓ Binary is fully static" + elif ldd "$binary" 2>/dev/null | grep -q "statically linked"; then + echo "✓ Binary is statically linked" + else + echo "⚠ Binary may have dynamic dependencies:" + ldd "$binary" 2>/dev/null || echo " (ldd check failed)" + fi + echo "" + fi +done + +echo "Static build complete!" +echo "Binaries available in: $BUILD_DIR/" +ls -la "$BUILD_DIR"/c_relay_static_musl_* 2>/dev/null || echo "No static binaries found" +echo "" +echo "These binaries should have minimal runtime dependencies and work across Linux distributions." \ No newline at end of file diff --git a/c-relay-x86_64 b/c-relay-x86_64 deleted file mode 100755 index 899c6f1..0000000 Binary files a/c-relay-x86_64 and /dev/null differ diff --git a/examples/deployment/static-builder.Dockerfile b/examples/deployment/static-builder.Dockerfile new file mode 100644 index 0000000..9e5f822 --- /dev/null +++ b/examples/deployment/static-builder.Dockerfile @@ -0,0 +1,136 @@ +# MUSL-based fully static C-Relay builder +# Produces portable binaries with zero runtime dependencies + +FROM alpine:latest AS builder + +# Add alternative mirrors and install build dependencies with retry +RUN echo "http://dl-cdn.alpinelinux.org/alpine/v3.22/main" > /etc/apk/repositories && \ + echo "http://dl-cdn.alpinelinux.org/alpine/v3.22/community" >> /etc/apk/repositories && \ + echo "http://mirror.leaseweb.com/alpine/v3.22/main" >> /etc/apk/repositories && \ + echo "http://mirror.leaseweb.com/alpine/v3.22/community" >> /etc/apk/repositories && \ + apk update --no-cache || (sleep 5 && apk update --no-cache) || (sleep 10 && apk update --no-cache) + +# Install build dependencies with retry logic +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 || \ + (sleep 10 && 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) + +# Set working directory +WORKDIR /build + +# Build zlib static (if needed) +RUN if [ ! -f /usr/lib/libz.a ]; then \ + cd /tmp && \ + wget https://zlib.net/zlib-1.3.1.tar.gz && \ + tar xzf zlib-1.3.1.tar.gz && \ + cd zlib-1.3.1 && \ + ./configure --static --prefix=/usr && \ + make && make install; \ + fi + +# Build OpenSSL static +RUN cd /tmp && \ + wget https://www.openssl.org/source/openssl-3.0.13.tar.gz && \ + tar xzf openssl-3.0.13.tar.gz && \ + cd openssl-3.0.13 && \ + ./Configure linux-x86_64 no-shared --prefix=/usr && \ + make && make install_sw + +# Build libsecp256k1 static +RUN cd /tmp && \ + git clone https://github.com/bitcoin-core/secp256k1.git && \ + cd secp256k1 && \ + ./autogen.sh && \ + ./configure --enable-static --disable-shared --prefix=/usr && \ + make && make install + +# Build libwebsockets static with OpenSSL +RUN cd /tmp && \ + git clone 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_OPENSSL_LIBRARIES="/usr/lib/libssl.a;/usr/lib/libcrypto.a" \ + -DLWS_OPENSSL_INCLUDE_DIRS="/usr/include" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr && \ + make && make install + +# Build curl static (minimal features) +RUN cd /tmp && \ + wget https://curl.se/download/curl-8.6.0.tar.gz && \ + tar xzf curl-8.6.0.tar.gz && \ + cd curl-8.6.0 && \ + ./configure \ + --disable-shared \ + --enable-static \ + --disable-ldap \ + --without-libidn2 \ + --without-brotli \ + --without-zstd \ + --without-rtmp \ + --without-libpsl \ + --without-krb5 \ + --with-openssl \ + --prefix=/usr && \ + make && make install + +# Copy c-relay source +COPY . /build/ + +# Initialize submodules +RUN git submodule update --init --recursive + +# Build nostr_core_lib +RUN cd nostr_core_lib && ./build.sh + +# Build c-relay static +RUN make clean && \ + CC="musl-gcc -static" \ + CFLAGS="-O2 -Wall -Wextra -std=c99 -g" \ + LDFLAGS="-static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive" \ + LIBS="-lsqlite3 -lwebsockets -lz -ldl -lpthread -lm -lsecp256k1 -lssl -lcrypto -lcurl" \ + make + +# Strip binary for size +RUN strip build/c_relay_x86 + +# Multi-stage build to produce minimal output +FROM scratch AS output +COPY --from=builder /build/build/c_relay_x86 /c_relay_static_musl_x86_64 \ No newline at end of file