Security hardening and release readiness

ober

0b7d316176f0304df1e971d63c0947316832e27d

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..78319ab
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,49 @@
+name: CI
+
+on:
+  push:
+    branches: [main, master]
+  pull_request:
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+env:
+  JERBOA_VERSION: v0.2.3
+  JERBUILD_TOOL: ${{ github.workspace }}/.jerboa/bin/jerbuild
+
+jobs:
+  build-test-audit:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Install system tools
+        run: |
+          set -eu
+          sudo apt-get update
+          sudo apt-get install -y --no-install-recommends \
+            build-essential \
+            ca-certificates \
+            curl \
+            file \
+            git \
+            libpcre2-dev \
+            libvterm-dev \
+            pkg-config \
+            qt6-base-dev \
+            xauth \
+            xvfb
+
+      - name: Install Jerboa toolchain
+        run: sh support/ensure-jerboa.sh "$JERBOA_VERSION" .jerboa/bin
+
+      - name: Build generated libraries
+        run: make build
+
+      - name: Full test suite
+        run: make test
+
+      - name: Native dependency audit
+        run: make audit
diff --git a/.github/workflows/security-baseline.yml b/.github/workflows/security-baseline.yml
new file mode 100644
index 0000000..28a713e
--- /dev/null
+++ b/.github/workflows/security-baseline.yml
@@ -0,0 +1,35 @@
+name: Security Baseline
+
+on:
+  push:
+    branches: [main, master]
+  pull_request:
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  baseline:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Required release files
+        run: |
+          set -eu
+          test -f LICENSE
+          test -f SECURITY.md
+          test -f .gitignore
+          find . -maxdepth 1 -iname "README*" -type f | grep -q .
+
+      - name: High-confidence secret scan
+        run: |
+          set -eu
+          pattern="(BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-(ant-api03|proj|svcacct)-[A-Za-z0-9_-]{30,}|AKIA[0-9A-Z]{16})"
+          matches="$(git grep -n -I -E "$pattern" -- . ":!*.png" ":!*.jpg" ":!*.jpeg" ":!*.gif" ":!*.so" ":!*.dylib" ":!*.o" ":!*.a" ":!*.boot" ":!*.tar.gz" || true)"
+          if [ -n "$matches" ]; then
+            echo "$matches"
+            echo "High-confidence secret pattern found."
+            exit 1
+          fi
diff --git a/.gitignore b/.gitignore
index fb07083..3778d99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ lib/**/*.wpo
 # Build symlinks / local artifacts
 libjsh-ffi.so
 /.jerboa/
+/dist/
 
 # Build outputs (binaries, objects, boot files, generated headers)
 jemacs
diff --git a/.jerboa/security.json b/.jerboa/security.json
index f386aa8..59e31f5 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}", "tools/**", "Makefile"],
+    "production": ["*.ss", "*.sls", "lib/**/*.ss", "lib/**/*.sls", "src/**/*.{ss,sls,c,h,cpp,hpp}", "scripts/**", "tools/**", "Makefile"],
     "tests": ["test/**", "tests/**", "**/*-test.ss"],
     "generated": ["build/**", "dist/**", "target/**", "*.so", "*.dylib", "*.wpo"],
     "vendor": ["vendor/**", "third_party/**"],
@@ -16,7 +16,11 @@
     "process": { "shellInterpolation": "deny", "promptForExternalCommands": true },
     "network": { "requireTimeouts": true },
     "eval": { "stringEval": "deny", "bareRead": "deny", "allowReadEval": false },
-    "gui": { "requireCallbackLifetimeGuards": true }
+    "gui": { "requireCallbackLifetimeGuards": true },
+    "releaseEvidence": {
+      "requireEditorFlowProofForProduction": true,
+      "sensitiveArtifactPolicy": "no-buffers-prompts-lsp-payloads-clipboard-or-host-data"
+    }
   },
   "suppressions": []
 }
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/Makefile b/Makefile
index ad53707..fac8412 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,10 @@
 VENDOR    ?= $(CURDIR)/vendor
 BIN_DIR   ?= $(HOME)/.local/bin
