Harden IPC, REPL, and dependency provenance

ober

be0ee5d1c30d9eb844697d1325d404948df6ff85

diff --git a/.build.yml b/.build.yml
index 9eeeb46..0744180 100644
--- a/.build.yml
+++ b/.build.yml
@@ -17,7 +17,7 @@ packages:
   - unzip
   - zip
 sources:
-  - https://git.sr.ht/~lisp/jerboa
+  - https://git.sr.ht/~lisp/jerboa#37426d9bb88e0cef607141605ae4bc928d5ccb24
   - https://git.sr.ht/~lisp/jerboa-emacs
 artifacts:
   - jerboa-emacs/jerboa-emacs-0.1.0.jpkg
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6fb33db..485ddb3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,7 +17,7 @@ jobs:
   build-test-audit:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
 
       - name: Security gate
         run: make security
diff --git a/.github/workflows/security-baseline.yml b/.github/workflows/security-baseline.yml
index 6e7e53b..be877e2 100644
--- a/.github/workflows/security-baseline.yml
+++ b/.github/workflows/security-baseline.yml
@@ -13,7 +13,7 @@ jobs:
   baseline:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
 
       - name: Required release files
         run: |
diff --git a/.gitignore b/.gitignore
index 3778d99..d38b383 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@ lib/**/*.wpo
 libjsh-ffi.so
 /.jerboa/
 /dist/
+.jemacs-server
+.jerboa-repl-port
 
 # Build outputs (binaries, objects, boot files, generated headers)
 jemacs
