Compare commits

...

3 Commits

9 changed files with 591 additions and 82 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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
@@ -319,12 +349,18 @@ create_gitea_release() {
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"$NEW_VERSION\", \"name\": \"$NEW_VERSION\", \"body\": \"$COMMIT_MESSAGE\"}")
local upload_result=false
if echo "$response" | grep -q '"id"'; then
print_success "Created release $NEW_VERSION"
upload_release_binaries "$api_url" "$token"
if upload_release_binaries "$api_url" "$token"; then
upload_result=true
fi
elif echo "$response" | grep -q "already exists"; then
print_warning "Release $NEW_VERSION already exists"
upload_release_binaries "$api_url" "$token"
if upload_release_binaries "$api_url" "$token"; then
upload_result=true
fi
else
print_error "Failed to create release $NEW_VERSION"
print_error "Response: $response"
@@ -334,18 +370,29 @@ create_gitea_release() {
local check_response=$(curl -s -H "Authorization: token $token" "$api_url/releases/tags/$NEW_VERSION")
if echo "$check_response" | grep -q '"id"'; then
print_warning "Release exists but creation response was unexpected"
upload_release_binaries "$api_url" "$token"
if upload_release_binaries "$api_url" "$token"; then
upload_result=true
fi
else
print_error "Release does not exist and creation failed"
return 1
fi
fi
# Return based on upload success
if [[ "$upload_result" == true ]]; then
return 0
else
print_error "Binary upload failed"
return 1
fi
}
# Function to upload release binaries
upload_release_binaries() {
local api_url="$1"
local token="$2"
local upload_success=true
# Get release ID with more robust parsing
print_status "Getting release ID for $NEW_VERSION..."
@@ -367,37 +414,131 @@ upload_release_binaries() {
# Upload x86_64 binary
if [[ -f "c-relay-x86_64" ]]; then
print_status "Uploading x86_64 binary..."
if curl -s -X POST "$api_url/releases/$release_id/assets" \
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-x86_64;filename=c-relay-${NEW_VERSION}-linux-x86_64" > /dev/null; then
print_success "Uploaded x86_64 binary"
-F "attachment=@c-relay-x86_64;filename=c-relay-${NEW_VERSION}-linux-x86_64")
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 x86_64 binary successfully"
else
print_warning "Failed to upload x86_64 binary"
print_error "Failed to upload x86_64 binary (HTTP $http_code)"
print_error "Response: $response_body"
upload_success=false
fi
else
print_warning "x86_64 binary not found: c-relay-x86_64"
fi
# Upload ARM64 binary
if [[ -f "c-relay-arm64" ]]; then
print_status "Uploading ARM64 binary..."
if curl -s -X POST "$api_url/releases/$release_id/assets" \
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" > /dev/null; then
print_success "Uploaded ARM64 binary"
-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
print_warning "Failed to upload ARM64 binary"
print_error "Failed to upload ARM64 binary (HTTP $http_code)"
print_error "Response: $response_body"
upload_success=false
fi
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
return 0
else
return 1
fi
}
# Function to clean up release binaries
cleanup_release_binaries() {
if [[ -f "c-relay-x86_64" ]]; then
rm -f c-relay-x86_64
print_status "Cleaned up x86_64 binary"
fi
if [[ -f "c-relay-arm64" ]]; then
rm -f c-relay-arm64
print_status "Cleaned up ARM64 binary"
local force_cleanup="$1" # Optional parameter to force cleanup even on failure
if [[ "$force_cleanup" == "force" ]] || [[ "$upload_success" == true ]]; then
if [[ -f "c-relay-x86_64" ]]; then
rm -f c-relay-x86_64
print_status "Cleaned up x86_64 binary"
fi
if [[ -f "c-relay-arm64" ]]; then
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:"
if [[ -f "c-relay-x86_64" ]]; then
print_status " - c-relay-x86_64"
fi
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
}
@@ -433,14 +574,18 @@ main() {
git_commit_and_push_no_tag
# Create Gitea release with binaries
create_gitea_release
if create_gitea_release; then
print_success "Release $NEW_VERSION completed successfully!"
print_status "Binaries uploaded to Gitea release"
upload_success=true
else
print_error "Release creation or binary upload failed"
upload_success=false
fi
# Cleanup
# Cleanup (only if upload was successful)
cleanup_release_binaries
print_success "Release $NEW_VERSION completed successfully!"
print_status "Binaries uploaded to Gitea release"
else
print_status "=== DEFAULT MODE ==="

146
build_static.sh Executable file
View File

@@ -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."

Binary file not shown.

View File

@@ -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

View File

@@ -19,6 +19,7 @@
#include "../nostr_core_lib/cjson/cJSON.h"
#include "../nostr_core_lib/nostr_core/nostr_core.h"
#include "../nostr_core_lib/nostr_core/nip013.h" // NIP-13: Proof of Work
#include "../nostr_core_lib/nostr_core/nip019.h" // NIP-19: bech32-encoded entities
#include "config.h" // Configuration management system
#include "sql_schema.h" // Embedded database schema
#include "websockets.h" // WebSocket protocol implementation
@@ -1293,8 +1294,8 @@ void print_usage(const char* program_name) {
printf(" -v, --version Show version information\n");
printf(" -p, --port PORT Override relay port (first-time startup only)\n");
printf(" --strict-port Fail if exact port is unavailable (no port increment)\n");
printf(" -a, --admin-pubkey HEX Override admin public key (64-char hex)\n");
printf(" -r, --relay-privkey HEX Override relay private key (64-char hex)\n");
printf(" -a, --admin-pubkey KEY Override admin public key (64-char hex or npub)\n");
printf(" -r, --relay-privkey KEY Override relay private key (64-char hex or nsec)\n");
printf("\n");
printf("Configuration:\n");
printf(" This relay uses event-based configuration stored in the database.\n");
@@ -1375,24 +1376,25 @@ int main(int argc, char* argv[]) {
return 1;
}
// Validate public key format (must be 64 hex characters)
if (strlen(argv[i + 1]) != 64) {
log_error("Invalid admin public key length. Must be exactly 64 hex characters.");
const char* input_key = argv[i + 1];
char decoded_key[65] = {0}; // Buffer for decoded hex key
// Try to decode the input as either hex or npub format
unsigned char pubkey_bytes[32];
if (nostr_decode_npub(input_key, pubkey_bytes) == NOSTR_SUCCESS) {
// Convert bytes back to hex string
char* hex_ptr = decoded_key;
for (int j = 0; j < 32; j++) {
sprintf(hex_ptr, "%02x", pubkey_bytes[j]);
hex_ptr += 2;
}
} else {
log_error("Invalid admin public key format. Must be 64 hex characters or valid npub format.");
print_usage(argv[0]);
return 1;
}
// Validate hex format
for (int j = 0; j < 64; j++) {
char c = argv[i + 1][j];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
log_error("Invalid admin public key format. Must contain only hex characters (0-9, a-f, A-F).");
print_usage(argv[0]);
return 1;
}
}
strncpy(cli_options.admin_pubkey_override, argv[i + 1], sizeof(cli_options.admin_pubkey_override) - 1);
strncpy(cli_options.admin_pubkey_override, decoded_key, sizeof(cli_options.admin_pubkey_override) - 1);
cli_options.admin_pubkey_override[sizeof(cli_options.admin_pubkey_override) - 1] = '\0';
i++; // Skip the key argument
@@ -1404,28 +1406,29 @@ int main(int argc, char* argv[]) {
print_usage(argv[0]);
return 1;
}
// Validate private key format (must be 64 hex characters)
if (strlen(argv[i + 1]) != 64) {
log_error("Invalid relay private key length. Must be exactly 64 hex characters.");
const char* input_key = argv[i + 1];
char decoded_key[65] = {0}; // Buffer for decoded hex key
// Try to decode the input as either hex or nsec format
unsigned char privkey_bytes[32];
if (nostr_decode_nsec(input_key, privkey_bytes) == NOSTR_SUCCESS) {
// Convert bytes back to hex string
char* hex_ptr = decoded_key;
for (int j = 0; j < 32; j++) {
sprintf(hex_ptr, "%02x", privkey_bytes[j]);
hex_ptr += 2;
}
} else {
log_error("Invalid relay private key format. Must be 64 hex characters or valid nsec format.");
print_usage(argv[0]);
return 1;
}
// Validate hex format
for (int j = 0; j < 64; j++) {
char c = argv[i + 1][j];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
log_error("Invalid relay private key format. Must contain only hex characters (0-9, a-f, A-F).");
print_usage(argv[0]);
return 1;
}
}
strncpy(cli_options.relay_privkey_override, argv[i + 1], sizeof(cli_options.relay_privkey_override) - 1);
strncpy(cli_options.relay_privkey_override, decoded_key, sizeof(cli_options.relay_privkey_override) - 1);
cli_options.relay_privkey_override[sizeof(cli_options.relay_privkey_override) - 1] = '\0';
i++; // Skip the key argument
log_info("Relay private key override specified");
} else if (strcmp(argv[i], "--strict-port") == 0) {
// Strict port mode option