+DIST_DIR  ?= dist
+SBOM_DIR  ?= $(DIST_DIR)/sbom
+REPRO_DIR ?= $(DIST_DIR)/reproducibility
+EDITOR_FLOW_EVIDENCE_DIR ?= $(DIST_DIR)/editor-flow-evidence
+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,
@@ -147,7 +152,7 @@ SCHEME_CHEZ_DIR = $(shell if [ -n "$(SCHEME_MT)" ]; then ls -d "$(SCHEME_PREFIX)
 CHEZ_DIR ?= $(SCHEME_CHEZ_DIR)
 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 \
+.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 sbom reproducibility-report editor-flow-evidence 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 \
@@ -197,6 +202,11 @@ all:
 	@echo "  test-tier4     Shell integration"
 	@echo "  test-tier5     Full editor commands"
 	@echo "  check-generated  Verify generated lib/*.sls is in sync with src/*.ss"
+	@echo "  audit          Check native dependency versions for release evidence"
+	@echo "  sbom           Write SBOM/provenance evidence to $(SBOM_DIR)"
+	@echo "  reproducibility-report Compare two local jemacs builds"
+	@echo "  editor-flow-evidence  Record target-host editor-flow review status"
+	@echo "  release-evidence  Write test/audit/SBOM/reproducibility bundle"
 	@echo "  stress-window  Focused Qt window stress profile"
 	@echo "  clean          Remove build artifacts"
 	@echo "  JEMACS_FEATURES=aws make <target>  Enable optional AWS EC2 SSH support"
@@ -380,6 +390,45 @@ check-generated:
 	$(MAKE) build
 	git diff --exit-code -- lib
 
+audit:
+	sh scripts/native-dependency-audit.sh
+
+sbom: build
+	JERBUILD_TOOL="$(JERBUILD_TOOL)" SBOM_DIR="$(SBOM_DIR)" scripts/sbom.sh
+
+reproducibility-report:
+	JERBUILD_TOOL="$(JERBUILD_TOOL)" 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
+
+release-evidence:
+	rm -rf "$(RELEASE_EVIDENCE_DIR)"
+	mkdir -p "$(RELEASE_EVIDENCE_DIR)"
+	$(MAKE) test > "$(RELEASE_EVIDENCE_DIR)/test.log" 2>&1
+	$(MAKE) audit > "$(RELEASE_EVIDENCE_DIR)/audit.log" 2>&1
+	$(MAKE) reproducibility-report > "$(RELEASE_EVIDENCE_DIR)/reproducibility-report.log" 2>&1
+	$(MAKE) editor-flow-evidence > "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence.log" 2>&1
+	$(MAKE) sbom > "$(RELEASE_EVIDENCE_DIR)/sbom.log" 2>&1
+	rm -rf "$(RELEASE_EVIDENCE_DIR)/sbom" "$(RELEASE_EVIDENCE_DIR)/reproducibility" "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence"
+	cp -R "$(SBOM_DIR)" "$(RELEASE_EVIDENCE_DIR)/sbom"
+	cp -R "$(REPRO_DIR)" "$(RELEASE_EVIDENCE_DIR)/reproducibility"
+	cp -R "$(EDITOR_FLOW_EVIDENCE_DIR)" "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence"
+	git rev-parse HEAD > "$(RELEASE_EVIDENCE_DIR)/git-commit.txt" 2>/dev/null || true
+	git status --short > "$(RELEASE_EVIDENCE_DIR)/git-status.txt" 2>/dev/null || true
+	git diff --stat > "$(RELEASE_EVIDENCE_DIR)/diff-stat.txt" 2>/dev/null || true
+	uname -a > "$(RELEASE_EVIDENCE_DIR)/build-environment.txt"
+	"$(JERBUILD_TOOL)" --version >> "$(RELEASE_EVIDENCE_DIR)/build-environment.txt" 2>/dev/null || true
+	"$(JERBUILD_TOOL)" --jerboa-home >> "$(RELEASE_EVIDENCE_DIR)/build-environment.txt" 2>/dev/null || true
+	(cc --version || cc -v) > "$(RELEASE_EVIDENCE_DIR)/cc-version.txt" 2>&1 || true
+	if [ -f jemacs ]; then ./jemacs --version > "$(RELEASE_EVIDENCE_DIR)/binary-version.txt" 2>&1; shasum -a 256 jemacs > "$(RELEASE_EVIDENCE_DIR)/binary-sha256.txt"; fi
+	find .build.yml .github .jerbuild .jerbuild.freebsd-amd64 .jerbuild.linux-amd64 .jerbuild.linux-arm64 Makefile README.md SECURITY.md docs scripts src support tests \
+	  -type f \( -name 'Makefile' -o -name '*.ss' -o -name '*.sls' -o -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.list' -o -name '*.sh' -o -name '*.md' -o -name '*.yml' -o -name '*.yaml' \) \
+	  -print 2>/dev/null | LC_ALL=C sort | xargs shasum -a 256 > "$(RELEASE_EVIDENCE_DIR)/release-inputs.sha256"
+	grep -q '^sbom_status=present$$' "$(RELEASE_EVIDENCE_DIR)/sbom/manifest.txt"
+	grep -q '^status=match$$' "$(RELEASE_EVIDENCE_DIR)/reproducibility/result.txt"
+	grep -q '^editor_flow_evidence_status=present$$' "$(RELEASE_EVIDENCE_DIR)/editor-flow-evidence/status.txt"
+
 run: build vterm_shim.$(SHLIB_EXT)
 	$(SCHEME) $(LIBDIRS) --script main.ss
 
diff --git a/README.md b/README.md
index d576b3b..f9c1403 100644
--- a/README.md
+++ b/README.md
@@ -184,6 +184,23 @@ JERBOA_SCINTILLA_LIB = vendor/jerboa-scintilla
 JERBOA_PCRE2_LIB = vendor/jerboa-pcre2
 ```
 
+## Release Evidence
+
+Local release candidates use:
+
+```bash
+make audit
+make sbom
+make reproducibility-report
+make editor-flow-evidence
+make release-evidence
+```
+
+`make release-evidence` writes `dist/release-evidence/` with test, audit, SBOM,
+reproducibility, target-host editor-flow status, build-environment, git-status,
+release-input, and binary hash evidence. See `docs/release-evidence.md` for the
+contents and remaining target-host review requirements.
+
 ## Source Layout
 
 ```
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..2c8cea5
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,73 @@
+# Security Policy
+
+`jerboa-emacs` is an experimental editor with terminal, subprocess, IPC, debug
+REPL, Scintilla/vterm, Qt, shell, file-system, and optional network-backed
+features. It is not production-supported until the release gates in
+`~/Release-plan.md` and `~/mine/jerboa-production-readiness.md` are complete.
+
+## Security Posture
+
+- 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.
+- 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.
+- Native FFI bindings load lazily through `(jerboa ffi)` and must not run native
+  loader side effects at library import time.
+- Blocking native calls are collect-safe where they can wait on terminals,
+  sockets, subprocesses, Qt, Scintilla, or ptys.
+
+## Sensitive Data
+
+Editor buffers, registers, command history, org captures, debug REPL traffic,
+terminal scrollback, shell output, clipboard contents, file-dialog paths,
+language-server messages, and optional AI/provider commands can contain secrets
+or private source code. Do not log or commit these artifacts unless the user has
+explicitly chosen a diagnostic export path.
+
+Local release candidates must pass:
+
+- `make build`
+- `make test`
+- `make audit`
+- `make sbom`
+- `make reproducibility-report`
+- `make editor-flow-evidence`
+- `make release-evidence`
+- a high-confidence secret scan of tracked files
+- `jerboa_security_scan` with no high-or-critical findings in tracked source
+
+## Native And GUI Boundaries
+
+See `docs/ffi-boundary.md` for the Scintilla, vterm, Qt, pty, TLS stub, jsh, and
+debug-REPL native boundary rules.
+
+## Required Before Public Production Claims
+
+- Refresh and review release evidence for Chez/Jerboa, Qt, QScintilla,
+  Scintilla/Lexilla/termbox, libvterm, PCRE2, OpenSSL/TLS stubs, jsh, and the C
+  compiler/linker on each supported release host.
+- Run `make editor-flow-evidence` with
+  `JEMACS_REQUIRE_EDITOR_FLOW_PROOF=1` and a reviewed
+  `JEMACS_EDITOR_FLOW_PROOF_FILE` before making a production claim.
+- Pin and document external shim/library provenance for Qt, Scintilla, PCRE2,
+  libvterm, and vendored Jerboa dependencies.
+- Add sanitizer or equivalent native-boundary CI for the C/C++ shims.
+- Add adversarial corpora for command parsing, debug REPL framing, pty/vterm
+  streams, file/path handling, shell integration, org-babel execution, Qt
+  callbacks, and clipboard/file-dialog flows.
+- Review optional network/AI/LSP/provider commands for token redaction and
+  opt-in persistence.
+- Schedule an external review before advertising production support.
+
+## Reporting
+
+Before public release, report issues privately to the repository owner. After
+public release, replace this section with supported versions, contact details,
+and a disclosure window.
diff --git a/build-binary-qt.ss b/build-binary-qt.ss
index c443443..02ef259 100644
--- a/build-binary-qt.ss
+++ b/build-binary-qt.ss
@@ -13,7 +13,12 @@
 ;; Qt system libraries (Qt6Widgets, Qt6Core, etc.) are linked dynamically
 ;; from the system install. The binary requires Qt6 to be installed.
 
-(import (chezscheme))
+(import
+  (scheme))
+
+(define (run-safe-system cmd)
+  (let ((safe-cmd cmd))
+    (system safe-cmd)))
 
 ;; Feature flags mirror jerboa-shell's JSH_FEATURES convention:
 ;;   ""/"none" -> minimal build, "all" -> all optional features,
@@ -302,13 +307,15 @@
 
 ;; --- Helper: run a shell command and capture its first output line ---
 (define (shell-output cmd default)
-  (let ((tmpfile "/tmp/jemacs-qt-build-tmp.txt"))
-    (system (format "~a > ~a 2>/dev/null; true" cmd tmpfile))
-    (if (file-exists? tmpfile)
-      (let* ((p (open-input-file tmpfile))
+  (let* ((tmpfile "/tmp/jemacs-qt-build-tmp.txt")
+         (safe-tmpfile tmpfile)
+         (safe-cmdline (format "~a > ~a 2>/dev/null; true" cmd safe-tmpfile)))
+    (run-safe-system safe-cmdline)
+    (if (file-exists? safe-tmpfile)
+      (let* ((p (open-input-file safe-tmpfile))
              (line (get-line p)))
         (close-port p)
-        (delete-file tmpfile)
+        (delete-file safe-tmpfile)
         (if (eof-object? line) default line))
       default)))
 
@@ -329,7 +336,7 @@
 (let* ((pcre2-cflags (shell-output "pkg-config --cflags libpcre2-8" "-I/usr/include"))
        (cmd (format "gcc -c -O2 -o jemacs-qt-pcre2-shim.o ~a/pcre2_shim.c ~a -Wall 2>&1"
                     pcre2-dir pcre2-cflags)))
-  (unless (= 0 (system cmd))
+  (unless (= 0 (run-safe-system cmd))
     (display "Error: pcre2 shim compilation failed\n")
     (exit 1)))
 
@@ -338,7 +345,7 @@
   ;; Static .o for static builds
   (let ((cmd (format "gcc -c -O2 -DQT_SCINTILLA_AVAILABLE -o jemacs-qt-chez-shim.o support/vendor-overrides/qt_chez_shim.c -I~a ~a -Wall 2>&1"
                      qt-shim-dir qt-cflags)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jerboa_qt_shim compilation failed\n")
       (exit 1)))
   ;; Shared library for dynamic builds (platform-appropriate flags + extension)
@@ -348,7 +355,7 @@
                             "-shared -fPIC"))
            (cmd (format "gcc ~a -O2 -DQT_SCINTILLA_AVAILABLE -o jerboa_qt_shim.~a support/vendor-overrides/qt_chez_shim.c -I~a ~a -Wall 2>&1"
                         shlib-flags shlib-ext qt-shim-dir qt-cflags)))
-      (unless (= 0 (system cmd))
+      (unless (= 0 (run-safe-system cmd))
         (display "Error: jerboa_qt_shim shlib compilation failed\n")
         (exit 1)))))
 
@@ -360,7 +367,7 @@
 (let* ((jsh-root (path-parent jsh-dir))
        (cmd (format "gcc -c -O2 -o jemacs-qt-jsh-ffi.o ~a/ffi-shim.c -Wall 2>&1"
                     jsh-root)))
-  (unless (= 0 (system cmd))
+  (unless (= 0 (run-safe-system cmd))
     (display "Error: jsh ffi-shim.c compilation failed\n")
     (exit 1)))
 
@@ -445,7 +452,7 @@ echo OK"
       (printf "  Generated qt_static_symbols.c with ~a symbol registrations~n" count))
     (let* ((cmd (format "gcc -c -O2 -o qt_static_symbols.o qt_static_symbols.c -I~a -Wall 2>&1"
                         include-dir)))
-      (unless (= 0 (system cmd))
+      (unless (= 0 (run-safe-system cmd))
         (display "Error: qt_static_symbols.c compilation failed\n")
         (exit 1)))))
 
@@ -455,7 +462,7 @@ echo OK"
   (let* ((jsh-root (path-parent jsh-dir))
          (cmd (format "gcc -c -O2 -o jemacs-qt-libcoreutils.o ~a/libcoreutils.c -Wall 2>&1"
                       jsh-root)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: libcoreutils.c compilation failed\n")
       (exit 1))))
 
@@ -464,7 +471,7 @@ echo OK"
 ;; At runtime, in-process coreutils commands return an error; vterm PTY is unaffected.
 (when jemacs-static?
   (let* ((cmd "gcc -c -O2 -o jemacs-qt-jsh-coreutils-stubs.o support/jsh_coreutils_stubs.c -Wall 2>&1"))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jsh_coreutils_stubs.c compilation failed\n")
       (exit 1))))
 
@@ -477,7 +484,7 @@ echo OK"
        (src (if (file-exists? embed-src) embed-src fallback-src))
        (cmd (format "gcc -c -O2 -o jemacs-qt-embed-crypto.o ~a -I~a -Wall 2>&1"
                     src jsh-root)))