diff --git a/.jerboa/security.json b/.jerboa/security.json
index 79aa9d4..b3c7d60 100644
--- a/.jerboa/security.json
+++ b/.jerboa/security.json
@@ -3,7 +3,7 @@
   "repo": "jerboa-emacs",
   "extends": ["jerboa:cli", "jerboa:gui", "jerboa:ffi"],
   "paths": {
-    "production": ["*.ss", "*.sls", "lib/**/*.ss", "lib/**/*.sls", "src/**/*.{ss,sls,c,h,cpp,hpp}", "scripts/**", "tools/**", "Makefile"],
+    "production": ["*.ss", "*.sls", "lib/**/*.ss", "lib/**/*.sls", "src/**/*.{ss,sls,c,h,cpp,hpp}", "support/**/*.{c,h,sh}", "scripts/**", "tools/**", "Makefile", "Dockerfile*", "dependencies.lock"],
     "tests": ["test/**", "tests/**", "**/*-test.ss"],
     "generated": ["build/**", "dist/**", "target/**", "*.so", "*.dylib", "*.wpo"],
     "vendor": ["vendor/**", "third_party/**"],
@@ -14,9 +14,10 @@
     "imports": { "directChezscheme": "allow-in-ffi-boundaries" },
     "ffi": { "allowed": true, "requireOwnershipComments": true, "requireDynamicWindCleanup": true },
     "process": { "shellInterpolation": "deny", "promptForExternalCommands": true },
-    "network": { "requireTimeouts": true },
+    "network": { "requireTimeouts": true, "requireAuthentication": true, "requireInputBounds": true },
     "eval": { "stringEval": "deny", "bareRead": "deny", "allowReadEval": false },
     "gui": { "requireCallbackLifetimeGuards": true },
+        "dependencies": { "requireImmutableGitRevisions": true, "requireArchiveDigests": true, "requireContainerDigests": true },
         "releaseEvidence": {
             "requireEditorFlowProofForProduction": true,
             "sensitiveArtifactPolicy": "no-buffers-prompts-lsp-payloads-clipboard-or-host-data"
diff --git a/.jerbuild b/.jerbuild
index 8d27a60..b994ea7 100644
--- a/.jerbuild
+++ b/.jerbuild
@@ -9,9 +9,17 @@
 
 (entry  "main.ss")
 (output "jemacs")
+(cc "sh support/deterministic-cc.sh")
 
 (requires "jerbuild" "cc" "pkg-config" "libpcre2" "libvterm")
 
+(static-native #t)
+
+(os-libs
+  (Darwin "-lm -lpthread -lncurses -liconv -lc++ -Wl,-no_uuid -Wl,-no_adhoc_codesign")
+  (Linux "-lm -ldl -lpthread -lutil -lncurses -lstdc++")
+  (FreeBSD "-lm -lpthread -lutil -ltermcap -L/usr/local/lib -liconv -lstdc++"))
+
 (libdirs "lib"
          "vendor/jerboa-shell/_jerbuild-stage"
          "vendor/jerboa-shell/src"
@@ -33,9 +41,8 @@
                "vendor/jerboa-shell/ffi-shim.c"
                "vendor/jerboa-shell/vendor/jerboa-coreutils/support/libcoreutils.c"
                "support/jsh_coreutils_stubs.c"
-               "support/jsh_embed_crypto_stubs.c"
-               "support/jerboa_native_stubs.c"
-               "support/jemacs_ssh_agent_stubs.c"
-               "support/jemacs_tls_stubs.c")
+               ("support/jsh_embed_crypto_stubs.c"
+                cflags: "-DJEMACS_STATIC_NATIVE=1")
+               "support/jemacs_ssh_agent_stubs.c")
 
 (pre-build "support/stage-for-jerbuild.sh")
diff --git a/.jerbuild.freebsd-amd64 b/.jerbuild.freebsd-amd64
index 08701c7..088e20c 100644
--- a/.jerbuild.freebsd-amd64
+++ b/.jerbuild.freebsd-amd64
@@ -14,9 +14,11 @@
 
 (requires "jerbuild" "cc" "pkg-config" "libpcre2" "libvterm")
 
+(static-native #t)
+
 ;; FreeBSD has dlopen in libc. -lutil covers forkpty/openpty, termcap backs
 ;; Chez's expeditor, and GNU libiconv is commonly under /usr/local/lib.
-(os-libs "-lm -lpthread -lutil -ltermcap -L/usr/local/lib -liconv")
+(os-libs "-lm -lpthread -lutil -ltermcap -L/usr/local/lib -liconv -lstdc++")
 
 (libdirs "lib"
          "vendor/jerboa-shell/_jerbuild-stage"
@@ -39,9 +41,8 @@
                "vendor/jerboa-shell/ffi-shim.c"
                "vendor/jerboa-shell/vendor/jerboa-coreutils/support/libcoreutils.c"
                "support/jsh_coreutils_stubs.c"
-               "support/jsh_embed_crypto_stubs.c"
-               "support/jerboa_native_stubs.c"
-               "support/jemacs_ssh_agent_stubs.c"
-               "support/jemacs_tls_stubs.c")
+               ("support/jsh_embed_crypto_stubs.c"
+                cflags: "-DJEMACS_STATIC_NATIVE=1")
+               "support/jemacs_ssh_agent_stubs.c")
 
 (pre-build "support/stage-for-jerbuild.sh")
diff --git a/.jerbuild.linux-amd64 b/.jerbuild.linux-amd64
index cd3a6cf..8d5b3aa 100644
--- a/.jerbuild.linux-amd64
+++ b/.jerbuild.linux-amd64
@@ -14,7 +14,9 @@
 
 (requires "jerbuild" "cc" "pkg-config" "libpcre2" "libvterm")
 
-(os-libs "-lm -ldl -lpthread -lutil -lncurses")
+(static-native #t)
+
+(os-libs "-lm -ldl -lpthread -lutil -lncurses -lstdc++")
 
 (libdirs "lib"
          "vendor/jerboa-shell/_jerbuild-stage"
@@ -37,9 +39,8 @@
                "vendor/jerboa-shell/ffi-shim.c"
                "vendor/jerboa-shell/vendor/jerboa-coreutils/support/libcoreutils.c"
                "support/jsh_coreutils_stubs.c"
-               "support/jsh_embed_crypto_stubs.c"
-               "support/jerboa_native_stubs.c"
-               "support/jemacs_ssh_agent_stubs.c"
-               "support/jemacs_tls_stubs.c")
+               ("support/jsh_embed_crypto_stubs.c"
+                cflags: "-DJEMACS_STATIC_NATIVE=1")
+               "support/jemacs_ssh_agent_stubs.c")
 
 (pre-build "support/stage-for-jerbuild.sh")
diff --git a/.jerbuild.linux-arm64 b/.jerbuild.linux-arm64
index 26056a0..b7b5f71 100644
--- a/.jerbuild.linux-arm64
+++ b/.jerbuild.linux-arm64
@@ -14,7 +14,9 @@
 
 (requires "jerbuild" "cc" "pkg-config" "libpcre2" "libvterm")
 
-(os-libs "-lm -ldl -lpthread -lutil -lncurses")
+(static-native #t)
+
+(os-libs "-lm -ldl -lpthread -lutil -lncurses -lstdc++")
 
 (libdirs "lib"
          "vendor/jerboa-shell/_jerbuild-stage"
@@ -37,9 +39,8 @@
                "vendor/jerboa-shell/ffi-shim.c"
                "vendor/jerboa-shell/vendor/jerboa-coreutils/support/libcoreutils.c"
                "support/jsh_coreutils_stubs.c"
-               "support/jsh_embed_crypto_stubs.c"
-               "support/jerboa_native_stubs.c"
-               "support/jemacs_ssh_agent_stubs.c"
-               "support/jemacs_tls_stubs.c")
+               ("support/jsh_embed_crypto_stubs.c"
+                cflags: "-DJEMACS_STATIC_NATIVE=1")
+               "support/jemacs_ssh_agent_stubs.c")
 
 (pre-build "support/stage-for-jerbuild.sh")
diff --git a/Dockerfile b/Dockerfile
index d3fcceb..3b4f957 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,7 +5,7 @@
 # itself (~5-10 min instead of ~30 min).
 #
 ARG ARCH=x86_64
-FROM alpine:3.21
+FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
 
 # ── Phase 1: Alpine build deps ──────────────────────────────────────────
 RUN apk add --no-cache \
@@ -29,9 +29,11 @@ RUN apk add --no-cache \
     gcc g++ binutils make git curl wget
 
 # Build static libXau (no Alpine -static package available)
+ARG LIBXAU_SHA256=74d0e4dfa3d39ad8939e99bda37f5967aba528211076828464d2777d477fc0fb
 RUN apk add --no-cache libxau-dev && \
     cd /tmp && \
     wget -q https://xorg.freedesktop.org/releases/individual/lib/libXau-1.0.12.tar.xz && \
+    echo "${LIBXAU_SHA256}  libXau-1.0.12.tar.xz" | sha256sum -c - && \
     tar xf libXau-1.0.12.tar.xz && \
     cd libXau-1.0.12 && \
     ./configure --prefix=/usr --enable-static --disable-shared && \
@@ -39,8 +41,10 @@ RUN apk add --no-cache libxau-dev && \
     cd / && rm -rf /tmp/libXau-1.0.12*
 
 # Build static libxcb-util (no Alpine -static package; needed by xcb-image)
+ARG XCB_UTIL_SHA256=5abe3bbbd8e54f0fa3ec945291b7e8fa8cfd3cccc43718f8758430f94126e512
 RUN cd /tmp && \
     wget -q https://xcb.freedesktop.org/dist/xcb-util-0.4.1.tar.xz && \
+    echo "${XCB_UTIL_SHA256}  xcb-util-0.4.1.tar.xz" | sha256sum -c - && \
     tar xf xcb-util-0.4.1.tar.xz && \
     cd xcb-util-0.4.1 && \
     ./configure --prefix=/usr --enable-static --disable-shared && \
@@ -49,20 +53,25 @@ RUN cd /tmp && \
 
 # Build static OpenSSL (needed by jerboa-ssl for AWS API calls)
 # Alpine's openssl-dev only ships shared libs; we need .a files for the static binary.
+ARG OPENSSL_VERSION=3.5.7
+ARG OPENSSL_SHA256=a8c0d28a529ca480f9f36cf5792e2cd21984552a3c8e4aa11a24aa31aeac98e8
 RUN apk add --no-cache openssl-dev perl && \
-    OPENSSL_VER=$(apk info openssl 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "3.3.2") && \
-    wget -q https://www.openssl.org/source/openssl-${OPENSSL_VER}.tar.gz || \
-    wget -q https://www.openssl.org/source/openssl-3.3.2.tar.gz && \
-    tar xf openssl-*.tar.gz && \
-    cd openssl-*/ && \
-    ./Configure linux-x86_64 no-shared no-tests no-apps -O2 --prefix=/usr && \
+    wget -q "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" && \
+    echo "${OPENSSL_SHA256}  openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c - && \
+    tar xf "openssl-${OPENSSL_VERSION}.tar.gz" && \
+    cd "openssl-${OPENSSL_VERSION}" && \
+    ./Configure no-shared no-tests -O2 --prefix=/usr && \
     make -j$(nproc) && \
+    ./apps/openssl version | grep -F "OpenSSL ${OPENSSL_VERSION}" && \
+    ./apps/openssl version -a > /usr/lib/jemacs-openssl-build.txt && \
     cp libssl.a libcrypto.a /usr/lib/ && \
-    cd / && rm -rf openssl-*/  openssl-*.tar.gz
+    cd / && rm -rf "openssl-${OPENSSL_VERSION}" "openssl-${OPENSSL_VERSION}.tar.gz"
 
 # ── Phase 2: Build Qt6 qtbase static ────────────────────────────────────
 ARG QT6_VERSION=6.8.3
+ARG QTBASE_SHA256=56001b905601bb9023d399f3ba780d7fa940f3e4861e496a7c490331f49e0b80
 RUN wget -q https://download.qt.io/official_releases/qt/6.8/${QT6_VERSION}/submodules/qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \
+    echo "${QTBASE_SHA256}  qtbase-everywhere-src-${QT6_VERSION}.tar.xz" | sha256sum -c - && \
     tar xf qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \
     rm qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \
     cmake -S qtbase-everywhere-src-${QT6_VERSION} -B qt6-build -G Ninja \
@@ -88,7 +97,9 @@ RUN wget -q https://download.qt.io/official_releases/qt/6.8/${QT6_VERSION}/submo
 
 # ── Phase 3: Build QScintilla static ────────────────────────────────────
 ARG QSCI_VERSION=2.14.1
+ARG QSCINTILLA_SHA256=dfe13c6acc9d85dfcba76ccc8061e71a223957a6c02f3c343b30a9d43a4cdd4d
 RUN wget -q https://www.riverbankcomputing.com/static/Downloads/QScintilla/${QSCI_VERSION}/QScintilla_src-${QSCI_VERSION}.tar.gz && \
+    echo "${QSCINTILLA_SHA256}  QScintilla_src-${QSCI_VERSION}.tar.gz" | sha256sum -c - && \
     tar xf QScintilla_src-${QSCI_VERSION}.tar.gz && \
     rm QScintilla_src-${QSCI_VERSION}.tar.gz && \
     cd QScintilla_src-${QSCI_VERSION}/src && \
@@ -186,15 +197,13 @@ RUN mkdir -p /opt/qt6-static/lib/pkgconfig && \
 # This is needed because jemacs embeds the program as a .so and loads it
 # at runtime via Sscheme_script (which calls dlopen internally).
 # musl's static libdl.a provides dlopen in the final static binary.
-ARG CHEZ_TAG=main
-ARG CHEZ_COMMIT=
-RUN git clone --depth 100 --branch ${CHEZ_TAG} \
-      https://github.com/cisco/ChezScheme /tmp/ChezScheme && \
+ARG CHEZ_COMMIT=fdf6b3f5d069bf53082bb827f46714f2de8f11f5
+RUN git init -q /tmp/ChezScheme && \
+    git -C /tmp/ChezScheme remote add origin https://github.com/cisco/ChezScheme && \
+    git -C /tmp/ChezScheme fetch -q --depth 1 origin ${CHEZ_COMMIT} && \
+    test "$(git -C /tmp/ChezScheme rev-parse FETCH_HEAD)" = "${CHEZ_COMMIT}" && \
+    git -C /tmp/ChezScheme checkout -q --detach ${CHEZ_COMMIT} && \
     cd /tmp/ChezScheme && \
-    if [ -n "${CHEZ_COMMIT}" ]; then \
-      echo "Pinning Chez to commit ${CHEZ_COMMIT}"; \
-      git checkout ${CHEZ_COMMIT}; \
-    fi && \
     ./configure --threads --installprefix=/opt/chez && \
     make -j$(nproc) && \
     make install && \
@@ -202,8 +211,12 @@ RUN git clone --depth 100 --branch ${CHEZ_TAG} \
 
 # ── Phase 5.5: Build tree-sitter + grammars (static) ──────────────────
 ARG TS_VERSION=0.24.7
-RUN git clone --depth 1 --branch v${TS_VERSION} \
-      https://github.com/tree-sitter/tree-sitter /tmp/tree-sitter && \
+ARG TS_COMMIT=5e8760bf462ce7b19b3d2396d5b7860f3906a297
+RUN git init -q /tmp/tree-sitter && \
+    git -C /tmp/tree-sitter remote add origin https://github.com/tree-sitter/tree-sitter && \
+    git -C /tmp/tree-sitter fetch -q --depth 1 origin ${TS_COMMIT} && \
+    test "$(git -C /tmp/tree-sitter rev-parse FETCH_HEAD)" = "${TS_COMMIT}" && \
+    git -C /tmp/tree-sitter checkout -q --detach ${TS_COMMIT} && \
     cd /tmp/tree-sitter && \
     cc -c -O2 -Ilib/include lib/src/lib.c -o lib/src/lib.o && \
     mkdir -p /opt/tree-sitter-lib && \
@@ -215,9 +228,12 @@ RUN git clone --depth 1 --branch v${TS_VERSION} \
 # Build tree-sitter grammar static archives
 RUN mkdir -p /opt/tree-sitter-grammars && \
     build_grammar() { \
-      name=$1; repo=$2; tag=$3; subdir=${4:-.}; \
-      git clone --depth 1 --branch "$tag" \
-        "https://github.com/$repo" /tmp/ts-$name && \
+      name=$1; repo=$2; commit=$3; subdir=${4:-.}; \
+      git init -q /tmp/ts-$name && \
+      git -C /tmp/ts-$name remote add origin "https://github.com/$repo" && \
+      git -C /tmp/ts-$name fetch -q --depth 1 origin "$commit" && \
+      test "$(git -C /tmp/ts-$name rev-parse FETCH_HEAD)" = "$commit" && \
+      git -C /tmp/ts-$name checkout -q --detach "$commit" && \
       cd /tmp/ts-$name/$subdir && \
       SRC_DIR=src && \
       gcc -c -O2 -I/opt/tree-sitter-include -I$SRC_DIR \
@@ -231,20 +247,20 @@ RUN mkdir -p /opt/tree-sitter-grammars && \
       fi && \
       cd / && rm -rf /tmp/ts-$name; \
     } && \
-    build_grammar c          tree-sitter/tree-sitter-c          v0.23.5 && \
-    build_grammar cpp        tree-sitter/tree-sitter-cpp        v0.23.4 && \
-    build_grammar python     tree-sitter/tree-sitter-python     v0.23.6 && \
-    build_grammar javascript tree-sitter/tree-sitter-javascript v0.23.1 && \
-    build_grammar rust       tree-sitter/tree-sitter-rust       v0.23.3 && \
-    build_grammar go         tree-sitter/tree-sitter-go         v0.23.4 && \
-    build_grammar bash       tree-sitter/tree-sitter-bash       v0.23.3 && \
-    build_grammar json       tree-sitter/tree-sitter-json       v0.24.8 && \
-    build_grammar ruby       tree-sitter/tree-sitter-ruby       v0.23.1 && \
-    build_grammar java       tree-sitter/tree-sitter-java       v0.23.5 && \
-    build_grammar css        tree-sitter/tree-sitter-css        v0.23.2 && \
-    build_grammar html       tree-sitter/tree-sitter-html       v0.23.2 && \
-    build_grammar lua        tree-sitter-grammars/tree-sitter-lua v0.3.0 && \
-    build_grammar scheme     6cdh/tree-sitter-scheme            main && \
+    build_grammar c          tree-sitter/tree-sitter-c          2a265d69a4caf57108a73ad2ed1e6922dd2f998c && \
+    build_grammar cpp        tree-sitter/tree-sitter-cpp        f41e1a044c8a84ea9fa8577fdd2eab92ec96de02 && \
+    build_grammar python     tree-sitter/tree-sitter-python     bffb65a8cfe4e46290331dfef0dbf0ef3679de11 && \
+    build_grammar javascript tree-sitter/tree-sitter-javascript 3a837b6f3658ca3618f2022f8707e29739c91364 && \
+    build_grammar rust       tree-sitter/tree-sitter-rust       3d087c3df25286140393ddecc339208fae107149 && \
+    build_grammar go         tree-sitter/tree-sitter-go         3c3775faa968158a8b4ac190a7fda867fd5fb748 && \
+    build_grammar bash       tree-sitter/tree-sitter-bash       487734f87fd87118028a65a4599352fa99c9cde8 && \
+    build_grammar json       tree-sitter/tree-sitter-json       ee35a6ebefcef0c5c416c0d1ccec7370cfca5a24 && \
+    build_grammar ruby       tree-sitter/tree-sitter-ruby       71bd32fb7607035768799732addba884a37a6210 && \
+    build_grammar java       tree-sitter/tree-sitter-java       94703d5a6bed02b98e438d7cad1136c01a60ba2c && \
+    build_grammar css        tree-sitter/tree-sitter-css        c0d581e32d183a536731ed6c3a72758b27e20411 && \
+    build_grammar html       tree-sitter/tree-sitter-html       5a5ca8551a179998360b4a4ca2c0f366a35acc03 && \
+    build_grammar lua        tree-sitter-grammars/tree-sitter-lua 534c461d2b75b0887ec968ef9635f4460b0878b7 && \
+    build_grammar scheme     6cdh/tree-sitter-scheme            c6cb7c7d7a04b3f5d999c28e2e9c0c31b2d50ece && \
     echo "Grammars built:" && ls /opt/tree-sitter-grammars/
 
 # ── Phase 6: Jerboa dependencies ───────────────────────────────────────
diff --git a/Dockerfile.qt b/Dockerfile.qt
index 37eb3b5..c9060a3 100644
--- a/Dockerfile.qt
+++ b/Dockerfile.qt
@@ -11,16 +11,21 @@
 #   docker cp $id:/out/jemacs-qt ./jemacs-qt && docker rm $id
 #   chmod +x jemacs-qt
 
-ARG JERBOA_IMAGE=jerboa21/jerboa
+ARG JERBOA_IMAGE=jerboa21/jerboa@sha256:9c4034f1296601d5a9f7b219a0decdd5d62862a2be4347976b3d96b13ed7701e
 
 FROM ${JERBOA_IMAGE} AS jerboa-runtime
 
 # ── Stage 1: Build Qt6 + QScintilla + tree-sitter + libvterm on Alpine ───────
-FROM alpine:3.21 AS qt-alpine
+FROM alpine:3.21@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d AS qt-alpine
 
 ARG QT6_VERSION=6.8.3
 ARG QSCI_VERSION=2.14.1
 ARG TS_VERSION=0.24.7
+ARG TS_COMMIT=5e8760bf462ce7b19b3d2396d5b7860f3906a297
+ARG LIBXAU_SHA256=74d0e4dfa3d39ad8939e99bda37f5967aba528211076828464d2777d477fc0fb
+ARG XCB_UTIL_SHA256=5abe3bbbd8e54f0fa3ec945291b7e8fa8cfd3cccc43718f8758430f94126e512
+ARG QTBASE_SHA256=56001b905601bb9023d399f3ba780d7fa940f3e4861e496a7c490331f49e0b80
+ARG QSCINTILLA_SHA256=dfe13c6acc9d85dfcba76ccc8061e71a223957a6c02f3c343b30a9d43a4cdd4d
 
 # ── Phase 1: Alpine build deps ──────────────────────────────────────────
 RUN apk add --no-cache \
@@ -47,6 +52,7 @@ RUN apk add --no-cache \
 RUN apk add --no-cache libxau-dev && \
     cd /tmp && \
     wget -q https://xorg.freedesktop.org/releases/individual/lib/libXau-1.0.12.tar.xz && \
+    echo "${LIBXAU_SHA256}  libXau-1.0.12.tar.xz" | sha256sum -c - && \
     tar xf libXau-1.0.12.tar.xz && \
     cd libXau-1.0.12 && \
     ./configure --prefix=/usr --enable-static --disable-shared && \
@@ -56,6 +62,7 @@ RUN apk add --no-cache libxau-dev && \
 # Build static libxcb-util (no Alpine -static package; needed by xcb-image)
 RUN cd /tmp && \
     wget -q https://xcb.freedesktop.org/dist/xcb-util-0.4.1.tar.xz && \
+    echo "${XCB_UTIL_SHA256}  xcb-util-0.4.1.tar.xz" | sha256sum -c - && \
     tar xf xcb-util-0.4.1.tar.xz && \
     cd xcb-util-0.4.1 && \
     ./configure --prefix=/usr --enable-static --disable-shared && \
@@ -64,6 +71,7 @@ RUN cd /tmp && \
 
 # ── Phase 2: Build Qt6 qtbase static ────────────────────────────────────
 RUN wget -q https://download.qt.io/official_releases/qt/6.8/${QT6_VERSION}/submodules/qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \
+    echo "${QTBASE_SHA256}  qtbase-everywhere-src-${QT6_VERSION}.tar.xz" | sha256sum -c - && \
     tar xf qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \
     rm qtbase-everywhere-src-${QT6_VERSION}.tar.xz && \
     cmake -S qtbase-everywhere-src-${QT6_VERSION} -B qt6-build -G Ninja \
@@ -93,6 +101,7 @@ RUN wget -q https://download.qt.io/official_releases/qt/6.8/${QT6_VERSION}/submo
 
 # ── Phase 3: Build QScintilla static ────────────────────────────────────
 RUN wget -q https://www.riverbankcomputing.com/static/Downloads/QScintilla/${QSCI_VERSION}/QScintilla_src-${QSCI_VERSION}.tar.gz && \
+    echo "${QSCINTILLA_SHA256}  QScintilla_src-${QSCI_VERSION}.tar.gz" | sha256sum -c - && \
     tar xf QScintilla_src-${QSCI_VERSION}.tar.gz && \
     rm QScintilla_src-${QSCI_VERSION}.tar.gz && \
     cd QScintilla_src-${QSCI_VERSION}/src && \
@@ -181,8 +190,11 @@ RUN mkdir -p /opt/qt6-static/lib/pkgconfig && \
     echo "Generated .pc files:" && ls /opt/qt6-static/lib/pkgconfig/
 
 # ── Phase 5: Build tree-sitter + grammars (static) ──────────────────────
-RUN git clone --depth 1 --branch v${TS_VERSION} \
-      https://github.com/tree-sitter/tree-sitter /tmp/tree-sitter && \
+RUN git init -q /tmp/tree-sitter && \
+    git -C /tmp/tree-sitter remote add origin https://github.com/tree-sitter/tree-sitter && \
+    git -C /tmp/tree-sitter fetch -q --depth 1 origin ${TS_COMMIT} && \
+    test "$(git -C /tmp/tree-sitter rev-parse FETCH_HEAD)" = "${TS_COMMIT}" && \
+    git -C /tmp/tree-sitter checkout -q --detach ${TS_COMMIT} && \
     cd /tmp/tree-sitter && \
     cc -c -O2 -Ilib/include lib/src/lib.c -o lib/src/lib.o && \
     mkdir -p /opt/tree-sitter-lib && \
@@ -193,9 +205,12 @@ RUN git clone --depth 1 --branch v${TS_VERSION} \
 
 RUN mkdir -p /opt/tree-sitter-grammars && \
     build_grammar() { \
-      name=$1; repo=$2; tag=$3; subdir=${4:-.}; \
-      git clone --depth 1 --branch "$tag" \
-        "https://github.com/$repo" /tmp/ts-$name && \
+      name=$1; repo=$2; commit=$3; subdir=${4:-.}; \
+      git init -q /tmp/ts-$name && \
+      git -C /tmp/ts-$name remote add origin "https://github.com/$repo" && \
+      git -C /tmp/ts-$name fetch -q --depth 1 origin "$commit" && \
+      test "$(git -C /tmp/ts-$name rev-parse FETCH_HEAD)" = "$commit" && \
+      git -C /tmp/ts-$name checkout -q --detach "$commit" && \
       cd /tmp/ts-$name/$subdir && \
       SRC_DIR=src && \
       gcc -c -O2 -I/opt/tree-sitter-include -I$SRC_DIR \
@@ -209,20 +224,20 @@ RUN mkdir -p /opt/tree-sitter-grammars && \
       fi && \
       cd / && rm -rf /tmp/ts-$name; \
     } && \
-    build_grammar c          tree-sitter/tree-sitter-c          v0.23.5 && \
-    build_grammar cpp        tree-sitter/tree-sitter-cpp        v0.23.4 && \
-    build_grammar python     tree-sitter/tree-sitter-python     v0.23.6 && \
-    build_grammar javascript tree-sitter/tree-sitter-javascript v0.23.1 && \
-    build_grammar rust       tree-sitter/tree-sitter-rust       v0.23.3 && \
-    build_grammar go         tree-sitter/tree-sitter-go         v0.23.4 && \
-    build_grammar bash       tree-sitter/tree-sitter-bash       v0.23.3 && \
-    build_grammar json       tree-sitter/tree-sitter-json       v0.24.8 && \
-    build_grammar ruby       tree-sitter/tree-sitter-ruby       v0.23.1 && \
-    build_grammar java       tree-sitter/tree-sitter-java       v0.23.5 && \
-    build_grammar css        tree-sitter/tree-sitter-css        v0.23.2 && \
-    build_grammar html       tree-sitter/tree-sitter-html       v0.23.2 && \
-    build_grammar lua        tree-sitter-grammars/tree-sitter-lua v0.3.0 && \
-    build_grammar scheme     6cdh/tree-sitter-scheme            main && \
+    build_grammar c          tree-sitter/tree-sitter-c          2a265d69a4caf57108a73ad2ed1e6922dd2f998c && \
+    build_grammar cpp        tree-sitter/tree-sitter-cpp        f41e1a044c8a84ea9fa8577fdd2eab92ec96de02 && \
+    build_grammar python     tree-sitter/tree-sitter-python     bffb65a8cfe4e46290331dfef0dbf0ef3679de11 && \
+    build_grammar javascript tree-sitter/tree-sitter-javascript 3a837b6f3658ca3618f2022f8707e29739c91364 && \
+    build_grammar rust       tree-sitter/tree-sitter-rust       3d087c3df25286140393ddecc339208fae107149 && \
+    build_grammar go         tree-sitter/tree-sitter-go         3c3775faa968158a8b4ac190a7fda867fd5fb748 && \
+    build_grammar bash       tree-sitter/tree-sitter-bash       487734f87fd87118028a65a4599352fa99c9cde8 && \
+    build_grammar json       tree-sitter/tree-sitter-json       ee35a6ebefcef0c5c416c0d1ccec7370cfca5a24 && \
+    build_grammar ruby       tree-sitter/tree-sitter-ruby       71bd32fb7607035768799732addba884a37a6210 && \
+    build_grammar java       tree-sitter/tree-sitter-java       94703d5a6bed02b98e438d7cad1136c01a60ba2c && \
+    build_grammar css        tree-sitter/tree-sitter-css        c0d581e32d183a536731ed6c3a72758b27e20411 && \
+    build_grammar html       tree-sitter/tree-sitter-html       5a5ca8551a179998360b4a4ca2c0f366a35acc03 && \
+    build_grammar lua        tree-sitter-grammars/tree-sitter-lua 534c461d2b75b0887ec968ef9635f4460b0878b7 && \
+    build_grammar scheme     6cdh/tree-sitter-scheme            c6cb7c7d7a04b3f5d999c28e2e9c0c31b2d50ece && \
     echo "Grammars built:" && ls /opt/tree-sitter-grammars/
 
 # ── Phase 6: Stage Qt transitive static deps for the final link ─────────
@@ -298,6 +313,6 @@ RUN ./jemacs-qt --version && \
     ls -lh jemacs-qt
 
 # ── Final stage: extract binary ───────────────────────────────────────────────
-FROM ubuntu:24.04
+FROM ubuntu:24.04@sha256:4fbb8e6a8395de5a7550b33509421a2bafbc0aab6c06ba2cef9ebffbc7092d90
 COPY --from=builder /src/jemacs-qt /out/jemacs-qt
 CMD ["cat", "/out/jemacs-qt"]
diff --git a/Dockerfile.tui b/Dockerfile.tui
index 72c0a8c..0839797 100644
--- a/Dockerfile.tui
+++ b/Dockerfile.tui
@@ -8,7 +8,7 @@
 #   podman cp $id:/out/jemacs ./jemacs && podman rm $id
 #   chmod +x jemacs
 
-FROM jerboa21/jerboa AS builder
+FROM jerboa21/jerboa@sha256:9c4034f1296601d5a9f7b219a0decdd5d62862a2be4347976b3d96b13ed7701e AS builder
 
 # ── Copy jerboa-emacs source ────────────────────────────────────────────────
 COPY . /src
@@ -24,6 +24,6 @@ RUN ./jemacs --version && \
     ls -lh jemacs
 
 # ── Output ───────────────────────────────────────────────────────────────────
-FROM ubuntu:24.04
+FROM ubuntu:24.04@sha256:4fbb8e6a8395de5a7550b33509421a2bafbc0aab6c06ba2cef9ebffbc7092d90
 COPY --from=builder /src/jemacs /out/jemacs
 CMD ["cat", "/out/jemacs"]
diff --git a/Makefile b/Makefile
index af32962..03a9e9d 100644
--- a/Makefile
+++ b/Makefile
@@ -9,8 +9,9 @@ RELEASE_EVIDENCE_DIR ?= $(DIST_DIR)/release-evidence
 # Standalone jerbuild toolchain. This mirrors the bootstrap path used by
 # jerboa-code/jerboa-shell: prefer project-local tools, then sibling Jerboa,
 # then PATH, and otherwise fetch a release into .jerboa/bin.
-JERBOA_VERSION ?= v0.2.0
+JERBOA_VERSION ?= $(JERBOA_TOOLCHAIN_VERSION)
 JERBOA_TOOL_DIR ?= $(CURDIR)/.jerboa/bin
+DETERMINISTIC_JERBUILD ?= $(CURDIR)/support/deterministic-jerbuild.sh
 JERBUILD_TOOL ?= $(shell if [ -x ./jerbuild ]; then echo ./jerbuild; \
 	elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ]; then echo "$(JERBOA_TOOL_DIR)/jerbuild"; \
 	elif [ -x ../jerboa/dist/jerbuild ]; then echo ../jerboa/dist/jerbuild; \
@@ -22,7 +23,7 @@ SSH ?= ssh
 SCP ?= scp
 LINUX_BUILD_HOST ?= linux.cons.io
 REMOTE_REPO_URL ?= $(shell git remote get-url origin 2>/dev/null || echo git@git.sr.ht:~lisp/jerboa-emacs)
-REMOTE_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo master)
+REMOTE_COMMIT ?= $(shell git rev-parse HEAD 2>/dev/null)
 LINUX_BUILD_ROOT ?= .cache/jemacs-build/jerboa-emacs
 LINUX_ARM64_BUILD_HOST ?=
 LINUX_ARM64_BUILD_ROOT ?= $(LINUX_BUILD_ROOT)
@@ -38,8 +39,14 @@ JSQLITE_REPO_URL     ?= https://git.sr.ht/~lisp/jsqlite
 JERBOA_LSP_REPO      ?= https://git.sr.ht/~lisp/jerboa-lsp
 JERBOA_AWS_REPO    ?= https://git.sr.ht/~lisp/jerboa-aws
 
+DEPENDENCY_LOCK ?= dependencies.lock
+include $(DEPENDENCY_LOCK)
+
 JEMACS_FEATURES ?=
 export JEMACS_FEATURES
+JEMACS_DEV_NATIVE ?= 1
+JERBOA_EMACS_SUPPORT ?= $(CURDIR)
+export JEMACS_DEV_NATIVE JERBOA_EMACS_SUPPORT
 
 empty :=
 space := $(empty) $(empty)
@@ -126,6 +133,7 @@ else
   TS_LIB_DIR  := /opt/tree-sitter-lib
 endif
 NATIVE_LIB := $(JERBOA)/lib/libjerboa_native.$(SHLIB_EXT)
+JERBOA_NATIVE_A ?= $(JERBOA)/jerboa-native-rs/target/release/libjerboa_native.a
 SSL_SHIM := $(JERBOA_SSL_ROOT)/jerboa_ssl_shim.$(SHLIB_EXT)
 SCINTILLA_SHIM := ./jerboa_scintilla_shim.$(SHLIB_EXT)
 TLS_STUB := ./jemacs_tls_stubs.$(SHLIB_EXT)
@@ -155,7 +163,7 @@ export CHEZ_DIR
 .PHONY: all vendor-deps vendor-update check-pcre2-system-deps check-vterm-system-deps deps ensure-vendor-jerboa-tools ensure-jerboa-tools build-jerbuild jemacs-jerbuild jerbuild-check scintilla-vendor-deps scintilla-vendor-clean build rebuild run test-tier0 test-tier2 test-tier3 test-tier4 test-tier5 test-org test-extra test clean clean-generated install audit security sbom reproducibility-report editor-flow-evidence verify release-evidence \
         test-org-duration test-org-element test-org-fold test-org-footnote \
         test-org-lint test-org-num test-org-property test-org-src test-org-tempo \
-        test-vtscreen test-debug-repl test-qt test-qt-e2e build-qt binary-qt \
+        test-vtscreen test-debug-repl test-ipc-security test-repl-shim test-qt test-qt-e2e build-qt binary-qt \
         test-pty test-emacs test-functional test-term-hang \
         podman-deps ensure-podman-deps static-qt static-tui clean-podman check-podman check-root \
         check-docker docker-static-qt \
@@ -217,16 +225,16 @@ vendor-deps:
 	@mkdir -p $(VENDOR)
 	@for repo in $(VENDOR_REPOS); do \
 	  case "$$repo" in \
-	    jerboa) url="$(JERBOA_REPO)" ;; \
-	    jerboa-shell) url="$(JERBOA_SHELL_REPO)" ;; \
-	    jerboa-pcre2) url="$(JERBOA_PCRE2_REPO)" ;; \
-	    jerboa-scintilla) url="$(JERBOA_SCINTILLA_REPO)" ;; \
-	    jerboa-qt) url="$(JERBOA_QT_REPO)" ;; \
-	    jerboa-ssl) url="$(JERBOA_SSL_REPO)" ;; \
-	    jerboa-https) url="$(JERBOA_HTTPS_REPO)" ;; \
-	    jsqlite) url="$(JSQLITE_REPO_URL)" ;; \
-	    jerboa-lsp) url="$(JERBOA_LSP_REPO)" ;; \
-	    jerboa-aws) url="$(JERBOA_AWS_REPO)" ;; \
+	    jerboa) url="$(JERBOA_REPO)"; rev="$(JERBOA_REV)" ;; \
+	    jerboa-shell) url="$(JERBOA_SHELL_REPO)"; rev="$(JERBOA_SHELL_REV)" ;; \
+	    jerboa-pcre2) url="$(JERBOA_PCRE2_REPO)"; rev="$(JERBOA_PCRE2_REV)" ;; \
+	    jerboa-scintilla) url="$(JERBOA_SCINTILLA_REPO)"; rev="$(JERBOA_SCINTILLA_REV)" ;; \
+	    jerboa-qt) url="$(JERBOA_QT_REPO)"; rev="$(JERBOA_QT_REV)" ;; \
+	    jerboa-ssl) url="$(JERBOA_SSL_REPO)"; rev="$(JERBOA_SSL_REV)" ;; \
+	    jerboa-https) url="$(JERBOA_HTTPS_REPO)"; rev="$(JERBOA_HTTPS_REV)" ;; \
+	    jsqlite) url="$(JSQLITE_REPO_URL)"; rev="$(JSQLITE_REV)" ;; \
+	    jerboa-lsp) url="$(JERBOA_LSP_REPO)"; rev="$(JERBOA_LSP_REV)" ;; \
+	    jerboa-aws) url="$(JERBOA_AWS_REPO)"; rev="$(JERBOA_AWS_REV)" ;; \
 	  esac; \
 	  dest="$(VENDOR)/$$repo"; \
 	  if [ -L "$$dest" ]; then \
