Fix build: port from Gerbil to proper Jerboa/Chez Scheme conventions

ober

91d53156513c5ba18b68ffb7721fb27767716286

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8e3abad
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.so
+*.wpo
+keys/
diff --git a/Makefile b/Makefile
index 0ac9b61..22d46d9 100644
--- a/Makefile
+++ b/Makefile
@@ -1,31 +1,34 @@
-JERBOA_DIR ?= ../jerboa
-GERBIL_LOADPATH := $(JERBOA_DIR)/lib:$(CURDIR)/lib
-export GERBIL_LOADPATH
+SCHEME ?= scheme
+JERBOA ?= $(HOME)/mine/jerboa/lib
+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
 
 build:
-	gerbil build
+	$(SCHEME) -q --libdirs $(LIBDIRS) --compile-imported-libraries < build-all.ss
 
 clean:
-	gerbil build clean
+	find lib -name '*.so' -delete
+	find lib -name '*.wpo' -delete
 
 keygen:
-	gxi bin/keygen.ss
+	$(SCHEME) --libdirs $(LIBDIRS) --script bin/keygen.ss
 
 agent:
-	gxi bin/agent.ss
+	$(SCHEME) --libdirs $(LIBDIRS) --script bin/agent.ss
 
 collector:
-	gxi bin/collector.ss $(ARGS)
+	$(SCHEME) --libdirs $(LIBDIRS) --script bin/collector.ss $(ARGS)
 
 analyze:
-	gxi bin/analyze.ss $(ARGS)
+	$(SCHEME) --libdirs $(LIBDIRS) --script bin/analyze.ss $(ARGS)
 
 test:
 	@echo "Running tests..."
 	@for f in tests/*-test.ss; do \
 		echo "Testing $$f..."; \
-		gxi $$f || exit 1; \
+		$(SCHEME) --libdirs $(LIBDIRS) --script $$f || exit 1; \
 	done
 	@echo "All tests passed."
diff --git a/bin/agent.ss b/bin/agent.ss
index 1cb7db5..10da94a 100644
--- a/bin/agent.ss
+++ b/bin/agent.ss
@@ -1,8 +1,8 @@
-#!/usr/bin/env gxi
+#!chezscheme
 ;; secmon-agent: Security monitoring agent
 (import
   (chezscheme)
-  (jerboa prelude)
+  (jerboa prelude clean)
   (secmon config)
   (secmon crypto ecies)
   (secmon crypto psk)
@@ -30,7 +30,7 @@
   (secmon stealth init)
   (std text hex))
 
-(def (main . args)
+(define (main . args)
   (let ([debug-mode (and (getenv "SECMON_DEBUG") #t)])
 
     ;; Stealth init (unless debug mode)
@@ -110,3 +110,5 @@
           (let ([server (make-poll-server psk-auth buffer
                           (agent-config-listen-addr config))])
             (poll-server-run! server)))))))
+
+(apply main (cdr (command-line)))
diff --git a/bin/analyze.ss b/bin/analyze.ss
index 2208a3d..a3740c0 100644
--- a/bin/analyze.ss
+++ b/bin/analyze.ss
@@ -1,48 +1,19 @@
-#!/usr/bin/env gxi
+#!chezscheme
 ;; secmon-analyze: Offline event analysis and detection
 (import
   (chezscheme)
-  (jerboa prelude)
+  (jerboa prelude clean)
   (secmon storage store)
   (std text json))
 
-(def (main . args)
-  (let ([cmd (and (not (null? args)) (car args))]
-        [rest (if (null? args) '() (cdr args))])
-    (case (and cmd (string->symbol cmd))
-      [(summary)   (run-summary rest)]
-      [(query)     (run-query rest)]
-      [(timeline)  (run-timeline rest)]
-      [(ioc)       (run-ioc rest)]
-      [(entity)    (run-entity rest)]
-      [(detect)    (run-detect rest)]
-      [(hosts)     (run-hosts rest)]
-      [(retention) (run-retention rest)]
-      [else
-       (displayln "Usage: secmon-analyze <command> <events.db> [options]")
-       (displayln "")
-       (displayln "Commands:")
-       (displayln "  summary   <db>                       Database summary")
-       (displayln "  query     <db> [filters]              Query events")
-       (displayln "  timeline  <db> --host <name>          Host timeline")
-       (displayln "  ioc       <db> --list <file>          IOC matching")
-       (displayln "  entity    <db> <type> <value>         Entity timeline")
-       (displayln "  detect    <db> [--rule name]          Run detection rules")
-       (displayln "  hosts     <db>                        List known hosts")
-       (displayln "  retention <db> --delete-before <time> Delete old events")
-       (displayln "")
-       (displayln "Filters: --host, --type, --severity, --since, --until,")
-       (displayln "         --pid, --process, --search, --limit, --format json")
-       (exit 1)])))
-
 ;; --- Argument parsing ---
 
-(def (get-db-path args)
+(define (get-db-path args)
   (if (null? args)
     (begin (displayln "ERROR: database path required") (exit 1))
     (car args)))
 
-(def (get-flag args flag)
+(define (get-flag args flag)
   (let loop ([as args])
     (cond
       [(null? as) #f]
@@ -50,55 +21,53 @@
        (if (null? (cdr as)) #f (cadr as))]
       [else (loop (cdr as))])))
 
-(def (has-flag? args flag)
+(define (has-flag? args flag)
   (member flag args))
 
-(def (parse-filter-args args)
-  ;; Returns keyword arg list for query-events
+(define (parse-filter-args args)
   (let ([result '()])
     (let loop ([as args])
       (cond
         [(null? as) result]
         [(equal? (car as) "--host")
          (when (not (null? (cdr as)))
-           (set! result (append result (list host: (cadr as))))
+           (set! result (append result (list 'host: (cadr as))))
            (loop (cddr as)))]
         [(equal? (car as) "--type")
          (when (not (null? (cdr as)))
-           (set! result (append result (list type: (cadr as))))
+           (set! result (append result (list 'type: (cadr as))))
            (loop (cddr as)))]
         [(equal? (car as) "--severity")
          (when (not (null? (cdr as)))
-           (set! result (append result (list severity: (cadr as))))
+           (set! result (append result (list 'severity: (cadr as))))
            (loop (cddr as)))]
         [(equal? (car as) "--since")
          (when (not (null? (cdr as)))
-           (set! result (append result (list since-ms: (parse-time-arg (cadr as)))))
+           (set! result (append result (list 'since-ms: (parse-time-arg (cadr as)))))
            (loop (cddr as)))]
         [(equal? (car as) "--until")
          (when (not (null? (cdr as)))
-           (set! result (append result (list until-ms: (parse-time-arg (cadr as)))))
+           (set! result (append result (list 'until-ms: (parse-time-arg (cadr as)))))
            (loop (cddr as)))]
         [(equal? (car as) "--pid")
          (when (not (null? (cdr as)))
-           (set! result (append result (list pid: (string->number (cadr as)))))
+           (set! result (append result (list 'pid: (string->number (cadr as)))))
            (loop (cddr as)))]
         [(equal? (car as) "--process")
          (when (not (null? (cdr as)))
-           (set! result (append result (list process: (cadr as))))
+           (set! result (append result (list 'process: (cadr as))))
            (loop (cddr as)))]
         [(equal? (car as) "--search")
          (when (not (null? (cdr as)))
-           (set! result (append result (list search: (cadr as))))
+           (set! result (append result (list 'search: (cadr as))))
            (loop (cddr as)))]
         [(equal? (car as) "--limit")
          (when (not (null? (cdr as)))
-           (set! result (append result (list limit: (string->number (cadr as)))))
+           (set! result (append result (list 'limit: (string->number (cadr as)))))
            (loop (cddr as)))]
         [else (loop (cdr as))]))))
 
-(def (parse-time-arg s)
-  ;; Parse "24h", "7d", "30d" or epoch ms
+(define (parse-time-arg s)
   (let ([now (current-time-ms)])
     (cond
       [(string-suffix? "h" s)
@@ -112,7 +81,7 @@
 
 ;; --- Commands ---
 
-(def (run-summary args)
+(define (run-summary args)
   (let* ([db (get-db-path args)]
          [store (open-event-store db)]
          [summary (store-summary store)])
@@ -149,7 +118,7 @@
           (format-ts (cdr (assoc "min_ts" tr)))
           (format-ts (cdr (assoc "max_ts" tr)))))))))
 
-(def (run-query args)
+(define (run-query args)
   (let* ([db (get-db-path args)]
          [rest (cdr args)]
          [store (open-event-store db)]
@@ -160,7 +129,7 @@
       (lambda (row) (print-row row format-json?))
       rows)))
 
-(def (run-timeline args)
+(define (run-timeline args)
   (let* ([db (get-db-path args)]
          [rest (cdr args)]
          [host (get-flag rest "--host")]
@@ -170,12 +139,12 @@
     (unless host
       (displayln "ERROR: --host required for timeline")
       (exit 1))
-    (let ([rows (apply query-events store host: host filters)])
+    (let ([rows (apply query-events store 'host: host filters)])
       (for-each
         (lambda (row) (print-row row format-json?))
         (reverse rows)))))
 
-(def (run-ioc args)
+(define (run-ioc args)
   (let* ([db (get-db-path args)]
          [rest (cdr args)]
          [ioc-file (get-flag rest "--list")]
@@ -201,8 +170,7 @@
                 matches))))
         iocs))))
 
-(def (load-ioc-file path)
-  ;; Returns list of (value . type)
+(define (load-ioc-file path)
   (guard (e [#t (displayln (format "ERROR: cannot read ~a" path)) '()])
     (let ([lines (filter
                    (lambda (line)
@@ -218,34 +186,31 @@
             (cons val (auto-detect-ioc-type val))))
         lines))))
 
-(def (auto-detect-ioc-type val)
+(define (auto-detect-ioc-type val)
   (cond
-    ;; IPv4
     [(and (string-contains val ".")
           (let ([parts (string-split val #\.)])
             (and (= (length parts) 4)
                  (andmap (lambda (p) (string->number p)) parts))))
      'ip]
-    ;; Hex hash (32=MD5, 40=SHA1, 64=SHA256)
     [(and (memv (string-length val) '(32 40 64))
-          (string-every (lambda (c) (or (char-numeric? c)
-                                        (memv c '(#\a #\b #\c #\d #\e #\f)))) val))
+          (string-every-char
+            (lambda (c) (or (char-numeric? c)
+                            (memv c '(#\a #\b #\c #\d #\e #\f)))) val))
      'hash]
-    ;; Domain (contains dot, no spaces)
     [(and (string-contains val ".")
           (not (string-contains val " ")))
      'domain]
-    ;; Default to process
     [else 'process]))
 
-(def (string-every pred s)
+(define (string-every-char pred s)
   (let loop ([i 0])
     (if (= i (string-length s)) #t
       (if (pred (string-ref s i))
         (loop (+ i 1))
         #f))))
 
-(def (run-entity args)
+(define (run-entity args)
   (when (< (length args) 3)
     (displayln "Usage: secmon-analyze entity <db> <type> <value> [filters]")
     (exit 1))
@@ -262,7 +227,7 @@
       (lambda (row) (print-row row format-json?))
       rows)))
 
-(def detection-rules
+(define detection-rules
   '((brute_force . "5+ auth failures for same user in 10min")
     (credential_stuffing . "5+ distinct usernames failing from same source in 10min")
     (dns_tunnel . "50+ DNS queries from same process in 5min")
@@ -274,7 +239,7 @@
     (log_cover . "Critical event -> log tampering within 1h")
     (lateral_after_shell . "Reverse shell -> lateral movement within 1h")))
 
-(def (run-detect args)
+(define (run-detect args)
   (let* ([db (get-db-path args)]
          [rest (cdr args)]
          [store (open-event-store db)]
@@ -293,21 +258,21 @@
          detection-rules)]
       [rule-name
        (let ([results (run-detection-rules store
-                        (string->symbol rule-name) since-ms: since-ms)])
+                        (string->symbol rule-name) 'since-ms: since-ms)])
          (displayln (format "--- Rule: ~a - ~a hits ---"
            rule-name (length results)))
          (for-each
            (lambda (row) (print-detection-row row format-json?))
            results))]
       [else
-       (let ([results (run-detection-rules store 'all since-ms: since-ms)])
+       (let ([results (run-detection-rules store 'all 'since-ms: since-ms)])
          (displayln (format "--- All rules - ~a total hits ---"
            (length results)))
          (for-each
            (lambda (row) (print-detection-row row format-json?))
            results))])))
 
-(def (run-hosts args)
+(define (run-hosts args)
   (let* ([db (get-db-path args)]
          [store (open-event-store db)]
          [hosts (store-list-hosts store)])
@@ -321,7 +286,7 @@
           (cdr (assoc "hostname" row)))))
       hosts)))
 
-(def (run-retention args)
+(define (run-retention args)
   (let* ([db (get-db-path args)]
          [rest (cdr args)]
          [before (get-flag rest "--delete-before")]
@@ -336,7 +301,7 @@
 
 ;; --- Output helpers ---
 
-(def (print-row row format-json?)
+(define (print-row row format-json?)
   (if format-json?
     (displayln (row->json row))
     (begin
@@ -351,12 +316,12 @@
       (display (cdr (assoc "summary" row)))
       (newline))))
 
-(def (print-detection-row row format-json?)
+(define (print-detection-row row format-json?)
   (if format-json?
     (displayln (row->json row))
     (displayln (format "  ~a" row))))
 
-(def (row->json row)
+(define (row->json row)
   (let ([ht (make-hashtable string-hash string=?)])
     (for-each
       (lambda (pair)
@@ -364,14 +329,14 @@
       row)
     (json-object->string ht)))
 
-(def (severity-marker sev)
+(define (severity-marker sev)
   (cond
     [(equal? sev "critical") "!!!"]
     [(equal? sev "high")     "!! "]
     [(equal? sev "medium")   "!  "]
     [else                    "   "]))
 
-(def (format-ts ms)
+(define (format-ts ms)
   (guard (e [#t (format "~a" ms)])
     (let* ([secs (quotient ms 1000)]
            [t (make-time 'time-utc 0 secs)]
@@ -380,7 +345,40 @@
         (date-year d) (date-month d) (date-day d)
         (date-hour d) (date-minute d) (date-second d)))))
 
-(def (current-time-ms)
+(define (current-time-ms)
   (let ([t (current-time)])
     (+ (* (time-second t) 1000)
        (quotient (time-nanosecond t) 1000000))))
+
+;; --- Main ---
+
+(define (main . args)
+  (let ([cmd (and (not (null? args)) (car args))]
+        [rest (if (null? args) '() (cdr args))])
+    (case (and cmd (string->symbol cmd))
+      [(summary)   (run-summary rest)]
+      [(query)     (run-query rest)]
+      [(timeline)  (run-timeline rest)]
+      [(ioc)       (run-ioc rest)]
+      [(entity)    (run-entity rest)]
+      [(detect)    (run-detect rest)]
+      [(hosts)     (run-hosts rest)]
+      [(retention) (run-retention rest)]
+      [else
+       (displayln "Usage: secmon-analyze <command> <events.db> [options]")
+       (displayln "")
+       (displayln "Commands:")
+       (displayln "  summary   <db>                       Database summary")
+       (displayln "  query     <db> [filters]              Query events")
+       (displayln "  timeline  <db> --host <name>          Host timeline")
+       (displayln "  ioc       <db> --list <file>          IOC matching")
+       (displayln "  entity    <db> <type> <value>         Entity timeline")
+       (displayln "  detect    <db> [--rule name]          Run detection rules")
+       (displayln "  hosts     <db>                        List known hosts")
+       (displayln "  retention <db> --delete-before <time> Delete old events")
+       (displayln "")
+       (displayln "Filters: --host, --type, --severity, --since, --until,")
+       (displayln "         --pid, --process, --search, --limit, --format json")
+       (exit 1)])))
+
+(apply main (cdr (command-line)))
diff --git a/bin/collector.ss b/bin/collector.ss
index 5677e37..e0c514c 100644
--- a/bin/collector.ss
+++ b/bin/collector.ss
@@ -1,8 +1,8 @@
-#!/usr/bin/env gxi
+#!chezscheme
 ;; secmon-collector: Collect and decrypt events from remote agents
 (import
   (chezscheme)
-  (jerboa prelude)
+  (jerboa prelude clean)
   (secmon config)
   (secmon crypto ecies)
   (secmon crypto psk)
@@ -12,25 +12,9 @@
   (std text json)
   (std text hex))
 
-(def (main . args)
-  (let ([cmd (and (not (null? args)) (car args))]
-        [rest (if (null? args) '() (cdr args))])
-    (case (and cmd (string->symbol cmd))
-      [(watch)   (run-watch rest)]
-      [(poll)    (run-poll rest)]
-      [(status)  (run-status rest)]
-      [else
-       (displayln "Usage: secmon-collector <command> [options]")
-       (displayln "")
-       (displayln "Commands:")
-       (displayln "  watch  <host:port...> [--format json] [--db path]")
-       (displayln "  poll   <host:port> [--after seq] [--format json]")
-       (displayln "  status <host:port>")
-       (exit 1)])))
-
 ;; --- Key loading ---
 
-(def (load-keys)
+(define (load-keys)
   (let-values ([(private-key psk) (load-collector-config)])
     (unless (and private-key (= (bytevector-length private-key) 32))
       (displayln "ERROR: Missing or invalid private key")
@@ -44,10 +28,10 @@
 
 ;; --- TCP client ---
 
-(def (connect-to-agent host port)
+(define (connect-to-agent host port)
   (let ([c-socket (foreign-procedure "socket" (int int int) int)]
         [c-connect (foreign-procedure "connect" (int u8* int) int)])
-    (let ([fd (c-socket 2 1 0)])  ;; AF_INET SOCK_STREAM
+    (let ([fd (c-socket 2 1 0)])
       (when (< fd 0)
         (error 'connect "socket() failed"))
       (let ([addr (make-connect-addr host port)])
@@ -57,11 +41,10 @@
           (values (open-fd-input-port fd)
                   (open-fd-output-port fd)))))))
 
-(def (make-connect-addr host port)
-  ;; struct sockaddr_in
+(define (make-connect-addr host port)
   (let ([addr (make-bytevector 16 0)]
         [ip-parts (map string->number (string-split host #\.))])
-    (bytevector-u16-set! addr 0 2 (endianness native))  ;; AF_INET
+    (bytevector-u16-set! addr 0 2 (endianness little))
     (bytevector-u16-set! addr 2 port (endianness big))
     (when (= (length ip-parts) 4)
       (bytevector-u8-set! addr 4 (list-ref ip-parts 0))
@@ -70,16 +53,16 @@
       (bytevector-u8-set! addr 7 (list-ref ip-parts 3)))
     addr))
 
-;; --- Transport I/O (same as server side) ---
+;; --- Transport I/O ---
 
-(def (send-msg! port psk-auth payload)
+(define (send-msg! port psk-auth payload)
   (let* ([encrypted (psk-encrypt-transport psk-auth payload)]
          [len-bytes (pack-u32-le (bytevector-length encrypted))])
     (put-bytevector port len-bytes)
     (put-bytevector port encrypted)
     (flush-output-port port)))
 
-(def (recv-msg! port psk-auth)
+(define (recv-msg! port psk-auth)
   (guard (e [#t #f])
     (let* ([len-bv (get-bytevector-n port 4)]
            [len (unpack-u32-le len-bv 0)])
@@ -90,28 +73,24 @@
 
 ;; --- Authenticate to agent ---
 
-(def (authenticate! in-port out-port psk-auth)
-  ;; 1. Receive challenge
+(define (authenticate! in-port out-port psk-auth)
   (let ([challenge-msg (recv-msg! in-port psk-auth)])
     (unless challenge-msg
       (error 'authenticate "no challenge received"))
     (let ([tag (bytevector-u8-ref challenge-msg 0)])
       (unless (= tag MSG-CHALLENGE)
         (error 'authenticate "unexpected message" tag))
-      ;; Parse challenge
       (let* ([challenge-data (make-bytevector (- (bytevector-length challenge-msg) 1))]
              [_ (bytevector-copy! challenge-msg 1 challenge-data 0
                    (bytevector-length challenge-data))]
              [challenge (bytevector->psk-challenge challenge-data)]
-             ;; 2. Respond
              [response (psk-respond-to-challenge psk-auth challenge)]
              [resp-bv (psk-response->bytevector response)]
              [msg (bv-tag MSG-CHALLENGE-RESPONSE resp-bv)])
         (send-msg! out-port psk-auth msg)
-        ;; 3. Wait for auth result or first request
         #t))))
 
-(def (bv-tag tag . parts)
+(define (bv-tag tag . parts)
   (let* ([total (+ 1 (apply + (map bytevector-length parts)))]
          [out (make-bytevector total)])
     (bytevector-u8-set! out 0 tag)
@@ -123,15 +102,13 @@
 
 ;; --- Send request and get response ---
 
-(def (send-request! in-port out-port psk-auth req-tag . data-bvs)
+(define (send-request! in-port out-port psk-auth req-tag . data-bvs)
   (let ([request (apply bv-tag MSG-REQUEST
                    (make-bytevector 1 req-tag) data-bvs)])
     (send-msg! out-port psk-auth request)
-    ;; Get response
     (let ([resp (recv-msg! in-port psk-auth)])
       (unless resp
         (error 'send-request "no response"))
-      ;; Strip MSG-RESPONSE tag, parse response
       (let ([tag (bytevector-u8-ref resp 0)])
         (cond
           [(= tag MSG-RESPONSE)
@@ -145,7 +122,7 @@
 
 ;; --- Event decryption ---
 
-(def (decrypt-event decryptor stored-ev)
+(define (decrypt-event decryptor stored-ev)
   (guard (e [#t #f])
     (let* ([enc-bv (stored-event-encrypted-data stored-ev)]
            [enc-payload (bytevector->encrypted-payload enc-bv)]
@@ -154,7 +131,7 @@
 
 ;; --- Print event ---
 
-(def (print-event ev format-json?)
+(define (print-event ev format-json?)
   (if format-json?
     (displayln (json-object->string (security-event->json ev)))
     (begin
@@ -169,8 +146,7 @@
       (print-event-body ev)
       (newline))))
 
-(def (format-timestamp ms)
-  ;; Simple timestamp format
+(define (format-timestamp ms)
   (let* ([secs (quotient ms 1000)]
          [t (make-time 'time-utc 0 secs)]
          [d (time-utc->date t 0)])
@@ -178,14 +154,14 @@
       (date-year d) (date-month d) (date-day d)
       (date-hour d) (date-minute d) (date-second d))))
 
-(def (severity-marker sev)
+(define (severity-marker sev)
   (case sev
     [(critical) "!!!"]
     [(high)     "!! "]
     [(medium)   "!  "]
     [else       "   "]))
 
-(def (print-event-body ev)
+(define (print-event-body ev)
   (let ([data (security-event-data ev)]
         [type (security-event-event-type ev)])
     (case type
@@ -261,7 +237,7 @@
       [else
        (display (json-object->string data))])))
 
-(def (ht-ref ht key . default)
+(define (ht-ref ht key . default)
   (let ([d (if (null? default) "" (car default))])
     (if (hashtable? ht)
       (hashtable-ref ht key d)
@@ -269,13 +245,13 @@
 
 ;; --- Commands ---
 
-(def (parse-host-port str)
+(define (parse-host-port str)
   (let ([parts (string-split str #\:)])
     (if (>= (length parts) 2)
       (values (car parts) (or (string->number (cadr parts)) 31337))
       (values str 31337))))
 
-(def (run-status args)
+(define (run-status args)
   (when (null? args)
     (displayln "Usage: secmon-collector status <host:port>")
     (exit 1))
@@ -291,11 +267,10 @@
           (close-port in)
           (close-port out))))))
 
-(def (run-poll args)
+(define (run-poll args)
   (let ([format-json? (member "--format" args)]
         [after-seq 0]
         [host-port #f])
-    ;; Parse args
     (let loop ([as args])
       (cond
         [(null? as) (void)]
@@ -328,11 +303,10 @@
             (close-port in)
             (close-port out)))))))
 
-(def (run-watch args)
+(define (run-watch args)
   (let ([format-json? (member "--format" args)]
         [db-path #f]
         [hosts '()])
-    ;; Parse args
     (let loop ([as args])
       (cond
         [(null? as) (void)]
@@ -353,7 +327,6 @@
       (let ([psk-auth (make-psk-auth psk)]
             [decryptor (make-ecies-decryptor private-key)]
             [store (and db-path (open-event-store db-path))])
-        ;; Spawn watcher thread per host
         (for-each
           (lambda (host-str)
             (fork-thread
@@ -365,21 +338,19 @@
           (sleep (make-time 'time-duration 0 3600))
           (loop))))))
 
-(def (watch-host host-str psk-auth decryptor store format-json?)
+(define (watch-host host-str psk-auth decryptor store format-json?)
   (let-values ([(host port) (parse-host-port host-str)])
     (let ([last-seq (box 0)]
           [backoff (box 1)])
       (let loop ()
         (guard (e [#t
-                   ;; Exponential backoff on error
                    (let ([wait (unbox backoff)])
                      (sleep (make-time 'time-duration 0 wait))
                      (set-box! backoff (min 30 (* wait 2)))
                      (loop))])
           (let-values ([(in out) (connect-to-agent host port)])
             (authenticate! in out psk-auth)
-            (set-box! backoff 1)  ;; Reset backoff on successful connect
-            ;; Continuous poll loop
+            (set-box! backoff 1)
             (let poll-loop ()
               (let ([resp (send-request! in out psk-auth
                             REQ-GET-EVENTS-AFTER
@@ -390,7 +361,6 @@
                       (let ([ev (decrypt-event decryptor stored-ev)])
                         (when ev
                           (print-event ev format-json?)
-                          ;; Store to DB if configured
                           (when store
                             (store-decrypted-event! store stored-ev ev host-str)))
                         (set-box! last-seq (stored-event-seq stored-ev))))
@@ -398,7 +368,7 @@
               (sleep (make-time 'time-duration 0 1))
               (poll-loop))))))))
 
-(def (store-decrypted-event! store stored-ev ev host-str)
+(define (store-decrypted-event! store stored-ev ev host-str)
   (guard (e [#t (void)])
     (let ([data (security-event-data ev)])
       (store-event! store
@@ -416,7 +386,7 @@
         (stored-event-seq stored-ev)
         (security-event-hostname ev)))))
 
-(def (format-event-summary ev)
+(define (format-event-summary ev)
   (let ([type (security-event-event-type ev)]
         [data (security-event-data ev)])
     (case type
@@ -428,3 +398,23 @@
          (ht-ref data "process_name")
          (ht-ref data "remote_addr") (ht-ref data "remote_port"))]
       [else (symbol->string type)])))
+
+;; --- Main ---
+
+(define (main . args)
+  (let ([cmd (and (not (null? args)) (car args))]
+        [rest (if (null? args) '() (cdr args))])
+    (case (and cmd (string->symbol cmd))
+      [(watch)   (run-watch rest)]
+      [(poll)    (run-poll rest)]
+      [(status)  (run-status rest)]
+      [else
+       (displayln "Usage: secmon-collector <command> [options]")
+       (displayln "")
+       (displayln "Commands:")
+       (displayln "  watch  <host:port...> [--format json] [--db path]")
+       (displayln "  poll   <host:port> [--after seq] [--format json]")
+       (displayln "  status <host:port>")
+       (exit 1)])))
+
+(apply main (cdr (command-line)))
diff --git a/bin/keygen.ss b/bin/keygen.ss
index 9c20604..9acdd74 100644
--- a/bin/keygen.ss
+++ b/bin/keygen.ss
@@ -1,15 +1,14 @@
-#!/usr/bin/env gxi
+#!chezscheme
 ;; secmon-keygen: Generate ECIES keypair and PSK
 (import
+  (chezscheme)
+  (jerboa prelude clean)
   (secmon crypto keys)
   (std text hex))
 
-(def (main . args)
-  ;; Generate ECIES keypair
+(define (main . args)
   (let-values ([(private-key public-key) (generate-keypair)])
-    ;; Generate PSK
     (let ([psk (generate-psk)])
-      ;; Print keys
       (displayln "=== SecMon Key Generation ===")
       (displayln "")
       (displayln (string-append "ECIES_PUBLIC_KEY="
@@ -43,3 +42,5 @@
       (displayln (string-append "   export SECMON_PSK="
                    (u8vector->hex-string psk)))
       (displayln ""))))
+
+(apply main (cdr (command-line)))
diff --git a/build-all.ss b/build-all.ss
new file mode 100644
index 0000000..ebd88ca
--- /dev/null
+++ b/build-all.ss
@@ -0,0 +1,44 @@
+;; build-all.ss — Import all modules to trigger --compile-imported-libraries
+(import
+  ;; Crypto
+  (secmon crypto keys)
+  (secmon crypto ecies)
+  (secmon crypto psk)
+  ;; Monitor core
+  (secmon monitor events)
+  (secmon monitor suspicious)
+  ;; Platform
+  (secmon platform provider)
+  (secmon platform linux)
+  ;; Buffer
+  (secmon buffer ring)
+  ;; Server
+  (secmon server protocol)
+  (secmon server listener)
+  ;; Config
+  (secmon config)
+  ;; Storage
+  (secmon storage store)
+  ;; Monitors
+  (secmon monitor process)
+  (secmon monitor network)
+  (secmon monitor files)
+  (secmon monitor auth)
+  (secmon monitor kernel)
+  (secmon monitor cron)
+  (secmon monitor container)
+  (secmon monitor dns)
+  (secmon monitor rootkit)
+  (secmon monitor persistence)
+  (secmon monitor revshell)
+  (secmon monitor lateral)
+  (secmon monitor logtamper)
+  (secmon monitor webshell)
+  (secmon monitor podman)
+  (secmon monitor selinux)
+  ;; Stealth
+  (secmon stealth anti-debug)
+  (secmon stealth masquerade)
+  (secmon stealth env-sanitize)
+  (secmon stealth integrity)
+  (secmon stealth init))
diff --git a/build.ss b/build.ss
deleted file mode 100644
index 3a65c9d..0000000
--- a/build.ss
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env gxi
-;; build.ss - Build script for secmon
-(import :gerbil/compiler :gerbil/gambit)
-
-(def (main . args)
-  (let ([cmd (if (null? args) "build" (car args))])
-    (case cmd
-      [("build") (build-all)]
-      [("clean") (clean-all)]
-      [else (displayln (format "Unknown command: ~a" cmd))])))
-
-(def (build-all)
-  ;; Libraries
-  (for-each compile-module
-    '("lib/secmon/crypto/keys"
-      "lib/secmon/crypto/ecies"
-      "lib/secmon/crypto/psk"
-      "lib/secmon/monitor/events"
-      "lib/secmon/monitor/suspicious"
-      "lib/secmon/platform/provider"
-      "lib/secmon/platform/linux"
-      "lib/secmon/buffer/ring"
-      "lib/secmon/server/protocol"
-      "lib/secmon/server/listener"
-      "lib/secmon/config"
-      "lib/secmon/storage/store"
-      "lib/secmon/monitor/process"
-      "lib/secmon/monitor/network"
-      "lib/secmon/monitor/files"
-      "lib/secmon/monitor/auth"
-      "lib/secmon/monitor/kernel"
-      "lib/secmon/monitor/cron"
-      "lib/secmon/monitor/container"
-      "lib/secmon/monitor/dns"
-      "lib/secmon/monitor/rootkit"
-      "lib/secmon/monitor/persistence"
-      "lib/secmon/monitor/revshell"
-      "lib/secmon/monitor/lateral"
-      "lib/secmon/monitor/logtamper"
-      "lib/secmon/monitor/webshell"
-      "lib/secmon/monitor/podman"
-      "lib/secmon/monitor/selinux"
-      "lib/secmon/stealth/anti-debug"
-      "lib/secmon/stealth/masquerade"
-      "lib/secmon/stealth/env-sanitize"
-      "lib/secmon/stealth/integrity"
-      "lib/secmon/stealth/init"))
-  ;; Executables
-  (for-each compile-exe
-    '("bin/keygen"
-      "bin/agent"
-      "bin/collector"
-      "bin/analyze")))
-
-(def (compile-module path)
-  (displayln (format "Compiling ~a ..." path))
-  (compile-file (string-append path ".sls")))
-
-(def (compile-exe path)
-  (displayln (format "Compiling exe ~a ..." path))
-  (compile-file (string-append path ".ss")))
-
-(def (clean-all)
-  (displayln "Cleaning..."))
diff --git a/gerbil.pkg b/gerbil.pkg
deleted file mode 100644
index 6152775..0000000
--- a/gerbil.pkg
+++ /dev/null
@@ -1,3 +0,0 @@
-(package: secmon
- version: "0.1"
- depend: (jerboa))
diff --git a/lib/libjerboa_native.so b/lib/libjerboa_native.so
deleted file mode 100755
index f1f2527..0000000
Binary files a/lib/libjerboa_native.so and /dev/null differ
diff --git a/lib/secmon/buffer/ring.sls b/lib/secmon/buffer/ring.sls
index 4f1881c..735280a 100644
--- a/lib/secmon/buffer/ring.sls
+++ b/lib/secmon/buffer/ring.sls
@@ -3,11 +3,11 @@
     make-event-buffer event-buffer?
     buffer-store! buffer-get-after buffer-get-in-range
     buffer-clear-before! buffer-latest-seq buffer-count
-    stored-event? stored-event-seq stored-event-timestamp-ms
+    make-stored-event stored-event? stored-event-seq stored-event-timestamp-ms
     stored-event-encrypted-data)
   (import
     (chezscheme)
-    (jerboa prelude)
+    (jerboa prelude clean)
     (secmon monitor events)
     (secmon crypto ecies))
 
@@ -18,15 +18,11 @@
   (define-record-type event-buffer
     (fields encryptor max-size
             (mutable events) (mutable next-seq) mutex)
-    (nongenerative event-buffer-type))
-
-  (define (make-event-buffer encryptor max-size)
-    ($make-event-buffer encryptor max-size '() 0 (make-mutex)))
-
-  (define $make-event-buffer
-    (record-constructor
-      (make-record-constructor-descriptor
-        (record-type-descriptor event-buffer) #f #f)))
+    (nongenerative event-buffer-type)
+    (protocol
+      (lambda (new)
+        (lambda (encryptor max-size)
+          (new encryptor max-size '() 0 (make-mutex))))))
 
   (define (buffer-store! buf event)
     (let ([mtx (event-buffer-mutex buf)])
diff --git a/lib/secmon/config.sls b/lib/secmon/config.sls
index b6e464d..b061a84 100644
--- a/lib/secmon/config.sls
+++ b/lib/secmon/config.sls
@@ -8,7 +8,7 @@
     load-key-from-env)
   (import
     (chezscheme)
-    (jerboa prelude)
+    (jerboa prelude clean)
     (std text hex))
 
   (define-record-type agent-config
diff --git a/lib/secmon/crypto/ecies.sls b/lib/secmon/crypto/ecies.sls
index c0daad4..91792c9 100644
--- a/lib/secmon/crypto/ecies.sls
+++ b/lib/secmon/crypto/ecies.sls
@@ -8,7 +8,7 @@
     encrypted-payload->bytevector bytevector->encrypted-payload)
   (import
     (chezscheme)
-    (jerboa prelude)
+    (jerboa prelude clean)
     (std crypto native-rust)
     (std text hex))
 
diff --git a/lib/secmon/crypto/psk.sls b/lib/secmon/crypto/psk.sls
index 261fe4a..be9dcf8 100644
--- a/lib/secmon/crypto/psk.sls
+++ b/lib/secmon/crypto/psk.sls
@@ -10,7 +10,7 @@
     psk-response->bytevector bytevector->psk-response)
   (import
     (chezscheme)
-    (jerboa prelude)
+    (jerboa prelude clean)
     (std crypto native-rust)
     (std text hex))
 
@@ -45,21 +45,15 @@
 
   (define-record-type psk-auth
     (fields psk auth-key transport-key)
-    (nongenerative psk-auth-type))
-
-  (define (make-psk-auth psk-bytes)
-    ;; Derive auth and transport keys via HKDF
-    (let ([auth-key (hkdf-sha256 psk-bytes #f
-                      (string->utf8 "secmon-psk-auth-v1") 32)]
-          [transport-key (hkdf-sha256 psk-bytes #f
-                           (string->utf8 "secmon-psk-transport-v1") 32)])
-      ($make-psk-auth psk-bytes auth-key transport-key)))
-
-  ;; Use the auto-generated constructor name for direct creation
-  (define $make-psk-auth
-    (record-constructor
-      (make-record-constructor-descriptor
-        (record-type-descriptor psk-auth) #f #f)))
+    (nongenerative psk-auth-type)
+    (protocol
+      (lambda (new)
+        (lambda (psk-bytes)
+          (let ([auth-key (hkdf-sha256 psk-bytes #f
+                            (string->utf8 "secmon-psk-auth-v1") 32)]
+                [transport-key (hkdf-sha256 psk-bytes #f
+                                 (string->utf8 "secmon-psk-transport-v1") 32)])
+            (new psk-bytes auth-key transport-key))))))
 
   ;; --- Challenge/Response Protocol ---
 
diff --git a/lib/secmon/monitor/auth.sls b/lib/secmon/monitor/auth.sls
index e0778e3..1770225 100644
--- a/lib/secmon/monitor/auth.sls
+++ b/lib/secmon/monitor/auth.sls
@@ -2,8 +2,9 @@
   (export spawn-auth-monitor)
   (import
     (chezscheme)
-    (jerboa prelude)
-    (secmon monitor events))
+    (jerboa prelude clean)
+    (secmon monitor events)
+    (std os file-info))
 
   ;; Auth monitor: detects SSH logins, sudo, su, user changes
   (define (spawn-auth-monitor emit! poll-ms hostname)
@@ -25,7 +26,7 @@
                 (tail-auth-log! log-path log-pos emit! hostname))
               ;; Check utmp for login changes