fix

ober

fbe34130490c9a83ae3bf5b4b2a14c7d9d670e1c

diff --git a/.build.yml b/.build.yml
index 5041d54..ca289f3 100644
--- a/.build.yml
+++ b/.build.yml
@@ -85,11 +85,13 @@ tasks:
       ls -lh "$tb"
       tar tzf "$tb" | grep -E 'lib/libjerboa_browser.so$' \
         || { echo "lib missing from tarball"; exit 1; }
-      # The native binary MUST be present now (this is the whole point).
-      tar tzf "$tb" | grep -E 'bin/jerboa-browser$' \
+      # The native binary MUST be present now (this is the whole point). It is
+      # installed as bin/jerboa-browser.bin behind a bin/jerboa-browser launcher
+      # that points the loader at the bundled lib/ (Qt backend + Rust crypto).
+      tar tzf "$tb" | grep -E 'bin/jerboa-browser\.bin$' \
         || { echo "native binary missing from tarball"; exit 1; }
       echo "--- native binary ---"
-      tar xzf "$tb" -O jerboa-browser-0.0.1-linux-x86_64/bin/jerboa-browser \
+      tar xzf "$tb" -O jerboa-browser-0.0.1-linux-x86_64/bin/jerboa-browser.bin \
         > /tmp/jb && file /tmp/jb
       # Stage 7 — adblock-rust engine_match semantics (pure C++, no Qt/network).
       # build-tarball.sh only builds the jerboa_browser target, so build the test.
diff --git a/Dockerfile.qt b/Dockerfile.qt
index 1e1fc56..0417fbd 100644
--- a/Dockerfile.qt
+++ b/Dockerfile.qt
@@ -52,14 +52,13 @@ RUN SCHEME="$(command -v scheme)" bash packaging/linux/build-tarball.sh && \
     mkdir -p /out && \
     cp qt-webengine/build/jerboa-browser-*-linux-*.tar.gz \
       /out/jerboa-browser-static-qt.tar.gz && \
-    tar tzf /out/jerboa-browser-static-qt.tar.gz | \
-      grep -E '/bin/jerboa-browser$' >/dev/null && \
-    tar tzf /out/jerboa-browser-static-qt.tar.gz | \
-      grep -E '/lib/libjerboa_browser.so$' >/dev/null && \
-    bin_path="$(tar tzf /out/jerboa-browser-static-qt.tar.gz | \
-      grep -E '/bin/jerboa-browser$' | head -n 1)" && \
-    tar xzf /out/jerboa-browser-static-qt.tar.gz -O \
-      "$bin_path" > /tmp/jerboa-browser && \
+    tb=/out/jerboa-browser-static-qt.tar.gz && \
+    tar tzf "$tb" | grep -E '/bin/jerboa-browser$'        >/dev/null && \
+    tar tzf "$tb" | grep -E '/bin/jerboa-browser\.bin$'   >/dev/null && \
+    tar tzf "$tb" | grep -E '/lib/libjerboa_browser\.so$' >/dev/null && \
+    tar tzf "$tb" | grep -E '/lib/libjerboa_native\.so$'  >/dev/null && \
+    bin_path="$(tar tzf "$tb" | grep -E '/bin/jerboa-browser\.bin$' | head -n 1)" && \
+    tar xzf "$tb" -O "$bin_path" > /tmp/jerboa-browser && \
     file /tmp/jerboa-browser
 
 FROM ubuntu:24.04
diff --git a/Makefile b/Makefile
index a7ba097..d1aaae0 100644
--- a/Makefile
+++ b/Makefile
@@ -13,11 +13,15 @@ JERBOA_IMAGE ?= jerboa21/jerboa
 STATIC_QT_IMAGE ?= jerboa-browser-qt-builder
 STATIC_QT_TARBALL ?= jerboa-browser-static-qt-linux-amd64.tar.gz
 