@@ -234,47 +242,57 @@ vendor-deps:
 	    rm "$$dest"; \
 	  fi; \
 	  if [ -d "$$dest/.git" ]; then \
-	    echo "  (exists) $$repo"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 	  elif [ -d "$$dest" ] && [ -z "$$(ls -A "$$dest")" ]; then \
 	    echo "  replacing empty gitlink directory $$dest"; \
 	    rmdir "$$dest"; \
 	    echo "  cloning $$repo from $$url"; \
-	    git clone --depth 1 "$$url" "$$dest"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 	  elif [ -d "$$dest" ]; then \
 	    echo "  replacing non-git vendor directory $$dest"; \
 	    rm -rf "$$dest"; \
 	    echo "  cloning $$repo from $$url"; \
-	    git clone --depth 1 "$$url" "$$dest"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 	  elif [ -e "$$dest" ]; then \
 	    echo "ERROR: $$dest exists but is not a directory; remove it and rerun make vendor-deps" >&2; \
 	    exit 1; \
 	  else \
 	    echo "  cloning $$repo from $$url"; \
-	    git clone --depth 1 "$$url" "$$dest"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 	  fi; \
 	done
 	@echo "=== Vendoring jsh dependencies into $(JSH_VENDOR) ==="
 	@mkdir -p $(JSH_VENDOR)
 	@for repo in $(JSH_VENDOR_REPOS); do \
