Add static musl binary build (Docker + local)

ober

bd768a5523407e2e337b8017a07e670c659c40ff

diff --git a/.gitignore b/.gitignore
index c63c751..9265087 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
 gitsafe-bin
+gitsafe-musl
+gitsafe-musl.sha256
 *.so
 *.wpo
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7c46c64
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,97 @@
+# Dockerfile — Build gitsafe-musl in a clean environment
+#
+# Produces a fully static binary with zero runtime dependencies.
+# No Chez Scheme or Jerboa installation needed on the target host.
+#
+# Usage:
+#   docker build -t gitsafe-builder .
+#   docker run --rm gitsafe-builder > gitsafe-musl && chmod +x gitsafe-musl
+#
+# Or extract via docker cp:
+#   docker build -t gitsafe-builder .
+#   id=$(docker create gitsafe-builder)
+#   docker cp $id:/out/gitsafe-musl ./gitsafe-musl
+#   docker cp $id:/out/gitsafe-musl.sha256 ./gitsafe-musl.sha256
+#   docker rm $id
+
+FROM ubuntu:24.04 AS builder
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+# ── System dependencies ──────────────────────────────────────────────────────
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    build-essential \
+    musl-tools \
+    musl-dev \
+    git \
+    ca-certificates \
+    libncurses-dev \
+    uuid-dev \
+    liblz4-dev \
+    zlib1g-dev \
+    && rm -rf /var/lib/apt/lists/*
+
+# Set HOME early — everything under /build so no real paths leak into binary
+ENV HOME=/build
+WORKDIR /build
+
+# ── Build Chez Scheme (stock glibc, for compilation steps) ───────────────────
+RUN git clone --depth 1 https://github.com/ober/ChezScheme.git && \
+    cd ChezScheme && \
+    git submodule update --init --depth 1 && \
+    ./configure --threads --disable-x11 --installprefix=/usr/local && \
+    make -j$(nproc) && \
+    make install && \
+    cd /build && rm -rf ChezScheme
+
+# ── Build Chez Scheme (musl, for static linking) ────────────────────────────
+# Two-pass build:
+#   Pass 1: Full build with stock gcc to generate boot files
+#   Pass 2: Rebuild kernel only with musl-gcc --static, reusing boot files
+RUN git clone https://github.com/ober/ChezScheme.git chez-musl-src && \
+    cd chez-musl-src && \
+    git submodule update --init && \
+    ./configure --threads --disable-x11 --installprefix=/build/chez-musl && \
+    make -j$(nproc) && \
+    cp ta6le/boot/ta6le/petite.boot /tmp/petite.boot && \
+    cp ta6le/boot/ta6le/scheme.boot /tmp/scheme.boot && \
+    make clean && \
+    ./configure --threads --disable-x11 --static CC=musl-gcc --installprefix=/build/chez-musl && \
+    mkdir -p ta6le/boot/ta6le && \
+    cp /tmp/petite.boot ta6le/boot/ta6le/ && \
+    cp /tmp/scheme.boot ta6le/boot/ta6le/ && \
+    make -j$(nproc) kernel && \
+    make install && \
+    cd /build && rm -rf chez-musl-src /tmp/petite.boot /tmp/scheme.boot
+
+# ── Clone Jerboa ─────────────────────────────────────────────────────────────
+ARG CACHE_BUST=0
+WORKDIR /build/mine
+RUN git clone --depth 1 https://github.com/ober/jerboa.git
+
+# ── Copy gitsafe source ─────────────────────────────────────────────────────
+COPY . /build/mine/jerboa-gitsafe
+
+# ── Build gitsafe-musl ───────────────────────────────────────────────────────
+ENV JERBOA_MUSL_CHEZ_PREFIX=/build/chez-musl
+ENV JERBOA_HOME=/build/mine/jerboa
+WORKDIR /build/mine/jerboa-gitsafe
+RUN make gitsafe-musl-local
+
+# ── Verify ───────────────────────────────────────────────────────────────────
+RUN ./gitsafe-musl --version
+RUN echo "--- Binary info ---" && \
+    ls -lh gitsafe-musl && \
+    file gitsafe-musl && \
+    echo "--- Hardening checks ---" && \
+    { file gitsafe-musl | grep -qE 'stripped|no section header' && echo "  PASS: stripped" || echo "  FAIL: not stripped"; } && \
+    { test -f gitsafe-musl.sha256 && echo "  PASS: integrity hash present" || echo "  FAIL: no hash"; } && \
+    echo "--- Path leak check ---" && \
+    count=$(strings gitsafe-musl | grep -c '/home/' || true) && \
+    { [ "$count" -gt 0 ] && echo "  WARNING: home paths found ($count)" || echo "  PASS: no home path leaks"; }
+
+# ── Output ───────────────────────────────────────────────────────────────────
+FROM ubuntu:24.04
+COPY --from=builder /build/mine/jerboa-gitsafe/gitsafe-musl /out/gitsafe-musl
+COPY --from=builder /build/mine/jerboa-gitsafe/gitsafe-musl.sha256 /out/gitsafe-musl.sha256
+CMD ["cat", "/out/gitsafe-musl"]
diff --git a/Makefile b/Makefile
index 875c4dc..c8187ba 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,8 @@ BIN_DIR := $(HOME)/.local/bin
 TEMPLATE_DIR := $(HOME)/.git-templates
 HOOK_DIR := $(TEMPLATE_DIR)/hooks
 
-.PHONY: run test binary install clean
+.PHONY: run test binary install clean \
+        gitsafe-musl gitsafe-musl-local docker verify-harden help
 
 run:
 	JERBOA_HOME=$(JERBOA_HOME) \
@@ -37,7 +38,79 @@ install: binary
 	@echo "All new repos (git init / git clone) will use gitsafe automatically."
 	@echo "To add to an existing repo: cd repo && git init"
 
+# ── Static musl binary ──────────────────────────────────────────────────────
+# Use `make gitsafe-musl` to build in Docker (canonical, reproducible).
+# Use `make gitsafe-musl-local` to build directly on the host (requires
+# musl-gcc and a musl-built Chez at ~/chez-musl or JERBOA_MUSL_CHEZ_PREFIX).
+
+gitsafe-musl: docker
+
+gitsafe-musl-local:
+	JERBOA_HOME=$(JERBOA_HOME) \
+		./build-gitsafe-musl.sh
+
+docker:
+	@echo "=== Building gitsafe-musl in Docker ==="
+	docker build --platform linux/amd64 --build-arg CACHE_BUST=$$(date +%s) -t gitsafe-builder .
+	@id=$$(docker create --platform linux/amd64 gitsafe-builder) && \
+	docker cp $$id:/out/gitsafe-musl ./gitsafe-musl && \
+	docker cp $$id:/out/gitsafe-musl.sha256 ./gitsafe-musl.sha256 && \
+	docker rm $$id >/dev/null && \
+	chmod +x gitsafe-musl
+	@echo ""
+	@ls -lh gitsafe-musl
+	@file gitsafe-musl
+
+install-musl: gitsafe-musl
+	mkdir -p $(BIN_DIR)
+	cp gitsafe-musl $(BIN_DIR)/gitsafe
+	@echo "Installed gitsafe-musl to $(BIN_DIR)/gitsafe"
+	mkdir -p $(HOOK_DIR)
+	printf '#!/bin/sh\nexec gitsafe pre-commit\n' > $(HOOK_DIR)/pre-commit
+	chmod +x $(HOOK_DIR)/pre-commit
+	printf '#!/bin/sh\nwhile read local_ref local_sha remote_ref remote_sha; do\n  gitsafe pre-push --local-ref "$$local_ref" --remote-ref "$$remote_ref" || exit $$?\ndone\n' > $(HOOK_DIR)/pre-push
+	chmod +x $(HOOK_DIR)/pre-push
+	git config --global init.templateDir $(TEMPLATE_DIR)
+	@echo ""
+	@echo "Global git hooks installed. Static binary — no runtime dependencies."
+
+verify-harden: gitsafe-musl
+	@echo "=== Hardening verification ==="
+	@(file gitsafe-musl | grep -qE 'stripped|no section header') && echo "  PASS: binary is stripped" || echo "  FAIL: binary not stripped"
+	@if strings gitsafe-musl | grep -q "$(HOME)"; then \
+		echo "  WARN: home directory path found in binary"; \
+	else \
+		echo "  PASS: no home directory paths leaked"; \
+	fi
+	@if [ -f gitsafe-musl.sha256 ]; then \
+		echo "  PASS: gitsafe-musl.sha256 exists ($$(wc -c < gitsafe-musl.sha256) bytes)"; \
+	else \
+		echo "  FAIL: gitsafe-musl.sha256 not found"; \
+	fi
+	@./gitsafe-musl --version >/dev/null 2>&1 && echo "  PASS: binary runs" || echo "  FAIL: binary doesn't run"
+
+# ── Cleanup ──────────────────────────────────────────────────────────────────
 clean:
 	find . -name '*.so' -delete
 	find . -name '*.wpo' -delete
-	rm -f gitsafe-bin
+	rm -f gitsafe-bin gitsafe-musl gitsafe-musl.sha256
+
+# ── Help ─────────────────────────────────────────────────────────────────────
+help:
+	@echo "gitsafe — secret-scanning git hooks"
+	@echo ""
+	@echo "Development:"
+	@echo "  make run ARGS='...'           Run gitsafe in interpreter mode"
+	@echo "  make test                     Run test suite"
+	@echo ""
+	@echo "Native binary (requires local Chez + Jerboa):"
+	@echo "  make binary                   Build gitsafe-bin (dynamic, native)"
+	@echo "  make install                  Build + install to ~/.local/bin"
+	@echo ""
+	@echo "Static binary (zero runtime dependencies):"
+	@echo "  make gitsafe-musl             Docker build (canonical, reproducible)"
+	@echo "  make gitsafe-musl-local       Local build (requires musl-gcc + musl Chez)"
+	@echo "  make install-musl             Docker build + install to ~/.local/bin"
+	@echo "  make verify-harden            Verify binary hardening (stripped, no leaks)"
+	@echo ""
+	@echo "  make clean                    Remove all build artifacts"
diff --git a/build-gitsafe-musl.sh b/build-gitsafe-musl.sh
new file mode 100755
index 0000000..03ba5e9
--- /dev/null
+++ b/build-gitsafe-musl.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+# build-gitsafe-musl.sh — Build gitsafe as a fully static binary using musl libc
+#
+# Prerequisites:
+#   - musl-gcc installed (apt install musl-tools)
+#   - Chez Scheme built with: ./configure --threads --static CC=musl-gcc
+#     and installed to ~/chez-musl (or set JERBOA_MUSL_CHEZ_PREFIX)
+#   - Jerboa libraries available
+#
+# The build uses stock scheme (glibc) for the Scheme compilation steps,
+# then musl-gcc for the C compilation and linking steps.
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+# Resolve jerboa
+if [ -n "${JERBOA_HOME:-}" ]; then
+    JERBOA_LIB="${JERBOA_HOME}/lib"
+elif [ -d "${SCRIPT_DIR}/../jerboa/lib" ]; then
+    JERBOA_LIB="$(realpath "${SCRIPT_DIR}/../jerboa/lib")"
+elif [ -d "${HOME}/mine/jerboa/lib" ]; then
+    JERBOA_LIB="${HOME}/mine/jerboa/lib"
+else
+    echo "ERROR: Cannot find Jerboa. Set JERBOA_HOME."
+    exit 1
+fi
+export JERBOA_HOME="${JERBOA_HOME:-$(dirname "$JERBOA_LIB")}"
+
+echo "==================================="
+echo "Building gitsafe-musl (static)"
+echo "==================================="
+echo ""
+echo "Jerboa: $JERBOA_LIB"
+echo ""
+
+# Check musl availability
+if ! command -v musl-gcc &>/dev/null; then
+    echo "ERROR: musl-gcc not found"
+    echo "Install: sudo apt install musl-tools"
+    exit 1
+fi
+
+echo "[1/2] Validating musl toolchain..."
+echo "  musl-gcc: $(command -v musl-gcc)"
+echo ""
+
+echo "[2/2] Running musl build..."
+scheme -q --libdirs "${SCRIPT_DIR}:${JERBOA_LIB}" --script build-gitsafe-musl.ss
+
+# Verify
+if [ -f "gitsafe-musl" ]; then
+    echo ""
+    echo "==================================="
+    echo "gitsafe-musl built successfully!"
+    echo "==================================="
+    ls -lh gitsafe-musl
+    echo ""
+    file gitsafe-musl
+    echo ""
+    ldd gitsafe-musl 2>&1 || echo "  (Fully static — no dynamic dependencies)"
+    echo ""
+    echo "Test: ./gitsafe-musl --version"
+else
+    echo "ERROR: gitsafe-musl not created"
+    exit 1
+fi
diff --git a/build-gitsafe-musl.ss b/build-gitsafe-musl.ss
new file mode 100644
index 0000000..2607932
--- /dev/null
+++ b/build-gitsafe-musl.ss
@@ -0,0 +1,245 @@
+#!chezscheme
+;; Build gitsafe as a fully static binary using musl libc.
+;;
+;; Usage: make gitsafe-musl-local
+;;   (runs via build-gitsafe-musl.sh → this script)
+;;
+;; Prerequisites:
+;;   - musl-gcc installed (apt install musl-tools)
+;;   - Chez Scheme built with: ./configure --threads --static CC=musl-gcc
+;;     installed to ~/chez-musl (or set JERBOA_MUSL_CHEZ_PREFIX)
+;;   - Stock scheme (glibc) for compilation steps
+;;
+;; Produces: ./gitsafe-musl (fully static ELF binary, zero runtime dependencies)
+
+(import (chezscheme))
+
+;; --- Helper: generate C byte-array from binary file ---
+(define (file->c-header input-path output-path array-name size-name)
+  (let* ([port (open-file-input-port input-path)]
+         [data (get-bytevector-all port)]
+         [size (bytevector-length data)])
+    (close-port port)
+    (call-with-output-file output-path
+      (lambda (out)
+        (fprintf out "/* Auto-generated — do not edit */\n")
+        (fprintf out "static const unsigned char ~a[] = {\n" array-name)
+        (let loop ([i 0])
+          (when (< i size)
+            (when (= 0 (modulo i 16)) (fprintf out "  "))
+            (fprintf out "0x~2,'0x" (bytevector-u8-ref data i))
+            (when (< (+ i 1) size) (fprintf out ","))
+            (when (= 15 (modulo i 16)) (fprintf out "\n"))
+            (loop (+ i 1))))
+        (fprintf out "\n};\n")
+        (fprintf out "static const unsigned int ~a = ~a;\n" size-name size))
+      'replace)
+    (printf "  ~a: ~a bytes\n" output-path size)))
+
+;; --- Locate musl-built Chez Scheme ---
+(define musl-chez-prefix
+  (or (getenv "JERBOA_MUSL_CHEZ_PREFIX")
+      (let* ([home (getenv "HOME")]
+             [p (format "~a/chez-musl" home)])
+        (and (file-exists? p) p))))
+
+(unless musl-chez-prefix
+  (display "Error: Cannot find musl Chez install.\n")
+  (display "  Set JERBOA_MUSL_CHEZ_PREFIX or install to ~/chez-musl\n")
+  (display "  See: https://github.com/ober/ChezScheme (build with --static CC=musl-gcc)\n")
+  (exit 1))
+
+(define (find-csv-dir lib-dir mt)
+  (let ([csv-dir
+          (let lp ([dirs (guard (e [#t '()]) (directory-list lib-dir))])
+            (cond
+              [(null? dirs) #f]
+              [(and (> (string-length (car dirs)) 3)
+                    (string=? "csv" (substring (car dirs) 0 3)))
+               (format "~a/~a/~a" lib-dir (car dirs) mt)]
+              [else (lp (cdr dirs))]))])
+    (and csv-dir
+         (file-exists? (format "~a/main.o" csv-dir))
+         csv-dir)))
+
+(define musl-chez-dir
+  (let ([mt (symbol->string (machine-type))])
+    (or (find-csv-dir (format "~a/lib" musl-chez-prefix) mt)
+        (begin
+          (printf "Error: Cannot find Chez ~a dir under ~a/lib\n"
+                  (machine-type) musl-chez-prefix)
+          (printf "  Expected: ~a/lib/csv<version>/~a/main.o\n"
+                  musl-chez-prefix mt)
+          (exit 1)))))
+
+;; --- Locate Jerboa ---
+(define home (getenv "HOME"))
+(define jerboa-dir
+  (or (getenv "JERBOA_HOME")
+      (let ([sibling (format "~a/../jerboa" (current-directory))])
+        (and (file-exists? sibling) sibling))
+      (begin
+        (display "Error: Cannot find Jerboa. Set JERBOA_HOME.\n")
+        (exit 1))))
+
+(printf "=== gitsafe musl static build ===\n")
+(printf "Musl Chez dir: ~a\n" musl-chez-dir)
+(printf "Jerboa dir:    ~a\n" jerboa-dir)
+(printf "Machine type:  ~a\n" (machine-type))
+
+;; Add library paths (stock Chez for compilation)
+(library-directories
+  (append
+    (list (cons (current-directory) (current-directory))
+          (cons (format "~a/lib" jerboa-dir)
+                (format "~a/lib" jerboa-dir)))
+    (library-directories)))
+
+;; --- Step 1: Compile all modules (optimize-level 3, WPO) ---
+(printf "\n[1/6] Compiling all modules (optimize-level 3, WPO)...\n")
+(parameterize ([compile-imported-libraries  #t]
+               [optimize-level              3]
+               [cp0-effort-limit            500]
+               [cp0-score-limit             50]
+               [cp0-outer-unroll-limit      1]
+               [commonization-level         4]
+               [enable-unsafe-application   #t]
+               [enable-unsafe-variable-reference #t]
+               [enable-arithmetic-left-associative #t]
+               [debug-level                 0]
+               [generate-inspector-information #f]
+               [generate-wpo-files          #t])
+  (compile-program "gitsafe/main-binary.ss"))
+
+;; --- Step 2: Whole-program optimization ---
+(printf "[2/6] Running whole-program optimization...\n")
+(let ([missing (compile-whole-program "gitsafe/main-binary.wpo" "gitsafe-all.so")])
+  (unless (null? missing)
+    (printf "  WPO: ~a libraries not incorporated (missing .wpo):\n" (length missing))
+    (for-each (lambda (lib) (printf "    ~a\n" lib)) missing)))
+
+;; --- Step 3: Create boot file + C headers ---
+(printf "[3/6] Creating boot file and C headers...\n")
+
+(define (existing-so-files paths)
+  (filter file-exists? paths))
+
+(define gitsafe-modules
+  '("gitsafe/entropy"
+    "gitsafe/config"
+    "gitsafe/allowlist"
+    "gitsafe/patterns"
+    "gitsafe/git"
+    "gitsafe/scanner"
+    "gitsafe/output"))
+
+(apply make-boot-file "gitsafe.boot" '("scheme" "petite")
+  (existing-so-files
+    (map (lambda (m) (format "~a.so" m)) gitsafe-modules)))
+
+;; Embed musl Chez boot files (must match musl kernel for ABI compatibility)
+(file->c-header "gitsafe-all.so"
+                "gitsafe_program.h"
+                "gitsafe_program_data" "gitsafe_program_size")
+(file->c-header (format "~a/petite.boot" musl-chez-dir)
+                "gitsafe_petite_boot.h"
+                "petite_boot_data" "petite_boot_size")
+(file->c-header (format "~a/scheme.boot" musl-chez-dir)
+                "gitsafe_scheme_boot.h"
+                "scheme_boot_data" "scheme_boot_size")
+(file->c-header "gitsafe.boot"
+                "gitsafe_boot.h"
+                "gitsafe_boot_data" "gitsafe_boot_size")
+
+;; --- Step 4: Generate C main with embedded program ---
+(printf "[4/6] Generating C main...\n")
+
+(call-with-output-file "gitsafe-main-musl.c"
+  (lambda (out)
+    (fprintf out "/* Auto-generated — do not edit */\n")
+    (fprintf out "#define _GNU_SOURCE\n")
+    (fprintf out "#include <stdlib.h>\n")
+    (fprintf out "#include <stdio.h>\n")
+    (fprintf out "#include <string.h>\n")
+    (fprintf out "#include <unistd.h>\n")
+    (fprintf out "#include \"scheme.h\"\n")
+    (fprintf out "#include \"gitsafe_petite_boot.h\"\n")
+    (fprintf out "#include \"gitsafe_scheme_boot.h\"\n")
+    (fprintf out "#include \"gitsafe_boot.h\"\n")
+    (fprintf out "#include \"gitsafe_program.h\"\n")
+    (fprintf out "\n")
+    ;; dlopen/dlsym stubs for static musl build
+    ;; Chez kernel references these; returning non-NULL from dlopen prevents
+    ;; load-shared-object from erroring, while dlsym returns NULL for any lookup.
+    (fprintf out "/* dlopen/dlsym stubs — no shared libraries in static binary */\n")
+    (fprintf out "void *dlopen(const char *f, int m) { (void)f; (void)m; return (void*)1; }\n")
+    (fprintf out "void *dlsym(void *h, const char *s) { (void)h; (void)s; return NULL; }\n")
+    (fprintf out "int dlclose(void *h) { (void)h; return 0; }\n")
+    (fprintf out "char *dlerror(void) { return \"static build\"; }\n")
+    (fprintf out "\n")
+    (fprintf out "int main(int argc, char *argv[]) {\n")
+    (fprintf out "  char prog_path[256];\n")
+    (fprintf out "  const char *tmpdir = getenv(\"TMPDIR\");\n")
+    (fprintf out "  if (!tmpdir) tmpdir = \"/tmp\";\n")
+    (display  "  snprintf(prog_path, sizeof(prog_path), \"%s/gitsafe-XXXXXX\", tmpdir);\n" out)
+    (fprintf out "  int fd = mkstemp(prog_path);\n")
+    (fprintf out "  if (fd < 0) { perror(\"mkstemp\"); return 1; }\n")
+    (fprintf out "  if (write(fd, gitsafe_program_data, gitsafe_program_size)\n")
+    (fprintf out "      != (ssize_t)gitsafe_program_size) {\n")
+    (fprintf out "    perror(\"write\"); close(fd); unlink(prog_path); return 1;\n")
+    (fprintf out "  }\n")
+    (fprintf out "  close(fd);\n")
+    (fprintf out "\n")
+    (fprintf out "  Sscheme_init(NULL);\n")
+    (fprintf out "  Sregister_boot_file_bytes(\"petite\", (void*)petite_boot_data, petite_boot_size);\n")
+    (fprintf out "  Sregister_boot_file_bytes(\"scheme\", (void*)scheme_boot_data, scheme_boot_size);\n")
+    (fprintf out "  Sregister_boot_file_bytes(\"gitsafe\", (void*)gitsafe_boot_data, gitsafe_boot_size);\n")
+    (fprintf out "  Sbuild_heap(NULL, NULL);\n")
+    (fprintf out "  int status = Sscheme_script(prog_path, argc, (const char **)argv);\n")
+    (fprintf out "  unlink(prog_path);\n")
+    (fprintf out "  Sscheme_deinit();\n")
+    (fprintf out "  return status;\n")
+    (fprintf out "}\n"))
+  'replace)
+
+;; --- Step 5: Compile and link with musl-gcc ---
+(printf "[5/6] Compiling and linking with musl-gcc (static)...\n")
+
+(define link-libs "-lkernel -llz4 -lz -lm -ldl -lpthread -luuid -lncurses")
+
+(let ([rc (system (format "musl-gcc -c -O2 -I~a -o gitsafe-main-musl.o gitsafe-main-musl.c"
+                          musl-chez-dir))])
+  (unless (= rc 0) (printf "Error: C compilation failed\n") (exit 1)))
+
+(let ([rc (system (format "musl-gcc -o gitsafe-musl gitsafe-main-musl.o -L~a ~a -static -Wl,--allow-multiple-definition"
+                          musl-chez-dir link-libs))])
+  (unless (= rc 0) (printf "Error: linking failed\n") (exit 1)))
+
+;; Strip and generate integrity hash
+(printf "  Stripping binary...\n")
+(system "strip --strip-all gitsafe-musl")
+(system "sha256sum gitsafe-musl > gitsafe-musl.sha256")
+
+;; --- Step 6: Cleanup ---
+(printf "[6/6] Cleaning up intermediate files...\n")
+(for-each (lambda (f) (when (file-exists? f) (delete-file f)))
+  '("gitsafe-main-musl.c" "gitsafe-main-musl.o"
+    "gitsafe_program.h" "gitsafe_petite_boot.h"
+    "gitsafe_scheme_boot.h" "gitsafe_boot.h"
+    "gitsafe-all.so" "gitsafe.boot"
+    "gitsafe/main-binary.wpo" "gitsafe/main-binary.so"))
+
+(for-each (lambda (m)
+            (for-each (lambda (ext)
+                        (let ([f (format "~a~a" m ext)])
+                          (when (file-exists? f) (delete-file f))))
+                      '(".so" ".wpo")))
+          gitsafe-modules)
+
+(printf "\nDone! Binary: ./gitsafe-musl\n")
+(printf "  Size:   ")
+(system "ls -lh gitsafe-musl | awk '{print $5}'")
+(printf "  SHA256: ")
+(system "cat gitsafe-musl.sha256")
+(printf "\n  Test:   ./gitsafe-musl --version\n")
+(printf "  Verify: file gitsafe-musl && ldd gitsafe-musl\n")