Add Docker-based static musl build pipeline

ober

ca9255dd4ef34ad48a835f5cea6a4575e573db5b

diff --git a/.gitignore b/.gitignore
index 8e3abad..7b820db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,9 @@
 *.so
 *.wpo
+*.o
 keys/
+secmon-agent
+secmon-agent.sha256
+secmon-program.c
+secmon-agent.boot
+jerboa-stage/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..b9e9cf3
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,107 @@
+# Dockerfile — Build secmon-agent as a fully static binary
+#
+# All source repos are cloned and built inside the container under /build/mine,
+# with HOME=/build so no real usernames or home directories leak into the binary.
+#
+# Usage:
+#   docker build -t secmon-builder .
+#   id=$(docker create secmon-builder)
+#   docker cp $id:/out/secmon-agent ./secmon-agent
+#   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 \
+    curl \
+    uuid-dev \
+    pkg-config \
+    file \
+    && rm -rf /var/lib/apt/lists/*
+
+# ── Rust toolchain (for jerboa-native-rs) ────────────────────────────────────
+RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
+    sh -s -- -y --default-toolchain stable --profile minimal && \
+    . /root/.cargo/env && \
+    rustup target add x86_64-unknown-linux-musl
+
+ENV PATH="/root/.cargo/bin:${PATH}"
+ENV RUSTUP_HOME="/root/.rustup"
+
+# Set HOME early — everything under /build so paths are clean
+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 passes: glibc for boot files, then musl for libkernel.a
+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 framework ──────────────────────────────────────────────────
+WORKDIR /build/mine
+RUN git clone --depth 1 https://github.com/ober/jerboa.git
+
+# ── Build Rust native library (static, musl target) ─────────────────────────
+RUN cd /build/mine/jerboa/jerboa-native-rs && \
+    CARGO_HOME=/build/.cargo \
+    RUSTFLAGS="--remap-path-prefix /build/.cargo/registry/src=crate --remap-path-prefix /build/mine=src" \
+    cargo build --release --target x86_64-unknown-linux-musl && \
+    strip -S target/x86_64-unknown-linux-musl/release/libjerboa_native.a
+
+# ── Copy secmon source ───────────────────────────────────────────────────────
+COPY . /build/mine/jerboa-secmon
+
+# ── Set environment for build ────────────────────────────────────────────────
+ENV JERBOA_MUSL_CHEZ_PREFIX=/build/chez-musl
+ENV JERBOA=/build/mine/jerboa/lib
+
+# ── Build secmon-agent ───────────────────────────────────────────────────────
+WORKDIR /build/mine/jerboa-secmon
+RUN make secmon-musl-local
+
+# ── Verify ───────────────────────────────────────────────────────────────────
+RUN echo "--- Binary info ---" && \
+    ls -lh secmon-agent && \
+    file secmon-agent && \
+    echo "--- Hardening checks ---" && \
+    (file secmon-agent | grep -qE 'stripped|no section header') && echo "  PASS: stripped" || echo "  FAIL: not stripped" && \
+    test -f secmon-agent.sha256 && echo "  PASS: integrity hash present" || echo "  FAIL: no hash" && \
+    echo "--- Username leak check ---" && \
+    count=$(strings secmon-agent | grep -c '/home/jafourni' || true) && \
+    echo "Occurrences of username path: $count" && \
+    if [ "$count" -gt 0 ]; then echo "WARNING: username still present"; fi
+
+# ── Output ───────────────────────────────────────────────────────────────────
+FROM ubuntu:24.04
+COPY --from=builder /build/mine/jerboa-secmon/secmon-agent /out/secmon-agent
+COPY --from=builder /build/mine/jerboa-secmon/secmon-agent.sha256 /out/secmon-agent.sha256
+CMD ["cat", "/out/secmon-agent"]
diff --git a/Makefile b/Makefile
index 22d46d9..45d8f31 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ LIBDIRS = lib:$(JERBOA)
 NATIVE_RS ?= $(HOME)/mine/jerboa/jerboa-native-rs/target/release
 export LD_LIBRARY_PATH := $(NATIVE_RS):$(LD_LIBRARY_PATH)
 
-.PHONY: build clean test keygen agent collector analyze
+.PHONY: build clean test keygen agent collector analyze docker secmon-musl secmon-musl-local
 
 build:
 	$(SCHEME) -q --libdirs $(LIBDIRS) --compile-imported-libraries < build-all.ss
@@ -32,3 +32,25 @@ test:
 		$(SCHEME) --libdirs $(LIBDIRS) --script $$f || exit 1; \
 	done
 	@echo "All tests passed."
+
+# ─── musl Static Binary ──────────────────────────────────────────────────────
+
+secmon-musl: docker
+
+secmon-musl-local: build
+	@echo "=== Building static secmon-agent with musl (local) ==="
+	./build-secmon-musl.sh
+
+# ─── Docker Build ────────────────────────────────────────────────────────────
+
+docker:
+	@echo "=== Building secmon-agent in Docker ==="
+	docker build -t secmon-builder .
+	@id=$$(docker create secmon-builder) && \
+	docker cp $$id:/out/secmon-agent ./secmon-agent && \
+	docker cp $$id:/out/secmon-agent.sha256 ./secmon-agent.sha256 && \
+	docker rm $$id >/dev/null && \
+	chmod +x secmon-agent
+	@echo "=== Docker build complete ==="
+	@ls -lh secmon-agent
+	@file secmon-agent
diff --git a/build-secmon-musl.sh b/build-secmon-musl.sh
new file mode 100755
index 0000000..0812256
--- /dev/null
+++ b/build-secmon-musl.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+# build-secmon-musl.sh — Build secmon-agent 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 compiled
+#   - libjerboa_native.a built for x86_64-unknown-linux-musl
+set -euo pipefail
+
+JERBOA_DIR="${JERBOA_DIR:-$HOME/mine/jerboa}"
+JERBOA_LIB="${JERBOA_DIR}/lib"
+
+echo "==================================="
+echo "Building secmon-agent with musl libc (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
+
+# Use jerboa's musl module to validate
+echo "[1/2] Validating musl toolchain via jerboa..."
+scheme -q --libdirs "lib:${JERBOA_LIB}" <<'VALIDATE'
+(import (chezscheme) (jerboa build musl))
+(let ([result (validate-musl-setup)])
+  (printf "  ~a: ~a~n" (car result) (cdr result))
+  (unless (eq? (car result) 'ok)
+    (exit 1)))
+VALIDATE
+
+echo ""
+echo "[2/2] Running musl build..."
+NATIVE_RS="${NATIVE_RS:-$HOME/mine/jerboa/jerboa-native-rs/target/x86_64-unknown-linux-musl/release}"
+LD_LIBRARY_PATH="${NATIVE_RS}:${LD_LIBRARY_PATH:-}" \
+  scheme -q --libdirs "lib:${JERBOA_LIB}" \
+  <build-secmon-musl.ss
+
+# Verify
+if [ -f "secmon-agent" ]; then
+	echo ""
+	echo "==================================="
+	echo "secmon-agent built successfully!"
+	echo "==================================="
+	ls -lh secmon-agent
+	echo ""
+	file secmon-agent
+	echo ""
+	ldd secmon-agent 2>&1 || echo "  (Fully static - no dependencies)"
+else
+	echo "ERROR: secmon-agent not created"
+	exit 1
+fi
diff --git a/build-secmon-musl.ss b/build-secmon-musl.ss
new file mode 100644
index 0000000..9e88696
--- /dev/null
+++ b/build-secmon-musl.ss
@@ -0,0 +1,485 @@
+#!chezscheme
+;;; build-secmon-musl.ss — Build a fully static secmon-agent binary using musl libc
+;;;
+;;; Usage: scheme -q --libdirs lib:<jerboa-lib> < build-secmon-musl.ss
+;;;
+;;; This script:
+;;;   1. Patches load-shared-object calls for static build
+;;;   2. Compiles all secmon modules
+;;;   3. Creates boot file + optimized program .so
+;;;   4. Generates C files with embedded boot data + FFI symbol registration
+;;;   5. Compiles C with musl-gcc against musl-built Chez's scheme.h
+;;;   6. Links fully static binary with libkernel.a + libjerboa_native.a
+;;;
+;;; The resulting secmon-agent binary has zero runtime dependencies.
+
+(import
+  (except (chezscheme) void box box? unbox set-box!
+          andmap ormap iota last-pair find
+          1+ 1- fx/ fx1+ fx1-
+          error error? raise with-exception-handler identifier?
+          hash-table? make-hash-table)
+  (jerboa build)
+  (jerboa build musl))
+
+;; ========== Validate musl setup ==========
+
+(let ([result (validate-musl-setup)])
+  (unless (eq? (car result) 'ok)
+    (printf "Error: ~a~n" (cdr result))
+    (exit 1)))
+
+(printf "musl Chez found: ~a~n~n" (musl-chez-lib-dir))
+
+;; ========== Locate directories ==========
+
+(define jerboa-dir
+  (or (getenv "JERBOA_DIR")
+      (format "~a/mine/jerboa/lib" (getenv "HOME"))))
+
+(define native-lib-path
+  (format "~a/mine/jerboa/jerboa-native-rs/target/x86_64-unknown-linux-musl/release/libjerboa_native.a"
+          (getenv "HOME")))
+
+(define has-native-lib? (file-exists? native-lib-path))
+(unless has-native-lib?
+  (printf "ERROR: libjerboa_native.a not found at ~a~n" native-lib-path)
+  (printf "Build with: cd ~/mine/jerboa/jerboa-native-rs && cargo build --release --target x86_64-unknown-linux-musl~n")
+  (exit 1))
+
+(printf "Native lib: ~a~n~n" native-lib-path)
+
+;; ========== Step 0: Stage and patch jerboa std modules for static builds ==========
+;; Modules that call (load-shared-object ...) need to be patched to (void)
+;; since dlopen is unavailable in static musl builds. FFI symbols are
+;; pre-registered via Sforeign_symbol() in the C bootstrap.
+
+(printf "[0/7] Patching jerboa modules for static build (no dlopen)...~n")
+
+(define jerboa-stage (format "~a/jerboa-stage" (current-directory)))
+(system (format "rm -rf '~a'" jerboa-stage))
+(system (format "mkdir -p '~a'" jerboa-stage))
+
+;; Copy the jerboa lib tree to staging
+(system (format "cp -a '~a/' '~a/'" jerboa-dir jerboa-stage))
+
+;; Patch all load-shared-object calls in staged copies
+(system (format "find '~a' -name '*.sls' -exec sed -i 's/(load-shared-object[^)]*)/(void)/g' {} +" jerboa-stage))
+;; Delete pre-compiled .so/.wpo files to force recompilation from patched sources
+(system (format "find '~a' -name '*.so' -delete" jerboa-stage))
+(system (format "find '~a' -name '*.wpo' -delete" jerboa-stage))
+
+(printf "  Patched jerboa sources staged in ~a~n" jerboa-stage)
+
+;; ========== Step 1: Compile all secmon modules ==========
+
+(printf "~n[1/7] Compiling secmon modules...~n")
+
+;; Use the staged (patched) jerboa as primary library path
+(parameterize ([optimize-level 2]
+               [generate-inspector-information #f]
+               [compile-imported-libraries #t]
+               [library-directories
+                 (cons (cons jerboa-stage jerboa-stage)
+                       (library-directories))])
+  ;; Crypto modules (dependency order)
+  (for-each
+    (lambda (m)
+      (let ([path (format "lib/secmon/crypto/~a.sls" m)])
+        (printf "  Compiling ~a~n" path)
+        (compile-library path)))
+    '("keys" "ecies" "psk"))
+
+  ;; Monitor events (base types first)
+  (printf "  Compiling lib/secmon/monitor/events.sls~n")
+  (compile-library "lib/secmon/monitor/events.sls")
+  (printf "  Compiling lib/secmon/monitor/suspicious.sls~n")
+  (compile-library "lib/secmon/monitor/suspicious.sls")
+
+  ;; Platform
+  (for-each
+    (lambda (m)
+      (let ([path (format "lib/secmon/platform/~a.sls" m)])
+        (printf "  Compiling ~a~n" path)
+        (compile-library path)))
+    '("provider" "linux"))
+
+  ;; Buffer
+  (printf "  Compiling lib/secmon/buffer/ring.sls~n")
+  (compile-library "lib/secmon/buffer/ring.sls")
+
+  ;; Server
+  (for-each
+    (lambda (m)
+      (let ([path (format "lib/secmon/server/~a.sls" m)])
+        (printf "  Compiling ~a~n" path)
+        (compile-library path)))
+    '("protocol" "listener"))
+
+  ;; Config
+  (printf "  Compiling lib/secmon/config.sls~n")
+  (compile-library "lib/secmon/config.sls")
+
+  ;; Storage
+  (printf "  Compiling lib/secmon/storage/store.sls~n")
+  (compile-library "lib/secmon/storage/store.sls")
+
+  ;; All monitor modules
+  (for-each
+    (lambda (m)
+      (let ([path (format "lib/secmon/monitor/~a.sls" m)])
+        (printf "  Compiling ~a~n" path)
+        (compile-library path)))
+    '("process" "network" "files" "auth" "kernel" "cron"
+      "container" "dns" "rootkit" "persistence" "revshell"
+      "lateral" "logtamper" "webshell" "podman" "selinux"))
+
+  ;; Stealth modules
+  (for-each
+    (lambda (m)
+      (let ([path (format "lib/secmon/stealth/~a.sls" m)])
+        (printf "  Compiling ~a~n" path)
+        (compile-library path)))
+    '("anti-debug" "masquerade" "env-sanitize" "integrity" "init")))
+
+;; ========== Step 2: Compile agent program ==========
+
+(printf "~n[2/7] Compiling bin/agent.ss (optimize-level 3)...~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]
+               [library-directories
+                 (cons (cons jerboa-stage jerboa-stage)
+                       (library-directories))])
+  (compile-program "bin/agent.ss"))
+
+;; ========== Step 3: Pre-compile boot-file dependencies ==========
+;; Ensure all jerboa std modules used by secmon are compiled
+
+(printf "~n[3/7] Pre-compiling boot file dependencies...~n")
+
+(define jerboa-boot-modules
+  '("jerboa/core" "jerboa/runtime"
+    "std/error" "std/format" "std/sort" "std/pregexp" "std/sugar"
+    "std/misc/string" "std/misc/list" "std/misc/alist" "std/misc/thread"
+    "std/foreign"
+    "std/os/path" "std/os/file-info"
+    "std/crypto/native-rust"
+    "std/db/sqlite-native"
+    "std/gambit-compat"))
+
+(parameterize ([compile-imported-libraries #t]
+               [optimize-level 2]
+               [generate-inspector-information #f]
+               [library-directories
+                 (cons (cons jerboa-stage jerboa-stage)
+                       (library-directories))])
+  (for-each
+    (lambda (m)
+      (let ([sls (format "~a/~a.sls" jerboa-stage m)]
+            [so  (format "~a/~a.so" jerboa-stage m)])
+        (when (and (file-exists? sls) (not (file-exists? so)))
+          (printf "  Pre-compiling ~a~n" sls)
+          (compile-library sls))))
+    jerboa-boot-modules))
+
+;; ========== Step 4: Create libs-only boot file ==========
+
+(printf "~n[4/7] Creating boot file...~n")
+
+(define boot-libs
+  (append
+    ;; Jerboa runtime + stdlib
+    (map (lambda (m) (format "~a/~a.so" jerboa-stage m))
+      '("jerboa/core"
+        "jerboa/runtime"
+        "std/error"
+        "std/format"
+        "std/sort"
+        "std/pregexp"
+        "std/sugar"
+        "std/misc/string"
+        "std/misc/list"
+        "std/misc/alist"
+        "std/misc/thread"
+        "std/foreign"
+        "std/os/path"
+        "std/os/file-info"
+        "std/gambit-compat"
+        "std/crypto/native-rust"
+        "std/db/sqlite-native"))
+    ;; Secmon modules
+    (map (lambda (m) (format "lib/secmon/crypto/~a.so" m))
+      '("keys" "ecies" "psk"))
+    (list "lib/secmon/monitor/events.so"
+          "lib/secmon/monitor/suspicious.so")
+    (map (lambda (m) (format "lib/secmon/platform/~a.so" m))
+      '("provider" "linux"))
+    (list "lib/secmon/buffer/ring.so"
+          "lib/secmon/server/protocol.so"
+          "lib/secmon/server/listener.so"
+          "lib/secmon/config.so"
+          "lib/secmon/storage/store.so")
+    (map (lambda (m) (format "lib/secmon/monitor/~a.so" m))
+      '("process" "network" "files" "auth" "kernel" "cron"
+        "container" "dns" "rootkit" "persistence" "revshell"
+        "lateral" "logtamper" "webshell" "podman" "selinux"))
+    (map (lambda (m) (format "lib/secmon/stealth/~a.so" m))
+      '("anti-debug" "masquerade" "env-sanitize" "integrity" "init"))))
+
+;; Verify all .so files exist
+(for-each
+  (lambda (so)
+    (unless (file-exists? so)
+      (printf "ERROR: Missing boot file dependency: ~a~n" so)
+      (exit 1)))
+  boot-libs)
+
+(apply make-boot-file "secmon-agent.boot" '("scheme" "petite") boot-libs)
+(printf "  Created secmon-agent.boot~n")
+
+;; ========== Step 5: Generate C code ==========
+
+(printf "~n[5/7] Generating C code with embedded boot files + FFI symbols...~n")
+
+(define musl-lib-dir (musl-chez-lib-dir))
+(define petite-boot-path (format "~a/petite.boot" musl-lib-dir))
+(define scheme-boot-path (format "~a/scheme.boot" musl-lib-dir))
+
+;; Generate the C source
+(call-with-output-file "secmon-program.c"
+  (lambda (out)
+    ;; Headers
+    (display "#include \"scheme.h\"\n" out)
+    (display "#include <string.h>\n" out)
+    (display "#include <stdlib.h>\n" out)
+    (display "#include <stdio.h>\n" out)
+    (display "#include <signal.h>\n" out)
+    (display "#include <unistd.h>\n" out)
+    (display "#include <errno.h>\n" out)
+    (display "#include <sys/types.h>\n" out)
+    (display "#include <sys/socket.h>\n" out)
+    (display "#include <sys/wait.h>\n" out)
+    (display "#include <sys/mman.h>\n" out)
+    (display "#include <sys/prctl.h>\n" out)
+    (display "#include <sys/ptrace.h>\n" out)
+    (display "#include <netinet/in.h>\n" out)
+    (display "#include <arpa/inet.h>\n\n" out)
+
+    ;; Embed boot files as C arrays
+    (display (file->c-array petite-boot-path "petite_boot") out)
+    (newline out)
+    (display (file->c-array scheme-boot-path "scheme_boot") out)
+    (newline out)
+    (display (file->c-array "secmon-agent.boot" "app_boot") out)
+    (newline out)
+
+    ;; Embed the compiled program
+    (display (file->c-array "bin/agent.so" "program_so") out)
+    (newline out)
+
+    ;; Extern declarations for Rust native library symbols
+    (display "/* Rust native library symbols (libjerboa_native.a) */\n" out)
+    (for-each
+      (lambda (sym)
+        (fprintf out "extern int ~a();\n" sym))
+      '("jerboa_last_error"
+        "jerboa_sha1" "jerboa_sha256" "jerboa_sha384" "jerboa_sha512"
+        "jerboa_random_bytes"
+        "jerboa_hmac_sha256" "jerboa_hmac_sha256_verify"
+        "jerboa_timing_safe_equal"
+        "jerboa_aead_seal" "jerboa_aead_open"
+        "jerboa_chacha20_seal" "jerboa_chacha20_open"
+        "jerboa_scrypt"
+        "jerboa_pbkdf2_derive" "jerboa_pbkdf2_verify"
+        "jerboa_x25519_generate_keypair"
+        "jerboa_x25519_public_from_private"
+        "jerboa_x25519_diffie_hellman"
+        "jerboa_hkdf_sha256"
+        ;; SQLite
+        "jerboa_sqlite_open" "jerboa_sqlite_close" "jerboa_sqlite_exec"
+        "jerboa_sqlite_prepare" "jerboa_sqlite_finalize" "jerboa_sqlite_reset"
+        "jerboa_sqlite_bind_int" "jerboa_sqlite_bind_double"
+        "jerboa_sqlite_bind_text" "jerboa_sqlite_bind_blob"
+        "jerboa_sqlite_bind_null"
+        "jerboa_sqlite_step"
+        "jerboa_sqlite_column_count" "jerboa_sqlite_column_type"
+        "jerboa_sqlite_column_int" "jerboa_sqlite_column_double"
+        "jerboa_sqlite_column_text" "jerboa_sqlite_column_blob"
+        "jerboa_sqlite_column_name"
+        "jerboa_sqlite_last_insert_rowid" "jerboa_sqlite_changes"
+        "jerboa_sqlite_errmsg"))
+    (newline out)
+
+    ;; FFI symbol registration function
+    (display "static void register_ffi_symbols(void) {\n" out)
+    ;; libc symbols (needed by secmon monitors and jerboa std)
+    (for-each
+      (lambda (sym)
+        (fprintf out "    Sforeign_symbol(\"~a\", (void*)~a);\n" sym sym))
+      '("socket" "connect" "setsockopt" "bind" "listen" "accept"
+        "fork" "ptrace" "waitpid" "_exit" "kill"
+        "getpid" "getppid" "prctl" "mlockall"
+        "readlink" "unsetenv" "access"
+        "__errno_location"
+        "read" "write" "close" "open"
+        "memfd_create"))
+    ;; Rust native symbols
+    (for-each
+      (lambda (sym)
+        (fprintf out "    Sforeign_symbol(\"~a\", (void*)~a);\n" sym sym))
+      '("jerboa_last_error"
+        "jerboa_sha1" "jerboa_sha256" "jerboa_sha384" "jerboa_sha512"
+        "jerboa_random_bytes"
+        "jerboa_hmac_sha256" "jerboa_hmac_sha256_verify"
+        "jerboa_timing_safe_equal"
+        "jerboa_aead_seal" "jerboa_aead_open"
+        "jerboa_chacha20_seal" "jerboa_chacha20_open"
+        "jerboa_scrypt"
+        "jerboa_pbkdf2_derive" "jerboa_pbkdf2_verify"
+        "jerboa_x25519_generate_keypair"
+        "jerboa_x25519_public_from_private"
+        "jerboa_x25519_diffie_hellman"
+        "jerboa_hkdf_sha256"
+        "jerboa_sqlite_open" "jerboa_sqlite_close" "jerboa_sqlite_exec"
+        "jerboa_sqlite_prepare" "jerboa_sqlite_finalize" "jerboa_sqlite_reset"
+        "jerboa_sqlite_bind_int" "jerboa_sqlite_bind_double"
+        "jerboa_sqlite_bind_text" "jerboa_sqlite_bind_blob"
+        "jerboa_sqlite_bind_null"
+        "jerboa_sqlite_step"
+        "jerboa_sqlite_column_count" "jerboa_sqlite_column_type"
+        "jerboa_sqlite_column_int" "jerboa_sqlite_column_double"
+        "jerboa_sqlite_column_text" "jerboa_sqlite_column_blob"
+        "jerboa_sqlite_column_name"
+        "jerboa_sqlite_last_insert_rowid" "jerboa_sqlite_changes"
+        "jerboa_sqlite_errmsg"))
+    (display "}\n\n" out)
+
+    ;; dlopen/dlsym stubs — needed because Chez tries to use them
+    ;; even when all symbols are pre-registered
+    (display "/* dlopen/dlsym stubs for static musl builds */\n" out)
+    (display "void *dlopen(const char *file, int mode) {\n" out)
+    (display "    (void)file; (void)mode;\n" out)
+    (display "    return (void*)1; /* non-NULL = success */\n" out)
+    (display "}\n\n" out)
+
+    (display "void *dlsym(void *handle, const char *name) {\n" out)
+    (display "    (void)handle; (void)name;\n" out)
+    (display "    return NULL;\n" out)
+    (display "}\n\n" out)
+
+    (display "int dlclose(void *handle) {\n" out)
+    (display "    (void)handle;\n" out)
+    (display "    return 0;\n" out)
+    (display "}\n\n" out)
+
+    (display "char *dlerror(void) {\n" out)
+    (display "    return \"dlopen not supported in static build\";\n" out)
+    (display "}\n\n" out)
+
+    ;; memfd_create stub if not available
+    (display "#ifndef __NR_memfd_create\n" out)
+    (display "#define __NR_memfd_create 319\n" out)
+    (display "#endif\n\n" out)
+
+    ;; Main function: bootstrap Chez and run the agent
+    ;; Uses Sscheme_script for threading support (unlike Sscheme_program
+    ;; which evaluates at heap build time before threads are ready)
+    (display "int main(int argc, const char *argv[]) {\n" out)
+    (display "    Sscheme_init(NULL);\n" out)
+    (display "    register_ffi_symbols();\n\n" out)
+    (display "    Sregister_boot_file_bytes(\"petite\", petite_boot, petite_boot_len);\n" out)
+    (display "    Sregister_boot_file_bytes(\"scheme\", scheme_boot, scheme_boot_len);\n" out)
+    (display "    Sregister_boot_file_bytes(\"app\", app_boot, app_boot_len);\n\n" out)
+    (display "    Sbuild_heap(argv[0], NULL);\n\n" out)
+    ;; Write program .so to a temp memfd and load via Sscheme_script
+    ;; This allows the program to run with threading support
+    (display "    /* Load program via memfd for threading support */\n" out)
+    (display "    int fd = memfd_create(\"secmon\", MFD_CLOEXEC);\n" out)
+    (display "    if (fd < 0) { fd = memfd_create(\"secmon\", 0); }\n" out)
+    (display "    if (fd >= 0) {\n" out)
+    (display "        write(fd, program_so, program_so_len);\n" out)
+    (display "        char fdpath[64];\n" out)
+    (display "        snprintf(fdpath, sizeof(fdpath), \"/proc/self/fd/%d\", fd);\n" out)
+    (display "        Sscheme_script(fdpath);\n" out)
+    (display "        close(fd);\n" out)
+    (display "    } else {\n" out)
+    (display "        /* Fallback: write to /tmp */\n" out)
+    (display "        FILE *f = fopen(\"/tmp/.secmon-program.so\", \"wb\");\n" out)
+    (display "        if (f) {\n" out)
+    (display "            fwrite(program_so, 1, program_so_len, f);\n" out)
+    (display "            fclose(f);\n" out)
+    (display "            Sscheme_script(\"/tmp/.secmon-program.so\");\n" out)
+    (display "            unlink(\"/tmp/.secmon-program.so\");\n" out)
+    (display "        }\n" out)
+    (display "    }\n\n" out)
+    (display "    Sscheme_deinit();\n" out)
+    (display "    return 0;\n" out)
+    (display "}\n" out))
+  'replace)
+
+(printf "  Generated secmon-program.c~n")
+
+;; ========== Step 6: Compile C with musl-gcc ==========
+
+(printf "~n[6/7] Compiling C with musl-gcc...~n")
+
+(define gcc (musl-gcc-path))
+(define scheme-h-dir musl-lib-dir)
+
+(let ([cmd (format "~a -c -O2 -I'~a' -o secmon-program.o secmon-program.c" gcc scheme-h-dir)])
+  (printf "  ~a~n" cmd)
+  (unless (= (system cmd) 0)
+    (printf "ERROR: C compilation failed~n")
+    (exit 1)))
+
+;; ========== Step 7: Link static binary ==========
+
+(printf "~n[7/7] Linking static binary...~n")
+
+(define main-o (format "~a/main.o" musl-lib-dir))
+(define libkernel (format "~a/libkernel.a" musl-lib-dir))
+(define libz (let ([p (format "~a/libz.a" musl-lib-dir)])
+               (if (file-exists? p) p #f)))
+(define liblz4 (let ([p (format "~a/liblz4.a" musl-lib-dir)])
+                 (if (file-exists? p) p #f)))
+
+(let* ([libs (filter values
+               (list libkernel libz liblz4 native-lib-path))]
+       [lib-flags (apply string-append
+                    (map (lambda (l) (format " '~a'" l)) libs))]
+       [cmd (format "~a -static secmon-program.o~a -lm -lrt -lpthread -o secmon-agent"
+                    gcc lib-flags)])
+  (printf "  ~a~n" cmd)
+  (unless (= (system cmd) 0)
+    (printf "ERROR: Linking failed~n")
+    (exit 1)))
+
+;; Strip the binary
+(printf "~n  Stripping...~n")
+(system "strip secmon-agent")
+
+;; Generate integrity hash
+(printf "  Generating SHA256 hash...~n")
+(system "sha256sum secmon-agent > secmon-agent.sha256")
+
+;; Verify
+(printf "~n=== Build complete ===~n")
+(system "ls -lh secmon-agent")
+(system "file secmon-agent")
+
+;; Cleanup
+(printf "~n  Cleaning up staging directory...~n")
+(system (format "rm -rf '~a'" jerboa-stage))
+(system "rm -f secmon-program.c secmon-program.o secmon-agent.boot")
+
+(printf "~nDone.~n")
diff --git a/lib/secmon/monitor/cron.sls b/lib/secmon/monitor/cron.sls
index 5ec23e3..b758e91 100644
--- a/lib/secmon/monitor/cron.sls
+++ b/lib/secmon/monitor/cron.sls
@@ -4,7 +4,7 @@
     (chezscheme)
     (jerboa prelude clean)
     (secmon monitor events)
-    (std crypto digest)
+    (std crypto native-rust)
     (std os file-info))
 
   ;; Cron/scheduled task monitor: detects changes to cron files, systemd timers, at-jobs
@@ -60,7 +60,7 @@
         (let ([bv (get-bytevector-all p)])
           (close-port p)
           (if (eof-object? bv) ""
-            (digest->hex-string (sha256 bv)))))))
+            (bytevector->hex-string (rust-sha256 bv)))))))
 
   (define (file-mtime-safe path)
     (guard (e [#t 0])
diff --git a/lib/secmon/monitor/files.sls b/lib/secmon/monitor/files.sls
index 5ac9898..3749fc8 100644
--- a/lib/secmon/monitor/files.sls
+++ b/lib/secmon/monitor/files.sls
@@ -5,7 +5,7 @@
     (jerboa prelude clean)
     (secmon monitor events)
     (secmon monitor suspicious)
-    (std crypto digest)
+    (std crypto native-rust)
     (std os file-info))
 
   ;; File integrity monitor: detects changes to critical system files
@@ -66,7 +66,7 @@
     (guard (e [#t ""])
       (let ([content (read-file-bytes path)])
         (if (and content (< (bytevector-length content) 10485760))  ;; 10MB max
-          (digest->hex-string (sha256 content))
+          (bytevector->hex-string (rust-sha256 content))
           ""))))
 
   (define (read-file-bytes path)
diff --git a/lib/secmon/monitor/persistence.sls b/lib/secmon/monitor/persistence.sls
index 5048dc3..dfeb091 100644
--- a/lib/secmon/monitor/persistence.sls
+++ b/lib/secmon/monitor/persistence.sls
@@ -5,7 +5,7 @@
     (jerboa prelude clean)
     (secmon monitor events)
     (secmon monitor suspicious)
-    (std crypto digest))
+    (std crypto native-rust))
 
   ;; Persistence monitor: detects modifications to persistence mechanism locations
   (define (spawn-persistence-monitor emit! poll-ms hostname)
@@ -136,7 +136,7 @@
         (let ([bv (get-bytevector-all p)])
           (close-port p)
           (if (eof-object? bv) #f
-            (digest->hex-string (sha256 bv)))))))
+            (bytevector->hex-string (rust-sha256 bv)))))))
 
   (define (bytevector->hex-string bv)
     (let ([len (bytevector-length bv)])
diff --git a/lib/secmon/stealth/integrity.sls b/lib/secmon/stealth/integrity.sls
index c3cdb33..a097eed 100644
--- a/lib/secmon/stealth/integrity.sls
+++ b/lib/secmon/stealth/integrity.sls
@@ -3,7 +3,7 @@
   (import
     (chezscheme)
     (jerboa prelude clean)
-    (std crypto digest))
+    (std crypto native-rust))
 
   ;; Self-integrity checking: detect binary tampering on disk
   (define *disk-hash* (box #f))
@@ -33,7 +33,7 @@
             (let ([bv (get-bytevector-all p)])
               (close-port p)
               (if (eof-object? bv) #f
-                (sha256 bv))))))))
+                (rust-sha256 bv))))))))
 
   ;; Hash the executable code section from /proc/self/maps
   (define (hash-code-section)
@@ -70,7 +70,7 @@
             (let ([bv (get-bytevector-n p size)])
               (close-port p)
               (if (or (eof-object? bv) (not bv)) #f
-                (sha256 bv))))))))
+                (rust-sha256 bv))))))))
 
   ;; Start background integrity checker (every 60s)
   (define (start-integrity-watchdog!)