build: build jerboa-edge with installed jerbuild (no ~/mine/jerboa)

ober

0d13422cc1181982661550865b28149a6e4b0f24

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..370c20f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+# Build artifacts (jerbuild build)
+/jerboa-edge
+*-main.c
+*.so
+*.wpo
+*.boot
+petite_boot.h
+scheme_boot.h
+program_boot.h
diff --git a/.jerbuild b/.jerbuild
index 39ce38f..05bb39f 100644
--- a/.jerbuild
+++ b/.jerbuild
@@ -1,10 +1,19 @@
-;; Build jerboa-edge with a standalone jerbuild binary.
+;; Build jerboa-edge with a standalone jerbuild binary: `jerbuild build`.
+;;
+;; jerbuild bundles Chez Scheme + the jerboa stdlib + the jerboa-native Rust
+;; crate, so this needs only jerbuild + a C compiler + cargo — no jerboa
+;; source checkout. jerboa-native provides rustls TLS, crypto, and
+;; sqlite-native (no DuckDB).
 
 (entry "edge.ss")
 (output "jerboa-edge")
-(requires "cc" "cargo" "bundled jerboa-native-rs")
-(notes "Requires jerboa-native-rs for rustls TLS, crypto, and sqlite-native. DuckDB is not required.")
 (libdirs ".")