-# Modules that use (std crypto native-rust) need libjerboa_native dlopen-able
-# when run in the interpreter (jerbuild exec: tests, repl); the compiled binary
-# registers those symbols statically and needs none. native-rust probes
-# ./lib/libjerboa_native.* (CWD-relative), so stage a symlink there from where
-# jerbuild built it (its cache, after `make binary`) or a sibling jerboa checkout.
+# Modules that use (std crypto native-rust) need libjerboa_native dlopen-able at
+# runtime — in the interpreter (jerbuild exec: tests, repl) AND in the compiled
+# binary: jerbuild does not statically register the Rust crypto symbols, so the
+# binary dlopens the lib too (else it aborts with "no entry for
+# jerboa_last_error"). native-rust probes ./lib/libjerboa_native.* (CWD-relative)
+# and the dynamic loader path, so stage a symlink under ./lib from where jerbuild
+# built it (its cache, after `make binary`) or a sibling jerboa checkout. (The
+# static-qt tarball bundles a copy in lib/; its launcher adds lib/ to
+# LD_LIBRARY_PATH so the binary finds it from any CWD.)
 SOEXT := $(if $(filter Darwin,$(shell uname -s)),dylib,so)
 # `make binary` links the static archive from a temp dir and leaves no shared
 # lib behind, so build the cdylib from jerbuild's bundled crate if it isn't
@@ -152,9 +156,13 @@ docker-static-qt: check-docker
 	@echo ""
 	@ls -lh $(STATIC_QT_TARBALL)
 	@tar tzf $(STATIC_QT_TARBALL) | grep -E '/bin/jerboa-browser$$' >/dev/null || \
+	  { echo "ERROR: launcher missing from $(STATIC_QT_TARBALL)" >&2; exit 1; }
+	@tar tzf $(STATIC_QT_TARBALL) | grep -E '/bin/jerboa-browser\.bin$$' >/dev/null || \
 	  { echo "ERROR: native binary missing from $(STATIC_QT_TARBALL)" >&2; exit 1; }
 	@tar tzf $(STATIC_QT_TARBALL) | grep -E '/lib/libjerboa_browser.so$$' >/dev/null || \
 	  { echo "ERROR: Qt WebEngine backend missing from $(STATIC_QT_TARBALL)" >&2; exit 1; }
+	@tar tzf $(STATIC_QT_TARBALL) | grep -E '/lib/libjerboa_native.so$$' >/dev/null || \
+	  { echo "ERROR: Rust crypto lib (libjerboa_native) missing from $(STATIC_QT_TARBALL)" >&2; exit 1; }
 
 clean:
 	rm -f jerboa-browser jb-main.c jb-main.o jb_*.h jerboa-browser.boot
diff --git a/packaging/linux/build-tarball.sh b/packaging/linux/build-tarball.sh
index c575efa..884cbdd 100755
--- a/packaging/linux/build-tarball.sh
+++ b/packaging/linux/build-tarball.sh
@@ -42,13 +42,58 @@ cp -f "$REPO/build-binary.ss" "$REPO/Makefile" "$STAGE/"
 # Jerboa toolchain is present (e.g. a developer host). CI images without Chez
 # skip this — the tarball still carries the sources + build-binary.ss.
 HAVE_BIN=0
+HAVE_NATIVE=0
 if [ -x "$SCHEME" ]; then
     echo "==> building the native jerboa-browser binary ($SCHEME)"
     ( cd "$REPO" && JERBOA_HOME="$JERBOA_HOME" SCHEME="$SCHEME" make binary )
     if [ -x "$REPO/jerboa-browser" ]; then
