# MUSL Static Binary Build Guide

## Overview

This guide explains how to build truly portable MUSL-based static binaries of c-relay using Alpine Linux Docker containers. These binaries have **zero runtime dependencies** and work on any Linux distribution.

## Why MUSL?

### MUSL vs glibc Static Binaries

**MUSL Advantages:**
- **Truly Static**: No hidden dependencies on system libraries
- **Smaller Size**: ~7.6MB vs ~12MB+ for glibc static builds
- **Better Portability**: Works on ANY Linux distribution without modification
- **Cleaner Linking**: No glibc-specific extensions or fortified functions
- **Simpler Deployment**: Single binary, no library compatibility issues

**glibc Limitations:**
- Static builds still require dynamic loading for NSS (Name Service Switch)
- Fortified functions (`__*_chk`) don't exist in MUSL
- Larger binary size due to glibc's complexity
- May have compatibility issues across different glibc versions

## Build Process

### Prerequisites

- Docker installed and running
- Sufficient disk space (~2GB for Docker layers)
- Internet connection (for downloading dependencies)

### Quick Start

```bash
# Build MUSL static binary
./build_static.sh

# The binary will be created at:
# build/c_relay_static_musl_x86_64  (on x86_64)
# build/c_relay_static_musl_arm64   (on ARM64)
```

### What Happens During Build

1. **Alpine Linux Base**: Uses Alpine 3.19 with native MUSL support
2. **Static Dependencies**: Builds all dependencies with static linking:
   - libsecp256k1 (Bitcoin cryptography)
   - libwebsockets (WebSocket server)
   - OpenSSL (TLS/crypto)
   - SQLite (database)
   - curl (HTTP client)
   - zlib (compression)

3. **nostr_core_lib**: Builds with MUSL-compatible flags:
   - Disables glibc fortification (`-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0`)
   - Includes required NIPs: 001, 006, 013, 017, 019, 044, 059
   - Produces static library (~316KB)

4. **c-relay Compilation**: Links everything statically:
   - All source files compiled with `-static` flag
   - Fortification disabled to avoid `__*_chk` symbols
   - Results in ~7.6MB stripped binary

5. **Verification**: Confirms binary is truly static:
   - `ldd` shows "not a dynamic executable"
   - `file` shows "statically linked"
   - Binary executes successfully

## Technical Details

### Dockerfile Structure

The build uses a multi-stage Dockerfile (`Dockerfile.alpine-musl`):

```dockerfile
# Stage 1: Builder (Alpine Linux)
FROM alpine:3.19 AS builder
- Install build tools and static libraries
- Build dependencies from source
- Compile nostr_core_lib with MUSL flags
- Compile c-relay with full static linking
- Strip binary to reduce size

# Stage 2: Output (scratch)
FROM scratch AS output
- Contains only the final binary
```

### Key Compilation Flags

**For nostr_core_lib:**
```bash
CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -fPIC -O2"
```

**For c-relay:**
```bash
gcc -static -O2 -Wall -Wextra -std=c99 \
    -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
    [source files] \
    -lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \
    -lcurl -lz -lpthread -lm -ldl
```

### Fortification Issue

**Problem**: GCC's `-O2` optimization enables fortification by default, replacing standard functions with `__*_chk` variants (e.g., `__snprintf_chk`, `__fprintf_chk`). These are glibc-specific and don't exist in MUSL.

**Solution**: Explicitly disable fortification with:
- `-U_FORTIFY_SOURCE` (undefine any existing definition)
- `-D_FORTIFY_SOURCE=0` (set to 0)

This must be applied to **both** nostr_core_lib and c-relay compilation.

### NIP Dependencies

The build includes these NIPs in nostr_core_lib:
- **NIP-001**: Basic protocol (event creation, signing)
- **NIP-006**: Key derivation from mnemonic
- **NIP-013**: Proof of Work validation
- **NIP-017**: Private Direct Messages
- **NIP-019**: Bech32 encoding (nsec/npub)
- **NIP-044**: Modern encryption
- **NIP-059**: Gift Wrap (required by NIP-017)