+	  case "$$repo" in \
+	    jerboa-coreutils) rev="$(JSH_COREUTILS_REV)" ;; \
+	    jerboa-awk) rev="$(JSH_AWK_REV)" ;; \
+	    jerboa-sed) rev="$(JSH_SED_REV)" ;; \
+	    jerboa-aws) rev="$(JSH_AWS_REV)" ;; \
+	    jerboa-yubikey) rev="$(JSH_YUBIKEY_REV)" ;; \
+	    jerboa-ssh) rev="$(JSH_SSH_REV)" ;; \
+	    jerboa-fuse) rev="$(JSH_FUSE_REV)" ;; \
+	  esac; \
+	  url="https://git.sr.ht/~lisp/$$repo"; \
 	  dest="$(JSH_VENDOR)/$$repo"; \
 	  if [ -d "$$dest/.git" ]; then \
-	    echo "  (exists) $$repo"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 	  elif [ -d "$$dest" ] && [ -z "$$(ls -A "$$dest")" ]; then \
 	    echo "  replacing empty directory $$dest"; \
 	    rmdir "$$dest"; \
 	    echo "  cloning $$repo"; \
-	    git clone --depth 1 "https://git.sr.ht/~lisp/$$repo" "$$dest"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 	  elif [ -d "$$dest" ]; then \
 	    echo "  replacing non-git vendor directory $$dest"; \
 	    rm -rf "$$dest"; \
 	    echo "  cloning $$repo"; \