-        cp -f "$REPO/jerboa-browser" "$STAGE/bin/jerboa-browser"
+        # The binary re-execs its embedded program from a temp file and dlopens
+        # the Qt backend (libjerboa_browser) + the Rust crypto lib
+        # (libjerboa_native, for the secure store) at runtime. jerbuild's
+        # launcher does not export JERBOA_BROWSER_EXE, so the in-binary "find the
+        # lib next to me" probe lands in /tmp. Install the real ELF as
+        # bin/jerboa-browser.bin behind a small launcher that points the loader
+        # at the bundled lib/ regardless of the caller's CWD — so the tarball is
+        # runnable as-is (the whole point of `make static-qt`).
+        cp -f "$REPO/jerboa-browser" "$STAGE/bin/jerboa-browser.bin"
+        chmod +x "$STAGE/bin/jerboa-browser.bin"
+        cat > "$STAGE/bin/jerboa-browser" <<'WRAP'
+#!/bin/sh
+# Launcher for the relocatable Jerboa Browser tarball: resolve our install dir
+# (following symlinks) and point the native binary at the bundled lib/ before
+# exec'ing it. Override JERBOA_BROWSER_LIB to use a different Qt backend build.
+set -eu
+self=$0
+while [ -L "$self" ]; do
+  link=$(readlink "$self")
+  case $link in
+    /*) self=$link ;;
+    *)  self=$(dirname "$self")/$link ;;
+  esac
+done
+here=$(CDPATH= cd -- "$(dirname -- "$self")/.." && pwd)
+export JERBOA_BROWSER_LIB="${JERBOA_BROWSER_LIB:-$here/lib/libjerboa_browser.so}"
+export LD_LIBRARY_PATH="$here/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
+exec "$here/bin/jerboa-browser.bin" "$@"
+WRAP
         chmod +x "$STAGE/bin/jerboa-browser"
         HAVE_BIN=1
+
+        # Stage the Rust crypto lib the binary dlopens for (std crypto
+        # native-rust) — the secure store needs it; without it the binary aborts
+        # at startup ("no entry for jerboa_last_error"). Reuse the Makefile rule
+        # (cargo build --features crypto, symlinked into ./lib). Best-effort: a
+        # stale-cargo CI host can't build a v4-lockfile crate, but the Docker
+        # static-qt path (modern cargo) builds it and asserts it in the tarball.
+        ( cd "$REPO" && make "lib/libjerboa_native.so" ) || true
+        if [ -f "$REPO/lib/libjerboa_native.so" ]; then
+            cp -Lf "$REPO/lib/libjerboa_native.so" "$STAGE/lib/libjerboa_native.so"
+            echo "    staged lib/libjerboa_native.so (Rust crypto)"
+            HAVE_NATIVE=1
+        else
+            echo "    WARNING: libjerboa_native.so unavailable — the secure store"
+            echo "             (crypto) will fail at runtime; install a current cargo"
+        fi
     fi
 else
     echo "==> Chez/Jerboa not found at $SCHEME — skipping the native binary"
@@ -69,10 +114,12 @@ Prerequisite: Qt 6 WebEngine runtime installed system-wide
    equivalent). The Chromium sandbox is left enabled.
 
 Layout:
-  lib/libjerboa_browser.so   browser backend (the native binary dlopens this)
+$([ "$HAVE_BIN" = 1 ] && echo "  bin/jerboa-browser         launcher: points the loader at lib/, runs the binary" || echo "  bin/                       (empty — build with 'make binary'; needs Chez + Jerboa)")
+$([ "$HAVE_BIN" = 1 ] && echo "  bin/jerboa-browser.bin     self-contained native binary (<url> | browse | repl | test | run <file>)")
+  lib/libjerboa_browser.so   Qt WebEngine backend (the native binary dlopens this)
+$([ "$HAVE_NATIVE" = 1 ] && echo "  lib/libjerboa_native.so    Rust crypto for the secure store (dlopened at runtime)")
   scheme/                    the (browser) Jerboa module + entry points
-  build-binary.ss, Makefile  build the native binary: \`make binary\`
-$([ "$HAVE_BIN" = 1 ] && echo "  bin/jerboa-browser         self-contained native binary (<url> | browse | repl | test | run <file>)" || echo "  bin/                       (empty — build with 'make binary'; needs Chez + Jerboa)")
+  build-binary.ss, Makefile  rebuild the native binary: \`make binary\`
 
 The native jerboa-browser binary embeds Chez Scheme, the boot image, and the
 (browser) library; only libjerboa_browser.so + system Qt 6 are external.