# NIP Static Build Guide

## Overview

This guide explains how to build NIP as a fully static binary for minimal deployment scenarios. Static builds are essential for the "minimal install philosophy" - enabling users to boot a tiny netinst image and build their perfect system from a single ~5MB binary.

## Why Static Linking?

### Benefits

1. **Zero Dependencies**: Binary runs on any Linux system with kernel 4.19+
2. **Minimal Deployment**: Single file, no package manager needed
3. **Predictable Behavior**: No library version conflicts
4. **Portable**: Works across different distributions
5. **Netinst Ready**: Perfect for minimal installation images

### Trade-offs

- **Larger Binary Size**: ~5-10MB vs ~2-3MB dynamic
- **No Shared Libraries**: Can't benefit from system library updates
- **Build Complexity**: Requires careful configuration

## Build Methods

### Method 1: Using build_static.sh (Recommended)

The easiest way to build a static binary:

```bash
cd nip
./build_static.sh
```

This script:
- Detects musl-gcc for optimal builds
- Configures all static linking flags
- Verifies the binary is truly static
- Creates deployment package
- Provides size comparison

**Output:**
- `nip-static` - Fully static binary
- `nip-v0.2.0-weihnachtsmann-static-*.tar.gz` - Deployment package

### Method 2: Manual Build with Musl

For the smallest possible binary (recommended):

```bash
# Install musl-tools (Debian/Ubuntu)
sudo apt install musl-tools

# Or on Arch
sudo pacman -S musl

# Build with musl
nim c \
    --define:static \
    --define:release \
    --define:danger \
    --opt:speed \
    --mm:orc \
    --threads:on \
    --passC:-flto \
    --passL:-flto \
    --passL:-static \
    --passL:-s \
    --gcc.exe:musl-gcc \
    --gcc.linkerexe:musl-gcc \
    --out:nip-static \
    nip.nim
```

**Expected Size:** ~5-7MB

### Method 3: Manual Build with Glibc

For maximum compatibility (larger binary):

```bash
nim c \
    --define:static \
    --define:release \
    --define:danger \
    --opt:speed \
    --mm:orc \
    --threads:on \
    --passC:-flto \
    --passL:-flto \
    --passL:-static \
    --passL:-static-libgcc \
    --passL:-s \
    --out:nip-static \
    nip.nim
```

**Expected Size:** ~8-12MB

## Configuration Details

### Static Linking Flags

The `config.nims` file includes static linking configuration when `-d:static` is defined:

```nim
when defined(static):
  # Core static linking
  switch("passL", "-static")
  switch("passL", "-static-libgcc")

  # Use musl if available
  when defined(linux):
    if fileExists("/usr/lib/x86_64-linux-musl/libc.a"):
      switch("gcc.exe", "musl-gcc")
      switch("gcc.linkerexe", "musl-gcc")

  # Optimization flags
  switch("passL", "-s")                    # Strip symbols
  switch("passC", "-flto")                 # Link-time optimization
  switch("passL", "-flto")
  switch("passC", "-ffunction-sections")   # Section garbage collection
  switch("passC", "-fdata-sections")
  switch("passL", "-Wl,--gc-sections")
```

### Compiler Flags Explained

| Flag | Purpose | Impact |
|------|---------|--------|
| `--define:static` | Enable static build mode | Activates static config |
| `--define:release` | Release optimizations | Faster code |
| `--define:danger` | Disable runtime checks | Smaller, faster |
| `--opt:speed` | Optimize for speed | Better performance |
| `--mm:orc` | Use ORC memory manager | Deterministic GC |
| `--threads:on` | Enable threading | Parallel operations |
| `--passC:-flto` | Link-time optimization (C) | Better code generation |
| `--passL:-flto` | Link-time optimization (linker) | Smaller binary |
| `--passL:-static` | Static linking | No dynamic deps |
| `--passL:-s` | Strip symbols | Smaller binary |
| `--gcc.exe:musl-gcc` | Use musl compiler | Smaller libc |

## Verification

### Check Static Linking

```bash
# Should output: "not a dynamic executable"
ldd nip-static

# Or on some systems: "statically linked"
file nip-static
```

### Check Binary Size

```bash
ls -lh nip-static
# Target: 5-10MB
```

### Test Functionality

```bash
# Basic test (may require root)
./nip-static --version

# Full test
sudo ./nip-static setup
sudo ./nip-static status
```

## Platform-Specific Notes

### Linux (x86_64)

**Recommended:** Use musl-gcc for smallest binaries

```bash
# Debian/Ubuntu
sudo apt install musl-tools

# Arch
sudo pacman -S musl

# Fedora
sudo dnf install musl-gcc
```

### Linux (ARM64)

```bash
# Install cross-compilation tools
sudo apt install gcc-aarch64-linux-gnu musl-tools

# Build for ARM64
nim c \
    --cpu:arm64 \
    --os:linux \
    --define:static \
    --gcc.exe:aarch64-linux-gnu-gcc \
    --gcc.linkerexe:aarch64-linux-gnu-gcc \
    --passL:-static \
    nip.nim
```

### Linux (RISC-V)

```bash
# Install RISC-V toolchain
sudo apt install gcc-riscv64-linux-gnu

# Build for RISC-V
nim c \
    --cpu:riscv64 \
    --os:linux \
    --define:static \
    --gcc.exe:riscv64-linux-gnu-gcc \
    nip.nim
```

## Troubleshooting