-	    git clone --depth 1 "https://git.sr.ht/~lisp/$$repo" "$$dest"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 	  elif [ -e "$$dest" ]; then \
 	    echo "ERROR: $$dest exists but is not a directory; remove it and rerun make vendor-deps" >&2; \
 	    exit 1; \
 	  else \
 	    echo "  cloning $$repo"; \
-	    git clone --depth 1 "https://git.sr.ht/~lisp/$$repo" "$$dest"; \
+	    sh support/fetch-locked-dependency.sh "$$url" "$$rev" "$$dest"; \
 		  fi; \
 		done
 	@sh scripts/patch-jerboa-std-compat.sh "$(abspath $(JERBOA)/lib)"
@@ -283,32 +301,12 @@ vendor-deps:
 	@sh scripts/patch-jerboa-qt-compat.sh "$(CURDIR)"
 	@echo "=== Vendor complete ==="
 
-# Fast-forward existing vendored git checkouts to current upstream HEAD.
-# `make vendor-deps` only CLONES missing deps; it never updates an existing
-# checkout, so a machine with old clones keeps building against stale sources
-# (the usual cause of "No rule to make target 'jerboa'/'stage-jsqlite'" and
-# pcre2 "multiple definitions for match"). This resets each tracked vendor/<dep>
-# to origin HEAD. Gitignored build artifacts (.chez/, build/) are preserved;
-# local patch-script edits re-apply on the next `make build`.
+# Compatibility alias retained for operators. Dependencies are never advanced
+# to mutable upstream HEAD: this target verifies/fetches dependencies.lock.
 vendor-update:
-	@echo "=== Updating vendored git checkouts to upstream HEAD ==="
-	@for d in $(VENDOR)/*/ ; do \
-	  d=$${d%/}; \
-	  [ -d "$$d/.git" ] || continue; \
-	  cur=$$(git -C "$$d" rev-parse --short HEAD 2>/dev/null); \
-	  if git -C "$$d" fetch --depth 1 origin HEAD >/dev/null 2>&1; then \
-	    new=$$(git -C "$$d" rev-parse --short FETCH_HEAD 2>/dev/null); \
-	    if [ "$$cur" != "$$new" ]; then \
-	      git -C "$$d" reset --hard FETCH_HEAD >/dev/null 2>&1 \
-	        && echo "  updated  $$(basename $$d): $$cur -> $$new"; \
-	    else \
-	      echo "  current  $$(basename $$d): $$cur"; \
-	    fi; \
-	  else \
-	    echo "  skip     $$(basename $$d) (no reachable upstream)"; \
-	  fi; \
-	done
-	@echo "=== vendor-update complete — run 'make build' to re-apply compat patches ==="
+	@echo "=== Verifying locked vendored dependency revisions ==="
+	@$(MAKE) --no-print-directory vendor-deps
+	@echo "=== vendor-update complete — no mutable upstream refs consumed ==="
 
 PCRE2_CHECK_CFLAGS := $(shell pkg-config --cflags libpcre2-8 2>/dev/null)
 PCRE2_CHECK_LIBS := $(shell pkg-config --libs libpcre2-8 2>/dev/null || echo -lpcre2-8)
@@ -370,14 +368,14 @@ ensure-jerboa-tools:
 build-jerbuild: ensure-jerboa-tools vendor-deps
 	JERBUILD="$(JERBUILD_TOOL)" support/stage-for-jerbuild.sh
 
-jerbuild-check: ensure-jerboa-tools
+jerbuild-check: ensure-vendor-jerboa-tools
 	@for cfg in .jerbuild .jerbuild.linux-amd64 .jerbuild.linux-arm64 .jerbuild.freebsd-amd64; do \
 	  echo "=== $$cfg ==="; \
-	  "$(JERBUILD_TOOL)" check --config "$$cfg"; \
+	  "$(VENDORED_JERBUILD)" check --config "$$cfg"; \
 	done
 
-jemacs-jerbuild: ensure-jerboa-tools vendor-deps repl_shim.$(SHLIB_EXT) pty_shim.$(SHLIB_EXT) vterm_shim.$(SHLIB_EXT) jerboa_scintilla_shim.$(SHLIB_EXT) jerboa_pcre2_shim.$(SHLIB_EXT)
-	JERBUILD="$(JERBUILD_TOOL)" "$(JERBUILD_TOOL)" build --config .jerbuild
+jemacs-jerbuild: ensure-vendor-jerboa-tools vendor-deps $(NATIVE_LIB) repl_shim.$(SHLIB_EXT) pty_shim.$(SHLIB_EXT) vterm_shim.$(SHLIB_EXT) jerboa_scintilla_shim.$(SHLIB_EXT) jerboa_pcre2_shim.$(SHLIB_EXT)
+	JERBOA_NATIVE_A="$(abspath $(JERBOA_NATIVE_A))" JERBUILD="$(VENDORED_JERBUILD)" JEMACS_PINNED_JERBUILD="$(VENDORED_JERBUILD)" "$(DETERMINISTIC_JERBUILD)" build --config .jerbuild
 
 # Generate lib/jerboa-emacs/*.sls from src/jerboa-emacs/*.ss (incremental)
 build: deps
@@ -388,8 +386,16 @@ rebuild: deps
 	$(JERBUILD) src/ lib/ --force
 
 check-generated:
-	$(MAKE) build
-	git diff --exit-code -- lib
+	@before=$$(mktemp); after=$$(mktemp); \
+	trap 'rm -f "$$before" "$$after"' EXIT HUP INT TERM; \
+	git diff --binary -- lib > "$$before"; \
+	$(MAKE) build; \
+	git diff --binary -- lib > "$$after"; \
+	if ! cmp -s "$$before" "$$after"; then \
+		echo "Generated lib/ files changed; run 'make build' and commit them." >&2; \
+		diff -u "$$before" "$$after" || true; \
+		exit 1; \
+	fi
 
 audit:
 	sh scripts/native-dependency-audit.sh
@@ -398,10 +404,10 @@ security:
 	REPO_ROOT="$(CURDIR)" sh scripts/security-check.sh
 
 sbom: build
-	JERBUILD_TOOL="$(JERBUILD_TOOL)" SBOM_DIR="$(SBOM_DIR)" scripts/sbom.sh
+	JERBUILD_TOOL="$(VENDORED_JERBUILD)" SBOM_DIR="$(SBOM_DIR)" scripts/sbom.sh
 
-reproducibility-report:
-	JERBUILD_TOOL="$(JERBUILD_TOOL)" REPRO_DIR="$(REPRO_DIR)" MAKE="$(MAKE)" scripts/reproducibility-report.sh
+reproducibility-report: ensure-vendor-jerboa-tools
+	JERBUILD_TOOL="$(VENDORED_JERBUILD)" REPRO_DIR="$(REPRO_DIR)" MAKE="$(MAKE)" scripts/reproducibility-report.sh
 
 editor-flow-evidence:
 	EDITOR_FLOW_EVIDENCE_DIR="$(EDITOR_FLOW_EVIDENCE_DIR)" scripts/editor-flow-evidence.sh
@@ -700,7 +706,7 @@ test-persist:
 
 test-extra: test-org-duration test-org-element test-org-fold test-org-footnote \
             test-org-lint test-org-num test-org-property test-org-src test-org-tempo \
-            test-vtscreen test-debug-repl test-qt
+            test-vtscreen test-debug-repl test-ipc-security test-repl-shim test-qt
 
 test-org-duration:
 	$(SCHEME) $(LIBDIRS) --script tests/test-org-duration.ss
@@ -735,6 +741,18 @@ test-vtscreen: jerboa_scintilla_shim.$(SHLIB_EXT) libjsh-ffi.$(SHLIB_EXT) libjsh
 test-debug-repl:
 	$(SCHEME) $(LIBDIRS) --program tests/test-debug-repl.ss
 
+test-ipc-security: repl_shim.$(SHLIB_EXT)
+	@tmp=$$(mktemp -d "$${TMPDIR:-/tmp}/jemacs-ipc-test.XXXXXX"); \
+	trap 'rm -rf "$$tmp"' EXIT HUP INT TERM; \
+	JEMACS_TEST_TMPDIR="$$tmp" $(SCHEME) $(LIBDIRS) --program tests/test-ipc-security.ss
+
+test-repl-shim:
+	@mkdir -p .jerboa/test
+	$(CC) -O2 -Wall -Wextra -o .jerboa/test/test-repl-shim \
+		tests/test-repl-shim.c support/repl_shim.c
+	@.jerboa/test/test-repl-shim
+	@rm -f .jerboa/test/test-repl-shim
+
 test-qt: build repl_shim.$(SHLIB_EXT) libqt_shim.$(SHLIB_EXT) vterm_shim.$(SHLIB_EXT) jerboa_qt_shim.$(SHLIB_EXT) libjsh-ffi.$(SHLIB_EXT) libjsh_coreutils_stub.$(SHLIB_EXT) jemacs_tls_stubs.$(SHLIB_EXT) $(NATIVE_LIB) $(SSL_SHIM)
 	QT_QPA_PLATFORM=offscreen $(PRELOAD_ENV) $(SCHEME) $(LIBDIRS) --script tests/test-qt.ss
 	@if [ -f tests/test-qt-part2.ss ]; then \
@@ -796,17 +814,23 @@ scintilla-vendor-deps: $(SCI_VENDOR_SCINTILLA_A) $(SCI_VENDOR_LEXILLA_A) $(SCI_V
 
 $(SCI_VENDOR_SCINTILLA_H):
 	mkdir -p $(SCINTILLA_VENDOR)
-	curl -fsSL $(SCINTILLA_URL) | tar xzf - -C $(SCINTILLA_VENDOR)
+	@tmp=$$(mktemp "$${TMPDIR:-/tmp}/scintilla.XXXXXX"); trap 'rm -f "$$tmp"' EXIT HUP INT TERM; \
+	  curl -fsSL $(SCINTILLA_URL) -o "$$tmp"; \
+	  sh scripts/verify-sha256.sh '$(SCINTILLA_SHA256)' "$$tmp"; \
+	  tar xzf "$$tmp" -C $(SCINTILLA_VENDOR)
 
 $(SCI_VENDOR_LEXILLA_H):
 	mkdir -p $(SCINTILLA_VENDOR)
-	curl -fsSL $(LEXILLA_URL) | tar xzf - -C $(SCINTILLA_VENDOR)
+	@tmp=$$(mktemp "$${TMPDIR:-/tmp}/lexilla.XXXXXX"); trap 'rm -f "$$tmp"' EXIT HUP INT TERM; \
+	  curl -fsSL $(LEXILLA_URL) -o "$$tmp"; \
+	  sh scripts/verify-sha256.sh '$(LEXILLA_SHA256)' "$$tmp"; \
+	  tar xzf "$$tmp" -C $(SCINTILLA_VENDOR)
 
 $(SCI_VENDOR_TB_H): $(SCI_VENDOR_SCINTILLA_H)
-	git clone --depth 1 $(SCINTILLA_TB_URL) $(SCI_VENDOR_TB_DIR)
+	sh support/fetch-locked-dependency.sh $(SCINTILLA_TB_URL) $(SCINTILLA_TERMBOX_REV) $(SCI_VENDOR_TB_DIR)
 
 $(SCI_VENDOR_TERMBOX_H): $(SCI_VENDOR_TB_H)
-	git clone --depth 1 $(TERMBOX_URL) $(SCI_VENDOR_TERMBOX_DIR)
+	sh support/fetch-locked-dependency.sh $(TERMBOX_URL) $(TERMBOX_NEXT_REV) $(SCI_VENDOR_TERMBOX_DIR)
 
 $(SCI_VENDOR_TERMBOX_A): $(SCI_VENDOR_TERMBOX_H)
 	$(MAKE) -C $(SCI_VENDOR_TERMBOX_DIR) FLAGS="-std=c99 -pedantic -Wall -Werror -g -fPIC -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700"
@@ -925,7 +949,7 @@ check-docker:
 
 # Disabled legacy container dependency image target.
 # Takes ~45-60 min: Qt6 static + QScintilla + Chez Scheme + all shims.
-CHEZ_COMMIT ?= 902a10098603481afce0ec8588114234c09d6318
+CHEZ_COMMIT ?= $(CHEZ_REV)
 
 podman-deps: check-podman
 	$(PODMAN) build $(PODMAN_BUILD_FLAGS) \
@@ -972,17 +996,17 @@ linux-amd64:
 	  *) $(MAKE) jemacs-linux-amd64-remote ;; \
 	esac
 
-jemacs-linux-amd64-local: ensure-jerboa-tools vendor-deps
+jemacs-linux-amd64-local: ensure-vendor-jerboa-tools vendor-deps $(NATIVE_LIB)
 	@case "$$(uname -s)-$$(uname -m)" in \
 	  Linux-x86_64|Linux-amd64) ;; \
 	  *) echo "ERROR: run locally on Linux amd64, or use 'make jemacs-linux-amd64-remote LINUX_BUILD_HOST=host'." >&2; exit 1 ;; \
 	esac
-	JERBUILD="$(JERBUILD_TOOL)" "$(JERBUILD_TOOL)" build --config .jerbuild.linux-amd64
+	JERBOA_NATIVE_A="$(abspath $(JERBOA_NATIVE_A))" JERBUILD="$(VENDORED_JERBUILD)" "$(VENDORED_JERBUILD)" build --config .jerbuild.linux-amd64
 	@ls -lh jemacs-linux-amd64
 	@file jemacs-linux-amd64
 
 jemacs-linux-amd64-remote:
-	$(SSH) $(LINUX_BUILD_HOST) 'set -e; if [ -d "$(LINUX_BUILD_ROOT)/.git" ]; then cd "$(LINUX_BUILD_ROOT)" && git fetch origin && git checkout "$(REMOTE_BRANCH)" && git pull --ff-only; else mkdir -p "$$(dirname "$(LINUX_BUILD_ROOT)")" && git clone "$(REMOTE_REPO_URL)" "$(LINUX_BUILD_ROOT)" && cd "$(LINUX_BUILD_ROOT)" && git checkout "$(REMOTE_BRANCH)"; fi; make linux-amd64'
+	$(SSH) $(LINUX_BUILD_HOST) 'set -e; test -n "$(REMOTE_COMMIT)"; if [ -d "$(LINUX_BUILD_ROOT)/.git" ]; then cd "$(LINUX_BUILD_ROOT)"; else test ! -e "$(LINUX_BUILD_ROOT)"; mkdir -p "$(LINUX_BUILD_ROOT)"; git -C "$(LINUX_BUILD_ROOT)" init -q; git -C "$(LINUX_BUILD_ROOT)" remote add origin "$(REMOTE_REPO_URL)"; cd "$(LINUX_BUILD_ROOT)"; fi; test "$$(git remote get-url origin)" = "$(REMOTE_REPO_URL)"; git fetch --no-tags --depth 1 origin "$(REMOTE_COMMIT)"; test "$$(git rev-parse FETCH_HEAD)" = "$(REMOTE_COMMIT)"; git checkout --detach "$(REMOTE_COMMIT)"; make linux-amd64'
 	$(SCP) $(LINUX_BUILD_HOST):$(LINUX_BUILD_ROOT)/jemacs-linux-amd64 ./jemacs-linux-amd64
 	@ls -lh jemacs-linux-amd64
 	@file jemacs-linux-amd64
@@ -994,17 +1018,17 @@ linux-arm64:
 	     $(MAKE) jemacs-linux-arm64-remote ;; \
 	esac
 
-jemacs-linux-arm64-local: ensure-jerboa-tools vendor-deps
+jemacs-linux-arm64-local: ensure-vendor-jerboa-tools vendor-deps $(NATIVE_LIB)
 	@case "$$(uname -s)-$$(uname -m)" in \
 	  Linux-aarch64|Linux-arm64) ;; \
 	  *) echo "ERROR: run locally on Linux arm64, or set LINUX_ARM64_BUILD_HOST and use make linux-arm64." >&2; exit 1 ;; \
 	esac
-	JERBUILD="$(JERBUILD_TOOL)" "$(JERBUILD_TOOL)" build --config .jerbuild.linux-arm64
+	JERBOA_NATIVE_A="$(abspath $(JERBOA_NATIVE_A))" JERBUILD="$(VENDORED_JERBUILD)" "$(VENDORED_JERBUILD)" build --config .jerbuild.linux-arm64
 	@ls -lh jemacs-linux-arm64
 	@file jemacs-linux-arm64
 
 jemacs-linux-arm64-remote:
-	$(SSH) $(LINUX_ARM64_BUILD_HOST) 'set -e; if [ -d "$(LINUX_ARM64_BUILD_ROOT)/.git" ]; then cd "$(LINUX_ARM64_BUILD_ROOT)" && git fetch origin && git checkout "$(REMOTE_BRANCH)" && git pull --ff-only; else mkdir -p "$$(dirname "$(LINUX_ARM64_BUILD_ROOT)")" && git clone "$(REMOTE_REPO_URL)" "$(LINUX_ARM64_BUILD_ROOT)" && cd "$(LINUX_ARM64_BUILD_ROOT)" && git checkout "$(REMOTE_BRANCH)"; fi; make linux-arm64'
+	$(SSH) $(LINUX_ARM64_BUILD_HOST) 'set -e; test -n "$(REMOTE_COMMIT)"; if [ -d "$(LINUX_ARM64_BUILD_ROOT)/.git" ]; then cd "$(LINUX_ARM64_BUILD_ROOT)"; else test ! -e "$(LINUX_ARM64_BUILD_ROOT)"; mkdir -p "$(LINUX_ARM64_BUILD_ROOT)"; git -C "$(LINUX_ARM64_BUILD_ROOT)" init -q; git -C "$(LINUX_ARM64_BUILD_ROOT)" remote add origin "$(REMOTE_REPO_URL)"; cd "$(LINUX_ARM64_BUILD_ROOT)"; fi; test "$$(git remote get-url origin)" = "$(REMOTE_REPO_URL)"; git fetch --no-tags --depth 1 origin "$(REMOTE_COMMIT)"; test "$$(git rev-parse FETCH_HEAD)" = "$(REMOTE_COMMIT)"; git checkout --detach "$(REMOTE_COMMIT)"; make linux-arm64'
 	$(SCP) $(LINUX_ARM64_BUILD_HOST):$(LINUX_ARM64_BUILD_ROOT)/jemacs-linux-arm64 ./jemacs-linux-arm64
 	@ls -lh jemacs-linux-arm64
 	@file jemacs-linux-arm64
@@ -1014,12 +1038,12 @@ jemacs-linux-arm64: linux-arm64
 
 freebsd: freebsd-amd64
 
-freebsd-amd64: ensure-jerboa-tools vendor-deps
+freebsd-amd64: ensure-vendor-jerboa-tools vendor-deps $(NATIVE_LIB)
 	@case "$$(uname -s)-$$(uname -m)" in \
 	  FreeBSD-amd64|FreeBSD-x86_64) ;; \
 	  *) echo "ERROR: build freebsd-amd64 natively on a FreeBSD amd64 host." >&2; exit 1 ;; \
 	esac
-	JERBUILD="$(JERBUILD_TOOL)" "$(JERBUILD_TOOL)" build --config .jerbuild.freebsd-amd64
+	JERBOA_NATIVE_A="$(abspath $(JERBOA_NATIVE_A))" JERBUILD="$(VENDORED_JERBUILD)" "$(VENDORED_JERBUILD)" build --config .jerbuild.freebsd-amd64
 	@ls -lh jemacs-freebsd-amd64
 	@file jemacs-freebsd-amd64
 
@@ -1177,15 +1201,16 @@ linux-static-qt-podman: vendor-deps ensure-podman-deps
 	  -v $(JSH_COREUTILS_LIB):/host-jsh-coreutils.a:ro \
 	  $(AWS_PODMAN_RUN_MOUNTS) \
 	  $(DEPS_IMAGE) \
-	  sh -c "apk add --no-cache libvterm-dev libvterm-static openssl-dev; \
-	         if [ ! -f /usr/lib/libssl.a ]; then \
-	           echo 'Building static OpenSSL (one-time)...' && \
-	           cd /tmp && wget -q https://www.openssl.org/source/openssl-3.3.2.tar.gz && \
-	           tar xf openssl-3.3.2.tar.gz && cd openssl-3.3.2 && \
-	           ./Configure linux-x86_64 no-shared no-tests no-apps -O2 --prefix=/usr && \
-	           make -j$(nproc) && cp libssl.a libcrypto.a /usr/lib/ && \
-	           cd / && rm -rf /tmp/openssl-3.3.2*; \
-	         fi; \
+	  sh -c "set -eu; apk add --no-cache libvterm-dev libvterm-static openssl-dev; \
+	         echo 'Building pinned static OpenSSL...' && \
+	         cd /tmp && wget -q https://github.com/openssl/openssl/releases/download/openssl-$(OPENSSL_VERSION)/openssl-$(OPENSSL_VERSION).tar.gz && \
+	         echo '$(OPENSSL_SHA256)  openssl-$(OPENSSL_VERSION).tar.gz' | sha256sum -c - && \
+	         tar xf openssl-$(OPENSSL_VERSION).tar.gz && cd openssl-$(OPENSSL_VERSION) && \
+	         ./Configure no-shared no-tests -O2 --prefix=/usr && \
+	         make -j$$(nproc) && ./apps/openssl version | grep -F 'OpenSSL $(OPENSSL_VERSION)' && \
+	         ./apps/openssl version -a > /usr/lib/jemacs-openssl-build.txt && \
+	         cp libssl.a libcrypto.a /usr/lib/ && \
+	         cd / && rm -rf /tmp/openssl-$(OPENSSL_VERSION)*; \
 	         cp /host-jsh-coreutils.a /deps/jsh/libjsh_coreutils.a; \
 	         cp -a /host-jsh-root/. /deps/jsh/; \
 	         mkdir -p /deps/jsh/vendor/jsqlite && cp -a /host-jsqlite/. /deps/jsh/vendor/jsqlite/; \
diff --git a/README.md b/README.md
index f9c1403..43f8cee 100644
--- a/README.md
+++ b/README.md
@@ -64,8 +64,14 @@ make jerbuild-check
 `ensure-jerboa-tools` follows the same convention as `jerboa-code` and
 `jerboa-shell`: it prefers project-local tools, a sibling `../jerboa` build, or
 `PATH`, and otherwise downloads `jerboa-${JERBOA_VERSION}` into `.jerboa/bin`.
+Downloaded toolchains are accepted only when their target-specific SHA-256
+matches `dependencies.lock`; a checksum fetched beside the artifact is not a
+trust root.
 The `.jerbuild*` configs describe native target-host builds for macOS/Linux/
-FreeBSD without requiring an installed full Jerboa runtime.
+FreeBSD without requiring an installed full Jerboa runtime. Release targets use
+the exact vendored `jerbuild` and statically link its pinned
+`libjerboa_native.a`; failing crypto/TLS placeholder stubs are not part of
+release binaries.
 
 ### Cross and target builds
 
@@ -101,8 +107,11 @@ jerbuild build --config .jerbuild.freebsd-amd64
 make vendor-deps
 ```
 
-This clones required dependencies into `vendor/` from their SourceHut remotes.
-The default `make build` target runs this first.
+This fetches the exact full commits recorded in `dependencies.lock` into
+`vendor/` and rejects an existing checkout at any other revision. Archive,
+container, static OpenSSL 3.5.7 LTS, Chez, and Tree-sitter inputs are likewise
+digest- or commit-pinned. The default `make build` target verifies this graph
+first; `make vendor-update` never follows a mutable upstream head.
 
 ### Build library modules
 
@@ -143,6 +152,15 @@ make run-qt
 
 Requires the Qt backend build (see below).
 
+### Authenticated local control sockets
+
+`--repl PORT` enables the full-power loopback debug REPL. It always requires a
+fresh 256-bit token, atomically published with the port in the mode-0600
+`~/.jerboa-repl-port` file. The always-on `jerboa-client` IPC endpoint uses an
+independent token in `~/.jemacs-server`. Both endpoints enforce bounded input,
+deadlines, and one active connection. See
+[docs/repl-server.md](docs/repl-server.md) for the protocol and threat model.
+
 ### Build Qt backend
 
 ```bash
@@ -159,6 +177,9 @@ The Qt backend consists of 45 modules in `src/jerboa-emacs/qt/` (~48,801 lines).
 make test
 ```
 
+The suite includes hostile authentication, overlong-frame, traversal, endpoint
+symlink, private-temp-file, and shell-metacharacter argv tests.
+
 This runs: test-tier0, test-tier2, test-tier3, test-tier4, test-tier5, test-org, test-extra.
 
 ### Run specific test suites
diff --git a/SECURITY.md b/SECURITY.md
index 571eaec..2473c53 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -9,19 +9,33 @@ production-readiness tracker gates are complete.
 
 - Source files are edited in `src/`; generated `lib/*.sls` files must stay in
   sync with source and must not be hand-edited.
-- Production readers use `:jerboa/reader` with read-eval disabled where command
-  input or socket input is parsed.
+- Production command/socket parsers use the bounded Jerboa reader with explicit
+  input, nesting, container, string, and symbol limits. Chez itself rejects
+  `#.` syntax and does not export a `read-eval` parameter.
 - File path sinks reject embedded NUL bytes before opening, deleting, loading,
   saving, chmoding, or shelling out on caller-provided paths.
-- Shell/process helpers use fixed executables and argv-style calls where the
-  editor invokes host tools such as `git`, `rg`, `nc`, `chmod`, and launchers.
-- The debug REPL socket is local-development tooling. Do not expose it outside a
-  local trust boundary, and do not enable command shells in static REPL builds
-  unless `JERBOA_REPL_ENABLE_SHELL=1` is an intentional operator choice.
+- Shell/process helpers use absolute, metadata-checked executables and argv
+  vectors. Filename, remote-path, URL, and working-directory data is never
+  interpolated into a shell command. Explicit interactive shell commands remain
+  an intentional code-execution feature.
+- Debug REPL and editor IPC connections use mandatory authentication with
+  independent 256-bit tokens, private atomic endpoint files, strict input and
+  queue caps, and idle/total deadlines. The full-power REPL remains
+  local-development tooling and must never be forwarded or proxied.
+- Sudo-save staging uses unpredictable `O_EXCL|O_NOFOLLOW` files created mode
+  0600, validated as regular/single-link, fsynced, and removed under guaranteed
+  cleanup. Privileged copies use fixed executable argv vectors.
 - Native FFI bindings load lazily through `(jerboa ffi)` and must not run native
   loader side effects at library import time.
+- Native `.jerbuild*` release binaries use the locked vendored toolchain and
+  statically link its real Rust crypto/TLS archive; fail-only native stubs are
+  excluded from those release manifests.
 - Blocking native calls are collect-safe where they can wait on terminals,
   sockets, subprocesses, Qt, Scintilla, or ptys.
+- `dependencies.lock` pins every cloned build input, release-toolchain digest,
+  source archive digest, and container digest. The static TLS build uses
+  OpenSSL 3.5.7 LTS and verifies the upstream archive SHA-256 before building;
+  the image records and checks the actual linked build version.
 
 ## Sensitive Data
 
diff --git a/dependencies.lock b/dependencies.lock
new file mode 100644
index 0000000..96236a4
--- /dev/null
+++ b/dependencies.lock
@@ -0,0 +1,57 @@
+# Reviewed immutable Git inputs for vendor-deps. Update each revision in a
+# dedicated dependency-review change; release builds fail on any mismatch.
+JERBOA_REV := 37426d9bb88e0cef607141605ae4bc928d5ccb24
+JERBOA_SHELL_REV := 413c59d5ed4bb61229868793d04a7eb8cd4c4219
+JERBOA_PCRE2_REV := 719751bc7ef3e408e9da1a260393859f8bed9ea8
+JERBOA_SCINTILLA_REV := 96745c5ea69c95f1c4fa2ce80f43e9cdf59a55a6
+JERBOA_QT_REV := dc86e8354ed3406066e9ce7482c0d1d43e4c6272
+JERBOA_SSL_REV := f466a3149f6c9a0e6f8dd202e00bb746a9bacd2c
+JERBOA_HTTPS_REV := 966c2f83e746e0153c7a843b235d8a2e466e1560
+JSQLITE_REV := 63d737101b3b74bff3b9db75cc35f34bff6df0b7
+JERBOA_LSP_REV := 2214f9c4e18abd714426fcf712dcfa55e66a8bf6
+JERBOA_AWS_REV := 38fd942574800c1d061dad362594bb9c8bc69727
+
+JSH_COREUTILS_REV := 6cf228c84f2a7141e41df366b341423fd3f8ad09
+JSH_AWK_REV := b5bf6e69f1c7317f629ead7aa6d3cab672de919e
+JSH_SED_REV := 34b3311f7fa3297afeb1cc9609029389d63ad7e4
+JSH_AWS_REV := 3f1181943c8c397d4922e5a090168dd749723528
+JSH_YUBIKEY_REV := e448afca926ca9ad40fd98effa8d6264cb1019f5
+JSH_SSH_REV := b47b3d0ee53777444de3e7b2b238d4b744058221
+JSH_FUSE_REV := 3b7b3a8d5d76100ea1526acefe7f696c96a62b2b
+
+# Static source archives and security-sensitive toolchain inputs.
+OPENSSL_VERSION := 3.5.7
+OPENSSL_SHA256 := a8c0d28a529ca480f9f36cf5792e2cd21984552a3c8e4aa11a24aa31aeac98e8
+LIBXAU_SHA256 := 74d0e4dfa3d39ad8939e99bda37f5967aba528211076828464d2777d477fc0fb
+XCB_UTIL_SHA256 := 5abe3bbbd8e54f0fa3ec945291b7e8fa8cfd3cccc43718f8758430f94126e512
+QTBASE_SHA256 := 56001b905601bb9023d399f3ba780d7fa940f3e4861e496a7c490331f49e0b80
+QSCINTILLA_SHA256 := dfe13c6acc9d85dfcba76ccc8061e71a223957a6c02f3c343b30a9d43a4cdd4d
+ALPINE_3_21_IMAGE_DIGEST := sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d
+UBUNTU_24_04_IMAGE_DIGEST := sha256:4fbb8e6a8395de5a7550b33509421a2bafbc0aab6c06ba2cef9ebffbc7092d90
+JERBOA_IMAGE_DIGEST := sha256:9c4034f1296601d5a9f7b219a0decdd5d62862a2be4347976b3d96b13ed7701e