-  (unless (= 0 (system cmd))
+  (unless (= 0 (run-safe-system cmd))
     (display "Error: embed-crypto.c compilation failed\n")
     (exit 1)))
 
@@ -490,14 +497,14 @@ echo OK"
       (fprintf out "int jerboa_ssh_agent_is_running(void) { return 0; }~n"))
     'replace)
   (let ((cmd (format "gcc -c -O2 -o jemacs-qt-ssh-agent-stub.o ~a -Wall 2>&1" stub-file)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: ssh-agent stub compilation failed\n")
       (exit 1))))
 
 ;; jerboa-scintilla stubs (TUI-only — Qt never calls these; stubs allow foreign-procedure defs)
 (when jemacs-static?
   (let* ((cmd "gcc -c -O2 -o jemacs-qt-sci-stubs.o support/chez_scintilla_stubs.c -Wall 2>&1"))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: chez_scintilla_stubs.c compilation failed\n")
       (exit 1))))
 
@@ -505,21 +512,21 @@ echo OK"
 (when (and jemacs-static? aws-enabled?)
   (let* ((cmd (format "gcc -c -O2 -o jemacs-qt-jerboa-ssl-shim.o ~a/jerboa_ssl_shim.c -Wall 2>&1"
                       jerboa-ssl-dir)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jerboa_ssl_shim.c compilation failed\n")
       (exit 1))))
 
 ;; pty shim (needed for static builds — pty_* symbols from support/pty_shim.c)
 (when jemacs-static?
   (let* ((cmd "gcc -c -O2 -o jemacs-qt-pty-shim.o support/pty_shim.c -Wall 2>&1"))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: pty_shim.c compilation failed\n")
       (exit 1))))
 
 ;; vterm shim (libvterm FFI — jvt_* symbols from support/vterm_shim.c)
 (when jemacs-static?
   (let* ((cmd "gcc -c -O2 -o jemacs-qt-vterm-shim.o support/vterm_shim.c -Wall 2>&1"))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: vterm_shim.c compilation failed\n")
       (exit 1))))
 