+;; Static binary: a custom main.c sets JERBOA_STATIC=1 so the bundled std
+;; modules skip dlopen of libjerboa_native and use the symbols registered from
+;; the list below (the linked-in archive members would otherwise be
+;; dead-stripped / unreachable via dlsym).
+(main-c "support/static-main.c")
+(ffi-symbols "support/ffi-symbols.list")
 (rust-crates
   ("@bundle/jerboa-native-rs/Cargo.toml"
    features: "tls,crypto,sqlite"
diff --git a/Makefile b/Makefile
index b53fdac..ba36d4a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,71 +1,37 @@
-JERBOA_HOME ?= $(HOME)/mine/jerboa
-SCHEME = scheme --libdirs $(JERBOA_HOME)/lib
-NATIVE = $(JERBOA_HOME)/jerboa-native-rs/target/release
-DOCKER_IMAGE = jerboa21/jerboa
-
-.PHONY: run run-db run-tls test clean edge-static
-
-## Run the webhook service on port 8080
-run:
-	LD_LIBRARY_PATH=$(NATIVE) $(SCHEME) --script edge.ss
-
-## Run with custom port and workers
-## Example: make run-custom PORT=9090 WORKERS=8
-run-custom:
-	LD_LIBRARY_PATH=$(NATIVE) \
-	  EDGE_PORT=$(or $(PORT),8080) \
-	  EDGE_WORKERS=$(or $(WORKERS),4) \
-	  EDGE_SECRET=$(or $(SECRET),) \
-	  $(SCHEME) --script edge.ss
-
-## Run with SQLite persistence (events survive restart)
-## Example: make run-db DB=/var/lib/edge/events.db
-run-db:
-	LD_LIBRARY_PATH=$(NATIVE) \
-	  EDGE_DB_PATH=$(or $(DB),/tmp/edge.db) \
-	  $(SCHEME) --script edge.ss
-
-## Run with TLS enabled (requires cert + key PEM files)
-## Example: make run-tls CERT=tls/cert.pem KEY=tls/key.pem
-run-tls:
-	LD_LIBRARY_PATH=$(NATIVE) \
-	  EDGE_TLS_CERT=$(or $(CERT),tls/cert.pem) \
-	  EDGE_TLS_KEY=$(or $(KEY),tls/key.pem) \
-	  EDGE_TLS_PORT=$(or $(TLS_PORT),8443) \
-	  $(SCHEME) --script edge.ss
-
-## Build a self-contained static Linux binary via Docker (Phase 3.1)
-## Produces ./edge — runs on any x86_64 Linux with zero dependencies.
-## Binary size target: <25MB
-## Requires Docker and the jerboa21/jerboa base image:
-##   docker pull jerboa21/jerboa   (or: make -C $(JERBOA_HOME) docker-build)
-edge-static:
-	@echo "=== Building static edge binary ==="
-	docker run --rm \
-	  -v $(JERBOA_HOME):/build/mine/jerboa:ro \
-	  -v $(PWD):/build/edge \
-	  -w /build/edge \
-	  $(DOCKER_IMAGE) \
-	  /build/mine/jerboa/support/build-static-script.sh edge.ss edge
-	@echo "=== Built: ./edge ($$(du -sh edge | cut -f1)) ==="
-	@echo "Run: LD_LIBRARY_PATH= ./edge"
-
-## Run smoke tests against a running server
-test:
-	./test-edge.sh
-
-## Run with HMAC enabled for signature tests
-test-hmac:
-	EDGE_SECRET=test-secret ./test-edge.sh
-
-## Benchmark with wrk (requires wrk)
-bench:
-	./bench-edge.sh
-
-## Syntax check (no server needed)
-check:
-	LD_LIBRARY_PATH=$(NATIVE) $(SCHEME) --compile-imported-libraries --script /dev/null 2>&1 || true
-	@echo "Syntax OK"
+# jerbuild bundles Chez Scheme + the jerboa stdlib + the jerboa-native Rust
+# crate, so building jerboa-edge needs only `jerbuild`, a C compiler, and
+# cargo — no jerboa source checkout and no separately-built Chez/native lib.
+JERBUILD ?= jerbuild
+JH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null)
+ifeq ($(JH),)
+$(error jerbuild not found on PATH (or '$(JERBUILD) --jerboa-home' failed). Install jerbuild, or set JERBUILD=/path/to/jerbuild)
+endif
+
+NATIVE_A := $(JH)/jerboa-native-rs/target/release/libjerboa_native.a
+BIN      := jerboa-edge
+BIN_DIR  := $(HOME)/.local/bin
+
+.PHONY: all build binary install clean
+
+all: binary
+
+# Standalone native binary via .jerbuild (entry edge.ss -> jerboa-edge).
+# Two passes: the first cargo-builds jerboa-native into jerbuild's cache; we
+# then regenerate the FFI symbol list from that archive (so it matches this
+# platform — e.g. epoll on Linux) and relink. A static main.c sets
+# JERBOA_STATIC=1 so the std modules use the registered symbols, not dlopen.
+binary:
+	@test -f "$(NATIVE_A)" || $(JERBUILD) build
+	sh support/gen-ffi-symbols.sh
+	$(JERBUILD) build
+
+build: binary
+
+install: binary
+	mkdir -p $(BIN_DIR)
+	install -m 0755 $(BIN) $(BIN_DIR)/$(BIN)
+	@echo "Installed $(BIN) to $(BIN_DIR)/$(BIN)"
 
 clean:
-	@echo "Nothing to clean (single-file service)"
+	rm -f $(BIN) $(BIN)-main.c
+	find . \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null || true
diff --git a/edge.ss b/edge.ss
index 8aaec77..d8b57d5 100644
--- a/edge.ss
+++ b/edge.ss
@@ -1,4 +1,4 @@
-#!/usr/bin/env -S scheme --libdirs lib --script
+#!chezscheme
 ;;; Jerboa Edge — Webhook Processing Service
 ;;;
 ;;; A single-file, production-grade webhook processor demonstrating:
diff --git a/support/ffi-symbols.list b/support/ffi-symbols.list
new file mode 100644
index 0000000..f9df1b3
--- /dev/null
+++ b/support/ffi-symbols.list
@@ -0,0 +1,115 @@
+# jerboa-native FFI symbols registered via Sforeign_symbol() (see .jerbuild).
+# AUTOGENERATED by support/gen-ffi-symbols.sh from libjerboa_native.a.
+
+jerboa_aead_open
+jerboa_aead_seal
+jerboa_antidebug_check_all
+jerboa_antidebug_check_breakpoint
+jerboa_antidebug_check_ld_preload
+jerboa_antidebug_check_tracer
+jerboa_antidebug_ptrace
+jerboa_antidebug_timing_check
+jerboa_aproc_close
+jerboa_aproc_dup
+jerboa_aproc_killpg
+jerboa_aproc_set_nonblock
+jerboa_aproc_spawn
+jerboa_aproc_spawn_pty
+jerboa_aproc_wait4
+jerboa_argon2id_hash
+jerboa_argon2id_verify
+jerboa_chacha20_open
+jerboa_chacha20_seal
+jerboa_deflate
+jerboa_freebsd_is_traced
+jerboa_freebsd_process_count
+jerboa_gunzip
+jerboa_gzip
+jerboa_hkdf_sha256
+jerboa_hmac_sha256
+jerboa_hmac_sha256_verify
+jerboa_inflate
+jerboa_integrity_hash_file
+jerboa_integrity_hash_region
+jerboa_integrity_hash_self
+jerboa_integrity_sign_verify
+jerboa_integrity_verify_hash
+jerboa_kill_probe
+jerboa_last_error
+jerboa_md5
+jerboa_mlockall
+jerboa_pbkdf2_derive
+jerboa_pbkdf2_verify
+jerboa_prctl_set_name
+jerboa_proc_self_exe
+jerboa_random_bytes
+jerboa_regex_captures
+jerboa_regex_compile
+jerboa_regex_compile_ex
+jerboa_regex_find
+jerboa_regex_find_at
+jerboa_regex_free
+jerboa_regex_group_count
+jerboa_regex_is_match
+jerboa_regex_replace_all
+jerboa_scrypt
+jerboa_secure_alloc
+jerboa_secure_free
+jerboa_secure_random_fill
+jerboa_secure_wipe
+jerboa_setproctitle
+jerboa_sha1
+jerboa_sha256
+jerboa_sha384
+jerboa_sha512
+jerboa_socks5_server_port
+jerboa_socks5_server_start
+jerboa_socks5_server_stats
+jerboa_socks5_server_stop
+jerboa_sqlite_bind_blob
+jerboa_sqlite_bind_double
+jerboa_sqlite_bind_int
+jerboa_sqlite_bind_null
+jerboa_sqlite_bind_text
+jerboa_sqlite_changes
+jerboa_sqlite_close
+jerboa_sqlite_column_blob
+jerboa_sqlite_column_count
+jerboa_sqlite_column_double
+jerboa_sqlite_column_int
+jerboa_sqlite_column_name
+jerboa_sqlite_column_text
+jerboa_sqlite_column_type
+jerboa_sqlite_errmsg
+jerboa_sqlite_exec
+jerboa_sqlite_finalize
+jerboa_sqlite_last_insert_rowid
+jerboa_sqlite_open
+jerboa_sqlite_prepare
+jerboa_sqlite_reset
+jerboa_sqlite_step
+jerboa_timing_safe_equal
+jerboa_tls_accept
+jerboa_tls_close
+jerboa_tls_connect
+jerboa_tls_connect_mtls
+jerboa_tls_connect_mtls_mem
+jerboa_tls_connect_mtls_pem_ca
+jerboa_tls_connect_pinned
+jerboa_tls_flush
+jerboa_tls_get_fd
+jerboa_tls_read
+jerboa_tls_server_free
+jerboa_tls_server_new
+jerboa_tls_server_new_mtls
+jerboa_tls_server_new_mtls_pem
+jerboa_tls_server_new_pem
+jerboa_tls_set_nonblock
+jerboa_tls_write
+jerboa_x25519_diffie_hellman
+jerboa_x25519_generate_keypair
+jerboa_x25519_public_from_private
+jerboa_x509_cert_fingerprint
+jerboa_x509_generate_self_signed
+jerboa_x509_generate_self_signed_mem
+jerboa_x509_generate_signed_by_ca_mem
diff --git a/support/gen-ffi-symbols.sh b/support/gen-ffi-symbols.sh
new file mode 100755
index 0000000..0902ab6
--- /dev/null
+++ b/support/gen-ffi-symbols.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Regenerate support/ffi-symbols.list from the jerboa-native archive that
+# jerbuild cargo-builds into its bundle cache. Run before the final
+# `jerbuild build` so the static binary registers exactly the jerboa_* symbols
+# present for THIS platform (e.g. epoll on Linux, kqueue on macOS).
+set -e
+JH="$(jerbuild --jerboa-home 2>/dev/null)"
+A="$JH/jerboa-native-rs/target/release/libjerboa_native.a"
+OUT="$(dirname "$0")/ffi-symbols.list"
+if [ ! -f "$A" ]; then
+  echo "gen-ffi-symbols: $A not found (run 'jerbuild build' once first)" >&2
+  exit 0
+fi
+{
+  echo "# jerboa-native FFI symbols registered via Sforeign_symbol() (see .jerbuild)."
+  echo "# AUTOGENERATED by support/gen-ffi-symbols.sh from libjerboa_native.a."
+  echo ""
+  nm -gjU "$A" 2>/dev/null | sed 's/^_//' | grep -E '^jerboa_[A-Za-z0-9_]+$' | sort -u
+} > "$OUT"
+echo "gen-ffi-symbols: wrote $(grep -c '^jerboa_' "$OUT") symbols to $OUT"