## Verification

### Check Binary Type

```bash
# Should show "statically linked"
file build/c_relay_static_musl_x86_64

# Should show "not a dynamic executable"
ldd build/c_relay_static_musl_x86_64

# Check size (should be ~7.6MB)
ls -lh build/c_relay_static_musl_x86_64
```

### Test Execution

```bash
# Show help
./build/c_relay_static_musl_x86_64 --help

# Show version
./build/c_relay_static_musl_x86_64 --version

# Run relay
./build/c_relay_static_musl_x86_64 --port 8888
```

### Cross-Distribution Testing

Test the binary on different distributions to verify portability:

```bash
# Alpine Linux
docker run --rm -v $(pwd)/build:/app alpine:latest /app/c_relay_static_musl_x86_64 --version

# Ubuntu
docker run --rm -v $(pwd)/build:/app ubuntu:latest /app/c_relay_static_musl_x86_64 --version

# Debian
docker run --rm -v $(pwd)/build:/app debian:latest /app/c_relay_static_musl_x86_64 --version

# CentOS
docker run --rm -v $(pwd)/build:/app centos:latest /app/c_relay_static_musl_x86_64 --version
```

## Troubleshooting

### Docker Permission Denied

**Problem**: `permission denied while trying to connect to the Docker daemon socket`

**Solution**: Add user to docker group:
```bash
sudo usermod -aG docker $USER
newgrp docker  # Or logout and login again
```

### Build Fails with Fortification Errors

**Problem**: `undefined reference to '__snprintf_chk'` or `'__fprintf_chk'`

**Solution**: Ensure fortification is disabled in both:
1. nostr_core_lib build.sh (line 534)
2. c-relay compilation flags in Dockerfile

### Binary Won't Execute

**Problem**: Binary fails to run on target system

**Checks**:
1. Verify it's truly static: `ldd binary` should show "not a dynamic executable"
2. Check architecture matches: `file binary` should show correct arch
3. Ensure execute permissions: `chmod +x binary`

### Missing NIP Functions

**Problem**: `undefined reference to 'nostr_nip*'` during linking

**Solution**: Add missing NIPs to the build command:
```bash
./build.sh --nips=1,6,13,17,19,44,59
```

## Deployment

### Single Binary Deployment

```bash
# Copy binary to server
scp build/c_relay_static_musl_x86_64 user@server:/opt/c-relay/

# Run on server (no dependencies needed!)
ssh user@server
cd /opt/c-relay
./c_relay_static_musl_x86_64 --port 8888
```

### SystemD Service

```ini
[Unit]
Description=C-Relay Nostr Relay (MUSL Static)
After=network.target

[Service]
Type=simple
User=c-relay
WorkingDirectory=/opt/c-relay
ExecStart=/opt/c-relay/c_relay_static_musl_x86_64
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

## Performance Comparison

| Metric | MUSL Static | glibc Static | glibc Dynamic |
|--------|-------------|--------------|---------------|
| Binary Size | 7.6 MB | 12+ MB | 2-3 MB |
| Startup Time | ~50ms | ~60ms | ~40ms |
| Memory Usage | Similar | Similar | Similar |
| Portability | ✓ Any Linux | ⚠ glibc only | ✗ Requires libs |
| Dependencies | None | NSS libs | Many libs |

## Best Practices

1. **Always verify** the binary is truly static before deployment
2. **Test on multiple distributions** to ensure portability
3. **Keep Docker images updated** for security patches
4. **Document the build date** and commit hash for reproducibility
5. **Store binaries** with architecture in filename (e.g., `_x86_64`, `_arm64`)

## References

- [MUSL libc](https://musl.libc.org/)
- [Alpine Linux](https://alpinelinux.org/)
- [Static Linking Best Practices](https://www.musl-libc.org/faq.html)
- [GCC Fortification](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html)

## Changelog

### 2025-10-11
- Initial MUSL build system implementation
- Alpine Docker-based build process
- Fortification fix for nostr_core_lib
- Complete NIP dependency resolution
- Documentation created