### Problem: Binary has dynamic dependencies

**Symptom:**
```bash
ldd nip-static
# Shows: linux-vdso.so.1, libc.so.6, etc.
```

**Solution:**
1. Ensure `--passL:-static` is used
2. Check for dynamic library overrides
3. Use musl-gcc instead of gcc
4. Verify no `-l` flags without static variants

### Problem: Binary is too large (>15MB)

**Causes:**
- Not using musl (glibc is larger)
- Debug symbols not stripped
- LTO not enabled

**Solutions:**
```bash
# Strip manually if needed
strip nip-static

# Verify LTO is enabled
nim c --define:static --passC:-flto --passL:-flto ...

# Use musl
nim c --define:static --gcc.exe:musl-gcc ...
```

### Problem: "undefined reference" errors

**Symptom:**
```
/usr/bin/ld: undefined reference to `pthread_create'
```

**Solution:**
Add threading library explicitly:
```bash
nim c --define:static --passL:-lpthread ...
```

### Problem: SSL/TLS not working

**Symptom:**
```
Error: SSL library not found
```

**Solution:**
Ensure static SSL libraries are available:
```bash
# Debian/Ubuntu
sudo apt install libssl-dev

# Build with SSL
nim c --define:static --define:ssl --passL:-lssl --passL:-lcrypto ...
```

## Size Optimization Tips

### 1. Use Musl Instead of Glibc

**Savings:** ~3-5MB

```bash
nim c --define:static --gcc.exe:musl-gcc nip.nim
```

### 2. Enable Link-Time Optimization

**Savings:** ~1-2MB

```bash
nim c --define:static --passC:-flto --passL:-flto nip.nim
```

### 3. Strip Symbols

**Savings:** ~500KB-1MB

```bash
nim c --define:static --passL:-s nip.nim
# Or manually: strip nip-static
```

### 4. Enable Section Garbage Collection

**Savings:** ~500KB

```bash
nim c \
    --define:static \
    --passC:-ffunction-sections \
    --passC:-fda-sections \
    --passL:-Wl,--gc-sections \
    nip.nim
```

### 5. Use UPX Compression (Optional)

**Savings:** ~50-70% (but slower startup)

```bash
# Install UPX
sudo apt install upx-ucl

# Compress binary
upx --best --lzma nip-static

# Result: ~2-3MB compressed
```

**Trade-off:** Slower startup time (~100-200ms decompression)

## Deployment

### Minimal Netinst Image

Create a minimal bootable image with just NIP:

```bash
# 1. Create minimal rootfs
mkdir -p netinst/{bin,lib,etc,usr/local/bin}

# 2. Copy static binary
cp nip-static netinst/usr/local/bin/nip

# 3. Add minimal init
cat > netinst/init << 'EOF'
#!/bin/sh
exec /usr/local/bin/nip setup
EOF
chmod +x netinst/init

# 4. Create initramfs
cd netinst
find . | cpio -o -H newc | gzip > ../netinst.img
```

**Result:** ~50-100MB bootable image with NIP

### Installation Script

The static build includes a minimal install script:

```bash
#!/bin/bash
# Ultra-minimal NIP installation
sudo cp nip /usr/local/bin/nip
sudo chmod +x /usr/local/bin/nip
sudo nip setup
```

## CI/CD Integration

### GitHub Actions Example

```yaml
name: Build Static Binary

on: [push, pull_request]

jobs:
  build-static:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install Nim
        uses: jiro4989/setup-nim-action@v1
        with:
          nim-version: '2.0.0'

      - name: Install musl-tools
        run: sudo apt-get install -y musl-tools

      - name: Build static binary
        run: |
          cd nip
          ./build_static.sh

      - name: Verify static linking
        run: |
          ldd nip/nip-static || echo "Fully static"

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: nip-static
          path: nip/nip-static
```

## Performance Considerations

### Startup Time

- **Dynamic binary:** ~10-20ms
- **Static binary (uncompressed):** ~15-30ms
- **Static binary (UPX compressed):** ~100-200ms

### Runtime Performance

Static binaries have **identical runtime performance** to dynamic binaries. The only difference is startup time.

### Memory Usage

Static binaries may use slightly more memory (~1-2MB) because:
- Entire libc is included
- No shared library benefits

For NIP's use case (system package manager), this is negligible.

## Best Practices

### 1. Always Verify Static Linking

```bash
ldd nip-static | grep -q "not a dynamic" || echo "WARNING: Not fully static"
```

### 2. Test on Minimal Systems

```bash
# Test in Docker with minimal base
docker run --rm -v $(pwd):/app alpine:latest /app/nip-static --version
```

### 3. Document Dependencies

Even static binaries have kernel requirements:
- Linux kernel 4.19+ (for modern syscalls)
- x86_64, ARM64, or RISC-V architecture

### 4. Provide Multiple Variants

Offer both static and dynamic builds:
- Static: For minimal installs, netboot, embedded
- Dynamic: For regular systems, faster startup

## References

- [Nim Static Linking Guide](https://nim-lang.org/docs/nimc.html#compiler-usage-static-linking)
- [Musl Libc](https://musl.libc.org/)
- [UPX Compression](https://upx.github.io/)
- [NIP Minimal Install Philosophy](.kiro/steering/nip/minimal-install-philosophy.md)

---

**Document Version:** 1.0
**Last Updated:** November 18, 2025
**Applies To:** NIP v0.2.0 "Weihnachtsmann"