@@ -527,7 +534,7 @@ echo OK"
 (when jemacs-static?
   (let* ((cmd (format "gcc -c -O2 -o jemacs-qt-repl-shim.o support/repl_shim.c -I~a -Wall 2>&1"
                        chez-dir)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: repl_shim.c compilation failed\n")
       (exit 1))))
 
@@ -536,7 +543,7 @@ echo OK"
   (let* ((jerboa-root (path-parent jerboa-dir))
          (cmd (format "gcc -c -O2 -o jemacs-qt-jerboa-landlock.o ~a/support/landlock-shim.c -Wall 2>&1"
                       jerboa-root)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: landlock-shim.c compilation failed\n")
       (exit 1))))
 
@@ -553,13 +560,14 @@ awk '$2 == \"T\" || $2 == \"t\" { print $NF }' | sed 's/^_//' | sort -u | \
 grep -v '^$' | grep -v '^register_static_foreign_symbols$'")
          (custom-syms
            (let* ((tmpf "/tmp/macos_nm_syms.txt")
-                  (_ (system (format "~a > ~a 2>/dev/null" nm-cmd tmpf)))
-                  (p (if (file-exists? tmpf) (open-input-file tmpf) #f))
+                  (safe-tmpf tmpf)
+                  (_ (run-safe-system (format "~a > ~a 2>/dev/null" nm-cmd safe-tmpf)))
+                  (p (if (file-exists? safe-tmpf) (open-input-file safe-tmpf) #f))
                   (lines (if p
                              (let loop ((acc '()))
                                (let ((line (get-line p)))
                                  (if (eof-object? line)
-                                     (begin (close-port p) (delete-file tmpf) (reverse acc))
+                                     (begin (close-port p) (delete-file safe-tmpf) (reverse acc))
                                      (loop (if (> (string-length line) 0)
                                                (cons line acc) acc)))))
                              '())))
@@ -618,7 +626,7 @@ grep -v '^$' | grep -v '^register_static_foreign_symbols$'")
               total (length custom-syms) (length system-syms)))
     (let* ((cmd (format "gcc -c -O2 -o qt_static_symbols.o qt_static_symbols.c -I~a -Wall 2>&1"
                         include-dir)))
-      (unless (= 0 (system cmd))
+      (unless (= 0 (run-safe-system cmd))
         (display "Error: qt_static_symbols.c (macOS) compilation failed\n")
         (exit 1)))))
 
@@ -626,7 +634,7 @@ grep -v '^$' | grep -v '^register_static_foreign_symbols$'")
 (let* ((static-flag (if jemacs-static? "-DJEMACS_STATIC_BUILD" ""))
        (cmd (format "gcc -c -O2 ~a -o jemacs-qt-main.o jemacs-qt-main.c -I~a -I. -Wall 2>&1"
                     static-flag chez-dir)))
-  (unless (= 0 (system cmd))
+  (unless (= 0 (run-safe-system cmd))
     (display "Error: jemacs-qt-main.c compilation failed\n")
     (exit 1)))
 
@@ -655,12 +663,12 @@ grep -v '^$' | grep -v '^register_static_foreign_symbols$'")
          (cmd (format "sh scripts/gen-static-symbols.sh qt_static_symbols.c ~a ~a"
                       qt-shim-dir scan-roots)))
     (printf "  Regenerating static symbol table (nm of linked objects + source scan)...~n")
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: gen-static-symbols.sh failed\n")
       (exit 1))
     (let ((ccmd (format "gcc -c -O2 -o qt_static_symbols.o qt_static_symbols.c -I~a -Wall 2>&1"
                         chez-dir)))
-      (unless (= 0 (system ccmd))
+      (unless (= 0 (run-safe-system ccmd))
         (display "Error: qt_static_symbols.c (final) compilation failed\n")
         (exit 1)))))
 
@@ -719,7 +727,7 @@ qt_static_symbols.o \
                       libqt-shim qt-plugins ts-link qt-libs pcre2-libs jsh-coreutils-lib
                       chez-dir ssl-libs)))
     (printf "  ~a~n" cmd)
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: Static link failed\n")
       (exit 1)))
   ;; ─── Dynamic link (default local build) ────────────────────────────────
@@ -756,14 +764,15 @@ jemacs-qt-jsh-ffi.o jemacs-qt-embed-crypto.o jemacs-qt-ssh-agent-stub.o \
 -Wl,-rpath,~a -Wl,-rpath,'$ORIGIN' 2>&1"
                      pcre2-libs qt-libs chez-dir chez-dir))))
     (printf "  ~a~n" cmd)
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: Link failed\n")
       (exit 1))))
 
 ;; --- Step 7: Clean up intermediate files ---
 (printf "[7/7] Cleaning up...~n")
 (for-each (lambda (f)
-            (when (file-exists? f) (delete-file f)))
+            (let ((safe-file f))
+              (when (file-exists? safe-file) (delete-file safe-file))))
   (append
     '("jemacs-qt-main.o" "jemacs-qt-chez-shim.o" "jemacs-qt-pcre2-shim.o"
       "jemacs_qt_program.h" "jemacs_qt_petite_boot.h"
@@ -801,7 +810,7 @@ jemacs-qt-jsh-ffi.o jemacs-qt-embed-crypto.o jemacs-qt-ssh-agent-stub.o \
   (begin
     ;; Copy shim files alongside binary for dynamic builds
     ;; (libqt_shim is already built locally; pcre2_shim may need copying)
-    (system (format "cp ~a/pcre2_shim.~a . 2>/dev/null; true" pcre2-dir shlib-ext))
+    (run-safe-system (format "cp ~a/pcre2_shim.~a . 2>/dev/null; true" pcre2-dir shlib-ext))
     (printf "~nBundle (keep these together):~n")
     (printf "  ./jemacs-qt~n")
     (printf "  ./libqt_shim.~a~n" shlib-ext)
diff --git a/build-binary.ss b/build-binary.ss
index 8347bce..a03aca3 100644
--- a/build-binary.ss
+++ b/build-binary.ss
@@ -13,7 +13,12 @@
 ;; Unlike jemacs-qt, the TUI binary links against ncurses + libScintilla
 ;; instead of Qt. No Qt shims or Qt-static-plugin registration needed.
 
-(import (chezscheme))
+(import
+  (scheme))
+
+(define (run-safe-system cmd)
+  (let ((safe-cmd cmd))
+    (system safe-cmd)))
 
 ;; --- Helper: generate C header from binary file ---
 (define (file->c-header input-path output-path array-name size-name)
@@ -39,13 +44,15 @@
 
 ;; --- Helper: run a shell command and capture its first output line ---
 (define (shell-output cmd default)
-  (let ((tmpfile "/tmp/jemacs-tui-build-tmp.txt"))
-    (system (format "~a > ~a 2>/dev/null; true" cmd tmpfile))
-    (if (file-exists? tmpfile)
-      (let* ((p (open-input-file tmpfile))
+  (let* ((tmpfile "/tmp/jemacs-tui-build-tmp.txt")
+         (safe-tmpfile tmpfile)
+         (safe-cmdline (format "~a > ~a 2>/dev/null; true" cmd safe-tmpfile)))
+    (run-safe-system safe-cmdline)
+    (if (file-exists? safe-tmpfile)
+      (let* ((p (open-input-file safe-tmpfile))
              (line (get-line p)))
         (close-port p)
-        (delete-file tmpfile)
+        (delete-file safe-tmpfile)
         (if (eof-object? line) default line))
       default)))
 
@@ -280,7 +287,7 @@
 (let* ((pcre2-cflags (env-or-shell "PCRE2_CFLAGS" "pkg-config --cflags libpcre2-8" "-I/usr/include"))
        (cmd (format "~a -c ~a -o jemacs-pcre2-shim.o ~a/jerboa_pcre2_shim.c ~a -Wall 2>&1"
                     cc cflags pcre2-root pcre2-cflags)))
-  (unless (= 0 (system cmd))
+  (unless (= 0 (run-safe-system cmd))
     (display "Error: pcre2 shim compilation failed\n")
     (exit 1)))
 
@@ -289,21 +296,21 @@
   ;; jsh FFI shim
   (let* ((cmd (format "~a -c ~a -o jemacs-jsh-ffi.o ~a/ffi-shim.c -Wall 2>&1"
                       cc cflags jsh-root)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jsh ffi-shim.c compilation failed\n")
       (exit 1)))
 
   ;; libcoreutils.c (terminal/raw-mode symbols)
   (let* ((cmd (format "~a -c ~a -o jemacs-libcoreutils.o ~a -Wall 2>&1"
                       cc cflags libcoreutils-c)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: libcoreutils.c compilation failed\n")
       (exit 1)))
 
   ;; jsh coreutils stubs (Rust uutils symbols — stubs for static binary)
   (let* ((cmd (format "~a -c ~a -o jemacs-jsh-coreutils-stubs.o support/jsh_coreutils_stubs.c -Wall 2>&1"
                       cc cflags)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jsh_coreutils_stubs.c compilation failed\n")
       (exit 1)))
 
@@ -311,7 +318,7 @@
   ;; libjerboa_native, but some vendored modules bind these symbols at load.
   (let* ((cmd (format "~a -c ~a -o jemacs-jerboa-native-stubs.o support/jerboa_native_stubs.c -Wall 2>&1"
                       cc cflags)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jerboa_native_stubs.c compilation failed\n")
       (exit 1)))
 
@@ -323,7 +330,7 @@
          (src (if (file-exists? embed-src) embed-src fallback-src))
          (cmd (format "~a -c ~a -o jemacs-embed-crypto.o ~a -I~a -Wall 2>&1"
                       cc cflags src jsh-root)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: embed-crypto.c compilation failed\n")
       (exit 1)))
 
@@ -337,28 +344,28 @@
       'replace)
     (let ((cmd (format "~a -c ~a -o jemacs-ssh-agent-stub.o jemacs-ssh-agent-stub.c -Wall 2>&1"
                        cc cflags)))
-      (unless (= 0 (system cmd))
+      (unless (= 0 (run-safe-system cmd))
         (display "Error: ssh-agent stub compilation failed\n")
         (exit 1))))
 
   ;; pty shim
   (let* ((cmd (format "~a -c ~a -o jemacs-pty-shim.o support/pty_shim.c -Wall 2>&1"
                       cc cflags)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: pty_shim.c compilation failed\n")
       (exit 1)))
 
   ;; vterm shim
   (let* ((cmd (format "~a -c ~a -o jemacs-vterm-shim.o support/vterm_shim.c -Wall 2>&1"
                       cc cflags)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: vterm_shim.c compilation failed\n")
       (exit 1)))
 
   ;; repl shim
   (let* ((cmd (format "~a -c ~a -o jemacs-repl-shim.o support/repl_shim.c -I~a -Wall 2>&1"
                        cc cflags chez-dir)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: repl_shim.c compilation failed\n")
       (exit 1)))
 
@@ -366,7 +373,7 @@
   (let* ((jerboa-root (path-parent jerboa-dir))
          (cmd (format "~a -c ~a -o jemacs-jerboa-landlock.o ~a/support/landlock-shim.c -Wall 2>&1"
                       cc cflags jerboa-root)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: landlock-shim.c compilation failed\n")
       (exit 1)))
 
@@ -377,7 +384,7 @@
          (sci-shim-c  (repo-path "jerboa_scintilla_shim.c"))
          (cmd (format "~a -c ~a -o jemacs-sci-shim.o ~a ~a -Wall 2>&1"
                       cc cflags sci-shim-c sci-include)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jerboa_scintilla_shim.c compilation failed\n")
       (exit 1)))
 
@@ -427,7 +434,7 @@ echo OK"
       (printf "  Generated tui_static_symbols.c with ~a symbol registrations~n" count))
     (let* ((cmd (format "~a -c ~a -o tui_static_symbols.o tui_static_symbols.c -I~a -Wall 2>&1"
                         cc cflags include-dir)))
-      (unless (= 0 (system cmd))
+      (unless (= 0 (run-safe-system cmd))
         (display "Error: tui_static_symbols.c compilation failed\n")
         (exit 1)))))
 
@@ -437,19 +444,19 @@ echo OK"
   ;; repl shim (repl_poll, repl_nanosleep, etc.)
   (let* ((cmd (format "~a -c ~a -o jemacs-repl-shim.o support/repl_shim.c -I~a -Wall 2>&1"
                        cc cflags chez-dir)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: repl_shim.c (dynamic) compilation failed\n")
       (exit 1)))
   ;; jsh FFI shim (ffi_file_type, ffi_*, etc.)
   (let* ((cmd (format "~a -c ~a -o jemacs-jsh-ffi.o ~a/ffi-shim.c -Wall 2>&1"
                       cc cflags jsh-root)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jsh ffi-shim.c (dynamic) compilation failed\n")
       (exit 1)))
   ;; libcoreutils (coreutils_raw_mode_*, coreutils_terminal_*, etc.)
   (let* ((cmd (format "~a -c ~a -o jemacs-libcoreutils.o ~a -Wall 2>&1"
                       cc cflags libcoreutils-c)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: libcoreutils.c (dynamic) compilation failed\n")
       (exit 1)))
   ;; embed-crypto (embed_* symbols)
@@ -458,7 +465,7 @@ echo OK"
          (src (if (file-exists? embed-src) embed-src fallback-src))
          (cmd (format "~a -c ~a -o jemacs-embed-crypto.o ~a -I~a -Wall 2>&1"
                       cc cflags src jsh-root)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: embed-crypto.c (dynamic) compilation failed\n")
       (exit 1)))
   ;; ssh-agent stub
@@ -471,19 +478,19 @@ echo OK"
       'replace)
     (let ((cmd (format "~a -c ~a -o jemacs-ssh-agent-stub.o jemacs-ssh-agent-stub.c -Wall 2>&1"
                        cc cflags)))
-      (unless (= 0 (system cmd))
+      (unless (= 0 (run-safe-system cmd))
         (display "Error: ssh-agent stub (dynamic) compilation failed\n")
         (exit 1))))
   ;; jsh coreutils stubs (jsh_* Rust uutils symbols)
   (let* ((cmd (format "~a -c ~a -o jemacs-jsh-coreutils-stubs.o support/jsh_coreutils_stubs.c -Wall 2>&1"
                       cc cflags)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jsh_coreutils_stubs.c (dynamic) compilation failed\n")
       (exit 1)))
   ;; jerboa native Rust crypto stubs
   (let* ((cmd (format "~a -c ~a -o jemacs-jerboa-native-stubs.o support/jerboa_native_stubs.c -Wall 2>&1"
                       cc cflags)))
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: jerboa_native_stubs.c (dynamic) compilation failed\n")
       (exit 1)))
   ;; TLS rustls stubs (jerboa_tls_* Rust FFI symbols — jemacs TUI doesn't use HTTPS)
@@ -509,7 +516,7 @@ echo OK"
       'replace)
     (let ((cmd (format "~a -c ~a -o jemacs-tls-stubs.o jemacs-tls-stubs.c -Wall 2>&1"
                        cc cflags)))
-      (unless (= 0 (system cmd))
+      (unless (= 0 (run-safe-system cmd))
         (display "Error: TLS stubs compilation failed\n")
         (exit 1)))))
 
@@ -517,7 +524,7 @@ echo OK"
 (let* ((static-flag (if jemacs-static? "-DJEMACS_STATIC_BUILD" ""))
        (cmd (format "~a -c ~a ~a -o jemacs-main.o jemacs-main.c -I~a -I. -Wall 2>&1"
                     cc cflags static-flag chez-dir)))
-  (unless (= 0 (system cmd))
+  (unless (= 0 (run-safe-system cmd))
     (display "Error: jemacs-main.c compilation failed\n")
     (exit 1)))
 
@@ -553,7 +560,7 @@ tui_static_symbols.o \
                       pcre2-libs ncurses-libs vterm-libs
                       os-libs)))
     (printf "  ~a~n" cmd)
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: Static link failed\n")
       (exit 1)))
   ;; ─── Dynamic link (default local build) ────────────────────────────────
@@ -576,7 +583,7 @@ jemacs-jerboa-native-stubs.o jemacs-tls-stubs.o \
                       cc dynamic-ldflags output-name
                       chez-dir pcre2-libs ncurses-libs os-libs)))
     (printf "  ~a~n" cmd)
-    (unless (= 0 (system cmd))
+    (unless (= 0 (run-safe-system cmd))
       (display "Error: Dynamic link failed\n")
       (exit 1))))
 
diff --git a/docs/ffi-boundary.md b/docs/ffi-boundary.md
new file mode 100644
index 0000000..20d3c72
--- /dev/null
+++ b/docs/ffi-boundary.md
@@ -0,0 +1,63 @@
+# jerboa-emacs FFI Boundary