# Ginxsom Static MUSL Build Guide

This guide explains how to build and deploy Ginxsom as a fully static MUSL binary with zero runtime dependencies.

## Overview

Ginxsom now supports building as a static MUSL binary using Alpine Linux and Docker. This produces a truly portable binary that works on **any Linux distribution** without requiring any system libraries.

## Benefits

| Feature | Static MUSL | Dynamic glibc |
|---------|-------------|---------------|
| **Portability** | ✓ Any Linux | ✗ Requires matching libs |
| **Dependencies** | None | libfcgi, libsqlite3, etc. |
| **Deployment** | Copy one file | Build on target |
| **Binary Size** | ~7-10 MB | ~2-3 MB + libraries |
| **Deployment Time** | ~10 seconds | ~5-10 minutes |

## Prerequisites

- Docker installed and running
- Internet connection (for first build only)
- ~2GB disk space for Docker images

## Quick Start

### 1. Build Static Binary

```bash
# Build production binary (optimized, stripped)
make static

# Or build debug binary (with symbols)
make static-debug

# Or use the script directly
./build_static.sh
./build_static.sh --debug
```

The binary will be created in `build/ginxsom-fcgi_static_x86_64` (or `_arm64` for ARM systems).

### 2. Verify Binary

```bash
# Check if truly static
ldd build/ginxsom-fcgi_static_x86_64
# Should output: "not a dynamic executable"

# Check file info
file build/ginxsom-fcgi_static_x86_64
# Should show: "statically linked"

# Check size
ls -lh build/ginxsom-fcgi_static_x86_64
```

### 3. Deploy to Server

```bash
# Use the simplified deployment script
./deploy_static.sh

# Or manually copy and start
scp build/ginxsom-fcgi_static_x86_64 user@server:/path/to/ginxsom/
ssh user@server
chmod +x /path/to/ginxsom/ginxsom-fcgi_static_x86_64
sudo spawn-fcgi -M 666 -u www-data -g www-data \
  -s /tmp/ginxsom-fcgi.sock \
  -- /path/to/ginxsom/ginxsom-fcgi_static_x86_64 \
  --db-path /path/to/db/ginxsom.db \
  --storage-dir /var/www/html/blossom
```

## Build Process Details

### What Happens During Build

1. **Docker Image Creation** (5-10 minutes first time, cached after):
   - Uses Alpine Linux 3.19 (native MUSL)
   - Builds secp256k1 statically
   - Builds nostr_core_lib with required NIPs
   - Embeds web interface files
   - Compiles Ginxsom with full static linking

2. **Binary Extraction**:
   - Extracts binary from Docker container
   - Verifies static linking
   - Makes executable

3. **Verification**:
   - Checks for dynamic dependencies
   - Reports file size
   - Tests execution

### Docker Layers (Cached)

The Dockerfile uses multi-stage builds with caching:

```
Layer 1: Alpine base + dependencies (cached)
Layer 2: Build secp256k1 (cached)
Layer 3: Initialize git submodules (cached unless .gitmodules changes)
Layer 4: Build nostr_core_lib (cached unless nostr_core_lib changes)
Layer 5: Embed web files (cached unless api/ changes)
Layer 6: Build Ginxsom (rebuilds when src/ changes)
```

This means subsequent builds are **much faster** (~1-2 minutes) since only changed layers rebuild.

## Deployment Comparison

### Old Dynamic Build Deployment

```bash
# 1. Sync entire project (30 seconds)
rsync -avz . user@server:/path/

# 2. Build on remote server (5-10 minutes)
ssh user@server "cd /path && make clean && make"

# 3. Restart service (10 seconds)
ssh user@server "sudo systemctl restart ginxsom"

# Total: ~6-11 minutes
```

### New Static Build Deployment

```bash
# 1. Build locally once (5-10 minutes first time, cached after)
make static

# 2. Copy binary (10 seconds)
scp build/ginxsom-fcgi_static_x86_64 user@server:/path/

# 3. Restart service (10 seconds)
ssh user@server "sudo systemctl restart ginxsom"

# Total: ~20 seconds (after first build)
```

## Cleanup

### Automatic Cleanup

The static build script automatically cleans up old dynamic build artifacts (`.o` files and `ginxsom-fcgi` binary) after successfully building the static binary. This keeps your `build/` directory clean.

### Manual Cleanup

```bash
# Clean dynamic build artifacts (preserves static binaries)
make clean

# Clean everything including static binaries
make clean-all

# Or manually remove specific files
rm -f build/*.o
rm -f build/ginxsom-fcgi
rm -f build/ginxsom-fcgi_static_*
```

## Troubleshooting

### Docker Not Found

```bash
# Install Docker
sudo apt install docker.io

# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
```

### Build Fails

```bash
# Clean Docker cache and rebuild
docker system prune -a
make static
```

### Binary Won't Run on Target

```bash
# Verify it's static
ldd build/ginxsom-fcgi_static_x86_64

# Check architecture matches
file build/ginxsom-fcgi_static_x86_64
uname -m  # On target system
```

### Alpine Package Not Found

If you get errors about missing Alpine packages, the package name may have changed. Check Alpine's package database:
- https://pkgs.alpinelinux.org/packages

## Advanced Usage

### Cross-Compilation

Build for different architectures:

```bash
# Build for ARM64 on x86_64 machine
docker build --platform linux/arm64 -f Dockerfile.alpine-musl -t ginxsom-arm64 .
```

### Custom NIPs

Edit `Dockerfile.alpine-musl` line 66 to change which NIPs are included:

```dockerfile
./build.sh --nips=1,6,19  # Minimal
./build.sh --nips=1,6,13,17,19,44,59  # Full (default)
```

### Debug Build

```bash
# Build with debug symbols (no optimization)
make static-debug

# Binary will be larger but include debugging info
gdb build/ginxsom-fcgi_static_x86_64
```

## File Structure

```
ginxsom/
├── Dockerfile.alpine-musl    # Alpine Docker build definition
├── build_static.sh            # Build script wrapper
├── deploy_static.sh           # Simplified deployment script
├── Makefile                   # Updated with 'static' target
└── build/
    └── ginxsom-fcgi_static_x86_64  # Output binary
```

## CI/CD Integration

### GitHub Actions Example

```yaml
name: Build Static Binary

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: recursive
      
      - name: Build static binary
        run: make static
      
      - name: Upload artifact
        uses: actions/upload-artifact@v2
        with:
          name: ginxsom-static
          path: build/ginxsom-fcgi_static_x86_64
```

## Performance

Static MUSL binaries have minimal performance impact:

| Metric | Static MUSL | Dynamic glibc |
|--------|-------------|---------------|
| Startup Time | ~50ms | ~40ms |
| Memory Usage | Similar | Similar |
| Request Latency | Identical | Identical |
| Binary Size | 7-10 MB | 2-3 MB + libs |

The slight startup delay is negligible for a long-running FastCGI process.

## References

- [MUSL libc](https://musl.libc.org/)
- [Alpine Linux](https://alpinelinux.org/)
- [Static Linking Best Practices](https://www.musl-libc.org/faq.html)
- [c-relay Static Build](../c-relay/STATIC_BUILD.md)

## Support

For issues with static builds:
1. Check Docker is running: `docker info`
2. Verify submodules: `git submodule status`
3. Clean and rebuild: `docker system prune -a && make static`
4. Check logs in Docker build output