Security hardening and release readiness

ober

08b37ba597c5e667c12d95fd0d4a61acc7b9b8ff

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..d3c0483
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,43 @@
+name: CI
+
+on:
+  push:
+    branches: [main, master]
+  pull_request:
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+env:
+  JERBOA_VERSION: v0.2.3
+  JERBUILD: ${{ github.workspace }}/.jerboa/bin/jerbuild
+  JERBOA_SSL_REPO: https://git.sr.ht/~lisp/jerboa-ssl
+  JERBOA_MAIL_REPO: https://git.sr.ht/~lisp/jerboa-mail
+  JERBOA_SSL_DIR: ${{ github.workspace }}/.deps/jerboa-ssl
+  JERBOA_MAIL_DIR: ${{ github.workspace }}/.deps/jerboa-mail
+
+jobs:
+  build-and-test:
+    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 git curl ca-certificates libssl-dev
+
+      - name: Install Jerboa toolchain
+        run: sh support/ensure-jerboa.sh "$JERBOA_VERSION" .jerboa/bin
+
+      - name: Fetch dependencies
+        run: |
+          set -eu
+          mkdir -p .deps
+          git clone --depth 1 "$JERBOA_SSL_REPO" "$JERBOA_SSL_DIR"
+          git clone --depth 1 "$JERBOA_MAIL_REPO" "$JERBOA_MAIL_DIR"
+
+      - name: Release evidence
+        run: make release-evidence
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 40643ee..3706de5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,11 @@
 *.log
 *.eml
 *.mbox
+/.deps/
+/.jerboa/*
+!/.jerboa/
+!/.jerboa/security.json
+/dist/
 /protonmail-read
 /protonmail-read-bin
 .protonmail-reader.local
diff --git a/.jerboa/security.json b/.jerboa/security.json
new file mode 100644
index 0000000..26035c7
--- /dev/null
+++ b/.jerboa/security.json
@@ -0,0 +1,48 @@
+{
+  "version": 1,
+  "repo": "jerboa-protonmail",
+  "extends": ["jerboa:cli", "jerboa:network-client", "jerboa:credentialed-client", "jerboa:mail-client"],
+  "paths": {
+    "production": ["*.ss", "*.sls", "bin/**", "protonmail/**/*.ss", "protonmail/**/*.sls", "Makefile"],
+    "tests": ["test/**", "tests/**", "**/*-test.ss"],
+    "generated": ["build/**", "dist/**", "target/**", "*.so", "*.dylib", "*.wpo", "*.boot"],
+    "vendor": ["vendor/**", ".deps/**"],
+    "docs": ["README.md", "docs/**", "*.md"]
+  },
+  "policy": {
+    "failOn": ["critical", "high"],
+    "imports": { "directChezscheme": "deny" },
+    "ffi": { "allowed": false },
+    "process": { "shellInterpolation": "deny" },
+    "network": { "requireTimeouts": true, "rawMailOutputEvidence": "deny" },
+    "eval": { "stringEval": "deny", "bareRead": "deny", "allowReadEval": false },
+    "credentials": {
+      "plaintextCache": "deny",
+      "rawCredentialEvidence": "deny",
+      "protonAccountPasswordUse": "deny",
+      "bridgeCredentialStorage": "environment-only"
+    },
+    "mail": {
+      "defaultMutations": "deny",
+      "requireBodyPeek": true,
+      "plaintextExportEvidence": "target-required"
+    },
+    "targetEvidence": {
+      "bridgeProof": "fail-closed",
+      "requiredMarkers": [
+        "bridge_doctor_status=target-evidence-recorded",
+        "bridge_loopback_status=confirmed",
+        "bridge_generated_credential_status=confirmed",
+        "bridge_credential_redaction_status=target-evidence-recorded",
+        "read_only_imap_status=target-evidence-recorded",
+        "body_peek_policy_status=confirmed",
+        "plaintext_export_review_status=target-evidence-recorded",
+        "target_platform_smoke_status=target-evidence-recorded",
+        "production_mail_review_status=current-run-recorded",
+        "raw_mail_output_status=not-preserved",
+        "sensitive_artifact_policy=no-proton-account-passwords-bridge-passwords-eml-mbox-mail-bodies-attachments-or-imap-transcripts-preserved"
+      ]
+    }
+  },
+  "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 c20844e..cbe2ac5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,34 @@
-JERBOA_HOME ?= $(realpath $(CURDIR)/../jerboa)
-SCHEME      ?= $(JERBOA_HOME)/.chez/bin/scheme
-JERBOA_SSL_DIR ?= $(realpath $(CURDIR)/../jerboa-ssl)
-JERBOA_MAIL_DIR ?= $(realpath $(CURDIR)/../jerboa-mail)
-LIBDIRS := $(CURDIR):$(JERBOA_MAIL_DIR):$(JERBOA_SSL_DIR)/lib:$(JERBOA_HOME)/lib
+JERBOA_VERSION ?= v0.2.3
+JERBOA_TOOL_DIR ?= $(CURDIR)/.jerboa/bin
+JERBUILD ?= $(shell if [ -x "$(CURDIR)/jerbuild" ]; then echo "$(CURDIR)/jerbuild"; \
+	elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ]; then echo "$(JERBOA_TOOL_DIR)/jerbuild"; \
+	elif [ -x "$(CURDIR)/../jerboa/dist/jerbuild" ]; then echo "$(CURDIR)/../jerboa/dist/jerbuild"; \
+	elif [ -x "$(CURDIR)/../jerboa/jerbuild" ]; then echo "$(CURDIR)/../jerboa/jerbuild"; \
+	elif command -v jerbuild >/dev/null 2>&1; then command -v jerbuild; \
+	else echo "$(JERBOA_TOOL_DIR)/jerbuild"; fi)
+JH = $(shell "$(JERBUILD)" --jerboa-home 2>/dev/null)
 
-.PHONY: help run test integration-test doctor clean
+DEPS_DIR ?= $(CURDIR)/.deps
+DIST_DIR ?= $(CURDIR)/dist
+RELEASE_EVIDENCE_DIR ?= $(DIST_DIR)/release-evidence
+TARGET_EVIDENCE_DIR ?= $(DIST_DIR)/target-evidence
+LOCAL_SSL_DIR := $(CURDIR)/../jerboa-ssl
+LOCAL_MAIL_DIR := $(CURDIR)/../jerboa-mail
+JERBOA_SSL_DIR ?= $(if $(wildcard $(LOCAL_SSL_DIR)/.),$(realpath $(LOCAL_SSL_DIR)),$(DEPS_DIR)/jerboa-ssl)
+JERBOA_MAIL_DIR ?= $(if $(wildcard $(LOCAL_MAIL_DIR)/.),$(realpath $(LOCAL_MAIL_DIR)),$(DEPS_DIR)/jerboa-mail)
+LIBDIRS = $(CURDIR):$(JERBOA_MAIL_DIR):$(JERBOA_SSL_DIR)/lib:$(JH)/lib
+
+UNAME_S := $(shell uname -s)
+ifeq ($(UNAME_S),Darwin)
+  LD_VAR = DYLD_LIBRARY_PATH
+  SO_EXT = .dylib
+else
+  LD_VAR = LD_LIBRARY_PATH
+  SO_EXT = .so
+endif
+LD_ENV = $(LD_VAR)="$(JERBOA_SSL_DIR)"
+
+.PHONY: help ensure-jerboa-tools ensure-deps deps build run test integration-test doctor verify security dependency-evidence sbom reproducibility-report target-evidence target-evidence-selftest release-evidence clean
 .DEFAULT_GOAL := help
 
 help:
@@ -12,30 +36,57 @@ help:
 	@echo ""
 	@echo "Development:"
 	@echo "  bin/protonmail-read --help  Run the checked-in launcher"
-	@echo "  make run ARGS='--help'   Run the CLI under the interpreter"
+	@echo "  make run ARGS='--help'   Run the CLI through jerbuild"
 	@echo "  make doctor              Run the placeholder doctor command"
 	@echo "  make test                Run smoke tests"
 	@echo "  make integration-test    Run Bridge doctor when PROTON_BRIDGE_INTEGRATION=1"
+	@echo "  make verify              Run release verification checks"
+	@echo "  make target-evidence     Record live Proton Bridge target proof status"
+	@echo "  make release-evidence    Write test/dependency/SBOM evidence under dist/"
 	@echo "  make clean               Remove generated local artifacts"
 	@echo ""
 	@echo "Environment:"
-	@echo "  JERBOA_HOME = $(JERBOA_HOME)"
-	@echo "  SCHEME      = $(SCHEME)"
+	@echo "  JERBUILD = $(JERBUILD)"
 	@echo "  JERBOA_SSL_DIR = $(JERBOA_SSL_DIR)"
 	@echo "  JERBOA_MAIL_DIR = $(JERBOA_MAIL_DIR)"
 
-run:
-	JERBOA_HOME=$(JERBOA_HOME) JERBOA_SSL_LIB=$(JERBOA_SSL_DIR) \
-		$(SCHEME) -q --libdirs $(LIBDIRS) \
-			--script main.ss -- $(ARGS)
+ensure-jerboa-tools:
+	@if "$(JERBUILD)" --jerboa-home >/dev/null 2>&1; then \
+	  echo "=== Using Jerboa toolchain: $(JERBUILD) ==="; \
+	elif [ -x "$(JERBOA_TOOL_DIR)/jerbuild" ] && "$(JERBOA_TOOL_DIR)/jerbuild" --jerboa-home >/dev/null 2>&1; then \
+	  echo "=== Using downloaded Jerboa toolchain: $(JERBOA_TOOL_DIR) ==="; \
+	elif command -v jerbuild >/dev/null 2>&1 && jerbuild --jerboa-home >/dev/null 2>&1; then \
+	  echo "=== Using Jerboa toolchain from PATH: $$(command -v jerbuild) ==="; \
+	else \
+	  echo "=== Fetching Jerboa $(JERBOA_VERSION) release tools into $(JERBOA_TOOL_DIR) ==="; \
+	  sh support/ensure-jerboa.sh "$(JERBOA_VERSION)" "$(JERBOA_TOOL_DIR)"; \
+	fi
+	@"$(JERBUILD)" --jerboa-home >/dev/null || { \
+	  echo "ERROR: Jerboa toolchain is unavailable; set JERBUILD=/path/to/jerbuild or JERBOA_VERSION=<tag>" >&2; \
+	  exit 1; \
+	}
+
+ensure-deps:
+	@test -d "$(JERBOA_SSL_DIR)" || { echo "ERROR: JERBOA_SSL_DIR missing: $(JERBOA_SSL_DIR)" >&2; exit 1; }
+	@test -d "$(JERBOA_MAIL_DIR)" || { echo "ERROR: JERBOA_MAIL_DIR missing: $(JERBOA_MAIL_DIR)" >&2; exit 1; }
+
+deps: ensure-jerboa-tools ensure-deps
+	@if [ ! -f "$(JERBOA_SSL_DIR)/lib/jerboa-ssl.sls" ] || [ ! -f "$(JERBOA_SSL_DIR)/jerboa_ssl_shim$(SO_EXT)" ]; then \
+	  $(MAKE) -C "$(JERBOA_SSL_DIR)" JERBUILD="$(JERBUILD)" build; \
+	fi
+
+build: deps
+
+run: build
+	JERBOA_SSL_LIB="$(JERBOA_SSL_DIR)" $(LD_ENV) \
+		"$(JERBUILD)" exec --libdirs "$(LIBDIRS)" main.ss -- $(ARGS)
 
 doctor:
 	$(MAKE) run ARGS='doctor'
 
-test:
-	JERBOA_HOME=$(JERBOA_HOME) JERBOA_SSL_LIB=$(JERBOA_SSL_DIR) \
-		$(SCHEME) -q --libdirs $(LIBDIRS) \
-			--script test/test-all.ss
+test: build
+	JERBOA_SSL_LIB="$(JERBOA_SSL_DIR)" $(LD_ENV) \
+		"$(JERBUILD)" exec --libdirs "$(LIBDIRS)" test/test-all.ss
 
 integration-test:
 	@if [ "$$PROTON_BRIDGE_INTEGRATION" != "1" ]; then \
@@ -44,6 +95,78 @@ integration-test:
 		bin/protonmail-read doctor; \
 	fi
 
+verify: test
+
+security:
+	@set -eu; \
+	pattern="(BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|gldt-[A-Za-z0-9_-]{20,}|glrt-[A-Za-z0-9_-]{20,}|PROTON_BRIDGE_PASSWORD=['\\\"]?[A-Za-z0-9_./+=:-]{32,}|AKIA[0-9A-Z]{16}|ASIA[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; \
+	echo "secret_scan_status=pass"
+
+dependency-evidence: deps
+	$(MAKE) -C "$(JERBOA_SSL_DIR)" JERBUILD="$(JERBUILD)" release-evidence
+	$(MAKE) -C "$(JERBOA_MAIL_DIR)" JERBUILD="$(JERBUILD)" release-evidence
+
+sbom: ensure-jerboa-tools ensure-deps
+	@REPO_ROOT="$(CURDIR)" \
+	DIST_DIR="$(DIST_DIR)" \
+	JERBUILD="$(JERBUILD)" \
+	JERBOA_SSL_DIR="$(JERBOA_SSL_DIR)" \
+	JERBOA_MAIL_DIR="$(JERBOA_MAIL_DIR)" \
+	sh scripts/sbom.sh
+
+reproducibility-report:
+	@REPO_ROOT="$(CURDIR)" \
+	DIST_DIR="$(DIST_DIR)" \
+	sh scripts/reproducibility-report.sh
+
+target-evidence:
+	@REPO_ROOT="$(CURDIR)" \
+	DIST_DIR="$(DIST_DIR)" \
+	TARGET_EVIDENCE_DIR="$(TARGET_EVIDENCE_DIR)" \
+	sh scripts/target-evidence.sh
+
+target-evidence-selftest:
+	@REPO_ROOT="$(CURDIR)" \
+	DIST_DIR="$(DIST_DIR)" \
+	sh scripts/test-target-evidence.sh
+
+release-evidence:
+	@rm -rf "$(RELEASE_EVIDENCE_DIR)" "$(TARGET_EVIDENCE_DIR)" "$(DIST_DIR)/reproducibility" "$(DIST_DIR)/sbom"
+	@mkdir -p "$(RELEASE_EVIDENCE_DIR)"
+	@echo "==> Collecting git status"
+	@git status --short > "$(RELEASE_EVIDENCE_DIR)/git-status.txt"
+	@git rev-parse HEAD > "$(RELEASE_EVIDENCE_DIR)/git-head.txt" 2>/dev/null || true
+	@echo "==> Running high-confidence secret scan"
+	@$(MAKE) security > "$(RELEASE_EVIDENCE_DIR)/security.log" 2>&1
+	@echo "==> Running tests"
+	@$(MAKE) test > "$(RELEASE_EVIDENCE_DIR)/test.log" 2>&1
+	@echo "==> Running CLI help smoke"
+	@$(MAKE) run ARGS='--help' > "$(RELEASE_EVIDENCE_DIR)/help.log" 2>&1
+	@echo "==> Running dependency release evidence"
+	@$(MAKE) dependency-evidence > "$(RELEASE_EVIDENCE_DIR)/dependency-evidence.log" 2>&1
+	@echo "==> Running reproducibility report"
+	@$(MAKE) reproducibility-report > "$(RELEASE_EVIDENCE_DIR)/reproducibility.log" 2>&1
+	@rm -rf "$(RELEASE_EVIDENCE_DIR)/reproducibility"
+	@cp -R "$(DIST_DIR)/reproducibility" "$(RELEASE_EVIDENCE_DIR)/reproducibility"
+	@echo "==> Running target evidence selftest"
+	@$(MAKE) target-evidence-selftest > "$(RELEASE_EVIDENCE_DIR)/target-evidence-selftest.log" 2>&1
+	@echo "==> Recording target Proton Bridge evidence"
+	@$(MAKE) target-evidence > "$(RELEASE_EVIDENCE_DIR)/target-evidence.log" 2>&1
+	@rm -rf "$(RELEASE_EVIDENCE_DIR)/target-evidence"
+	@cp -R "$(TARGET_EVIDENCE_DIR)" "$(RELEASE_EVIDENCE_DIR)/target-evidence"
+	@echo "==> Generating SBOM"
+	@$(MAKE) sbom > "$(RELEASE_EVIDENCE_DIR)/sbom.log" 2>&1
+	@rm -rf "$(RELEASE_EVIDENCE_DIR)/sbom"
+	@cp -R "$(DIST_DIR)/sbom" "$(RELEASE_EVIDENCE_DIR)/sbom"
+	@find bin protonmail test docs support scripts .github .jerboa/security.json -type f -print | LC_ALL=C sort | xargs shasum -a 256 > "$(RELEASE_EVIDENCE_DIR)/source-hashes.sha256"
+	@echo "Release evidence written to $(RELEASE_EVIDENCE_DIR)"
+
 clean:
 	rm -f protonmail-read protonmail-read-bin
-	rm -rf exports cache tmp
+	rm -rf exports cache tmp dist
diff --git a/README.md b/README.md
index d1df277..103ea6c 100644
--- a/README.md
+++ b/README.md
@@ -37,14 +37,18 @@ Live Proton Bridge checks are attempted only when Bridge credentials are set.
 make run ARGS='--help'
 make doctor
 make test
+make release-evidence
 make integration-test
 bin/protonmail-read doctor
 ```
 
-By default the Makefile expects Jerboa at `../jerboa`. Override with:
+By default the Makefile uses `../jerboa/dist/jerbuild`, a local
+`.jerboa/bin/jerbuild`, or a `jerbuild` on `PATH`. If no toolchain exists,
+`make test` fetches the pinned Jerboa release into `.jerboa/bin`.
+Override with:
 
 ```sh
-make JERBOA_HOME=/path/to/jerboa test
+make JERBUILD=/path/to/jerbuild test
 ```
 
 The Bridge transport uses `../jerboa-ssl` by default. Override with:
@@ -95,3 +99,12 @@ messages as read. The tool does not issue `STORE`, `EXPUNGE`, `COPY`, `MOVE`,
 
 Exported `.eml` files contain plaintext mail after Bridge has decrypted it
 locally. Treat them as sensitive.
+
+Credential and plaintext-mail handling are documented in
+[`docs/credential-handling.md`](docs/credential-handling.md). Dependency
+provenance is documented in
+[`docs/dependency-provenance.md`](docs/dependency-provenance.md), and local
+release evidence is documented in
+[`docs/release-evidence.md`](docs/release-evidence.md). Production release
+requires `make release-evidence` plus live Bridge credential and plaintext
+export review.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..9179d20
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,51 @@
+# Security Policy
+
+`jerboa-protonmail` is a read-only Proton Mail Bridge IMAP client. Proton
+Bridge handles Proton account authentication, mailbox decryption, and local IMAP
+service exposure; this repository must treat Bridge credentials and decrypted
+messages as sensitive local secrets.
+
+## Supported Status
+
+No public production-support commitment exists yet. Treat current builds as
+experimental until the release checklist in `~/Release-plan.md` and
+`~/mine/jerboa-production-readiness.md` is complete for this repository.
+
+## Security Expectations
+
+- Use Proton Bridge generated IMAP credentials, not Proton account passwords.
+- Keep Bridge bound to loopback. Do not expose the Bridge IMAP listener on a
+  shared or remote interface.
+- Do not commit Bridge usernames, Bridge passwords, `.env` files, exported
+  `.eml`/`.mbox` messages, mailbox metadata, private hostnames, or user data.
+- Treat raw messages, decoded message bodies, headers, attachment filenames,
+  and exported `.eml` files as plaintext sensitive data after Bridge decrypts
+  them locally.
+- The client must remain read-only by default. Release builds must not add IMAP
+  mutation commands without a threat model and tests.
+- Diagnostics must redact credentials and avoid printing message bodies unless
+  the user explicitly requested `show`, `raw`, or `export-eml`.
+- Production release requires marker-complete live Proton Bridge target
+  evidence through `JPROTONMAIL_TARGET_PROOF_FILE`; setting
+  `JPROTONMAIL_REQUIRE_TARGET_PROOF=1` makes missing or incomplete proof fail
+  closed.
+- Target proof files must contain only status markers and sanitized reviewer
+  notes. Do not preserve Proton account passwords, Bridge passwords, raw IMAP
+  `LOGIN` commands, `.eml`/`.mbox` exports, message bodies, attachment content,
+  mailbox transcripts, private hostnames, or user data.
+- Release builds must pass `make release-evidence`, which runs tests, CLI help
+  smoke, high-confidence secret scan, sibling `jerboa-ssl` and `jerboa-mail`
+  release-evidence gates, SBOM/dependency capture, and launcher/source
+  reproducibility checks.
+
+Credential and plaintext-mail details are documented in
+[`docs/credential-handling.md`](docs/credential-handling.md).
+Dependency provenance and local release evidence are documented in
+[`docs/dependency-provenance.md`](docs/dependency-provenance.md) and
+[`docs/release-evidence.md`](docs/release-evidence.md).
+
+## Reporting
+
+Before public release, report issues privately to the repository owner. After
+public release, replace this section with a dedicated advisory contact,
+supported versions, and disclosure window.
diff --git a/bin/protonmail-read b/bin/protonmail-read
index f9fc4eb..7108337 100755
--- a/bin/protonmail-read
+++ b/bin/protonmail-read
@@ -2,12 +2,27 @@
 set -eu
 
 repo_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
-jerboa_home=${JERBOA_HOME:-"$repo_dir/../jerboa"}
 jerboa_ssl_dir=${JERBOA_SSL_DIR:-"$repo_dir/../jerboa-ssl"}
 jerboa_mail_dir=${JERBOA_MAIL_DIR:-"$repo_dir/../jerboa-mail"}
-scheme=${SCHEME:-"$jerboa_home/.chez/bin/scheme"}
+
+if [ "${JERBUILD:-}" ]; then
+    jerbuild=$JERBUILD
+elif [ -x "$repo_dir/.jerboa/bin/jerbuild" ]; then
+    jerbuild="$repo_dir/.jerboa/bin/jerbuild"
+elif [ -x "$repo_dir/../jerboa/dist/jerbuild" ]; then
+    jerbuild="$repo_dir/../jerboa/dist/jerbuild"
+elif [ -x "$repo_dir/../jerboa/jerbuild" ]; then
+    jerbuild="$repo_dir/../jerboa/jerbuild"
+elif command -v jerbuild >/dev/null 2>&1; then
+    jerbuild=$(command -v jerbuild)
+else
+    echo "ERROR: jerbuild not found; run make test or set JERBUILD=/path/to/jerbuild" >&2
+    exit 1
+fi
+
+jerboa_home=$("$jerbuild" --jerboa-home)
 
 export JERBOA_SSL_LIB=${JERBOA_SSL_LIB:-"$jerboa_ssl_dir"}
 
-exec "$scheme" -q --libdirs "$repo_dir:$jerboa_mail_dir:$jerboa_ssl_dir/lib:$jerboa_home/lib" \
-  --script "$repo_dir/main.ss" -- "$@"
+exec "$jerbuild" exec --libdirs "$repo_dir:$jerboa_mail_dir:$jerboa_ssl_dir/lib:$jerboa_home/lib" \
+  "$repo_dir/main.ss" -- "$@"
diff --git a/docs/credential-handling.md b/docs/credential-handling.md
new file mode 100644
index 0000000..69ae2db
--- /dev/null
+++ b/docs/credential-handling.md
@@ -0,0 +1,70 @@
+# Credential Handling
+
+`jerboa-protonmail` talks to a local Proton Mail Bridge IMAP listener. Bridge
+terminates Proton authentication and decrypts mailbox content locally, so this
+tool only needs Bridge IMAP settings.
+
+## Credential Sources
+
+The CLI reads Bridge configuration from environment variables:
+
+```sh
+PROTON_BRIDGE_HOST=127.0.0.1
+PROTON_BRIDGE_PORT=1143
+PROTON_BRIDGE_USER=bridge-generated-user
+PROTON_BRIDGE_PASSWORD=bridge-generated-password
+PROTON_BRIDGE_TLS=auto
+```
+
+Use Bridge generated IMAP credentials only. Do not use a Proton account
+password. Prefer a shell session, process supervisor secret injection, or a
+local file loaded by the shell with restrictive permissions:
+
+```sh
+chmod 0600 .protonmail-reader.local
+```
+
+If a future config-file loader is added, it must reject group-readable or
+world-readable files when they contain a password.
+
+## Logging And Output
+
+The `doctor` command redacts the Bridge username and password. Error messages
+must not include raw IMAP `LOGIN` commands, Bridge passwords, or complete
+mailbox responses that may contain message content.
+
+The `show`, `raw`, and `export-eml` commands intentionally disclose decrypted
+mail selected by the user. Treat their output as sensitive:
+
+- stdout may be captured by shell history wrappers, terminal logs, or calling
+  programs.
+- `.eml` exports are plaintext decrypted messages.
+- headers can reveal correspondents, subjects, dates, and mailbox structure.
+- raw IMAP literals can contain attachments and embedded secrets.
+
+## Read-Only Mailbox Policy
+
+Production releases must stay read-only unless a separate threat model and test
+suite is added. Allowed fetches use `BODY.PEEK[...]` and read/search/list
+commands. The client must not issue `STORE`, `EXPUNGE`, `COPY`, `MOVE`,
+`APPEND`, `CREATE`, `DELETE`, or other mailbox-mutating commands by default.
+
+## Release Requirements
+
+Before a production release:
+
+- Run `make release-evidence` from a clean checkout.
+- Run release evidence with `JPROTONMAIL_REQUIRE_TARGET_PROOF=1` and a
+  marker-complete `JPROTONMAIL_TARGET_PROOF_FILE` from a least-privilege live
+  Proton Bridge setup.
+- The target proof must confirm Bridge doctor smoke, loopback-only exposure,
+  generated Bridge credentials, credential redaction, read-only IMAP behavior,
+  `BODY.PEEK` policy, plaintext export review, target-platform smoke,
+  production mail review, no raw mail output retention, and the
+  sensitive-artifact policy.
+- Run the baseline tracked-file secret scan and a history scan for Bridge
+  credentials or exported mail.
+- Confirm release artifacts do not include `.eml`, `.mbox`, cache, export,
+  transcript, or local config files.
+- Review `jerboa-ssl` TLS shim and OpenSSL dependency versions.
+- Review `jerboa-mail` parsing behavior for untrusted MIME/header inputs.
diff --git a/docs/dependency-provenance.md b/docs/dependency-provenance.md
new file mode 100644
index 0000000..76f122c
--- /dev/null
+++ b/docs/dependency-provenance.md
@@ -0,0 +1,19 @@
+# Dependency Provenance
+
+`jerboa-protonmail` is a read-only Proton Mail Bridge IMAP client. It does not
+implement Proton account authentication or mailbox decryption directly.
+
+Dependency boundaries:
+
+- Proton Mail Bridge performs Proton authentication and local message
+  decryption.
+- `jerboa-ssl` provides the local TLS transport when TLS is enabled.
+- `jerboa-mail` parses and decodes headers, MIME structure, and friendly message
+  output.
+- This repository reads Bridge settings from environment variables and does not
+  persist credentials.
+
+`make release-evidence` runs dependency release-evidence gates for the selected
+`jerboa-ssl` and `jerboa-mail` checkouts, then records their git state, hashes,
+OpenSSL advisory evidence, TLS reproducibility result, and mail parser
+dependency audit evidence in the local SBOM bundle.
diff --git a/docs/release-evidence.md b/docs/release-evidence.md
new file mode 100644
index 0000000..bd31eb2
--- /dev/null
+++ b/docs/release-evidence.md
@@ -0,0 +1,61 @@
+# Release Evidence
+
+`make release-evidence` writes ignored local evidence under
+`dist/release-evidence/`.
+
+The evidence bundle contains:
+
+- high-confidence tracked-file secret-scan output;
+- smoke-test output;
+- CLI help smoke output;
+- sibling `jerboa-ssl` and `jerboa-mail` release-evidence logs;
+- SBOM-style source, workflow, launcher, dependency, and toolchain manifests;
+- copied `jerboa-ssl` OpenSSL advisory and reproducibility evidence when
+  available;
+- copied `jerboa-mail` dependency audit evidence when available;
+- target Proton Bridge proof status and proof-gate self-test output;
+- a reproducibility note that this repo ships a launcher/script path rather than
+  a standalone native binary.
+
+This local evidence does not replace live Proton Bridge release blockers:
+marker-complete least-privilege Bridge credential proof, loopback-only Bridge
+exposure review, plaintext export review, and external review of the read-only
+IMAP command set are still required before production support claims.
+
+## Target Proton Bridge Proof
+
+Local `make release-evidence` always writes
+`dist/release-evidence/target-evidence/status.txt`. Without a proof file, the
+target-only Bridge checks are recorded as `blocked-not-run` so local smoke
+evidence is not mistaken for live mailbox evidence.
+
+To attach production target evidence, create a sanitized marker-only proof file
+and run:
+
+```sh
+JPROTONMAIL_TARGET_PROOF_FILE=/path/to/protonmail-target-proof.txt \
+JPROTONMAIL_REQUIRE_TARGET_PROOF=1 \
+make release-evidence
+```
+
+The proof file must contain every line below exactly:
+
+```text
+bridge_doctor_status=target-evidence-recorded
+bridge_loopback_status=confirmed
+bridge_generated_credential_status=confirmed
+bridge_credential_redaction_status=target-evidence-recorded
+read_only_imap_status=target-evidence-recorded
+body_peek_policy_status=confirmed
+plaintext_export_review_status=target-evidence-recorded
+target_platform_smoke_status=target-evidence-recorded
+production_mail_review_status=current-run-recorded
+raw_mail_output_status=not-preserved
+sensitive_artifact_policy=no-proton-account-passwords-bridge-passwords-eml-mbox-mail-bodies-attachments-or-imap-transcripts-preserved
+```
+
+The proof gate rejects missing, empty, marker-incomplete, or high-confidence
+sensitive proof files. The proof must not preserve Proton account passwords,
+Bridge passwords, raw IMAP `LOGIN` commands, `.eml`/`.mbox` files, decrypted
+message bodies, attachments, mailbox transcripts, private hostnames, or raw
+Bridge output.
diff --git a/main.ss b/main.ss
index 70f0e11..e1191f8 100644
--- a/main.ss
+++ b/main.ss
@@ -1,31 +1,6 @@
 #!chezscheme
 ;;; Script entry point for protonmail-read.
 
-(import (except (chezscheme)
-                make-hash-table hash-table?
-                sort sort!
-                printf fprintf
-                path-extension path-absolute?
-                with-input-from-string with-output-to-string
-                iota 1+ 1-
-                partition
-                make-date make-time))
-
-(define home (or (getenv "HOME") "."))
-(define jerboa-dir
-  (or (getenv "JERBOA_HOME")
-      (string-append home "/mine/jerboa")))
-(define project-dir
-  (or (getenv "JERBOA_PROTONMAIL_DIR")
-      (current-directory)))
-
-(library-directories
-  (append
-    (list (cons project-dir project-dir)
-          (cons (string-append jerboa-dir "/lib")
-                (string-append jerboa-dir "/lib")))
-    (library-directories)))
-
 (import (protonmail cli))
 
 (run-cli (command-line-arguments))
diff --git a/protonmail/cli.ss b/protonmail/cli.ss
index f3e10ba..f5528e8 100644
--- a/protonmail/cli.ss
+++ b/protonmail/cli.ss
@@ -4,15 +4,7 @@
 (library (protonmail cli)
   (export run-cli usage-string)
 
-  (import (except (chezscheme)
-                  make-hash-table hash-table?
-                  sort sort!
-                  printf fprintf
-                  path-extension path-absolute?
-                  with-input-from-string with-output-to-string
-                  iota 1+ 1-
-                  partition
-                  make-date make-time)
+  (import (rnrs)
           (protonmail config)
           (protonmail imap client)
           (jerboa-mail encoding)
@@ -177,7 +169,7 @@
         (print-summary-table summaries))))
 
   (define (write-stdout-bytes bv)
-    (let ([p (standard-output-port (buffer-mode block))])
+    (let ([p (standard-output-port)])
       (put-bytevector p bv)
       (flush-output-port p)))
 
diff --git a/protonmail/config.ss b/protonmail/config.ss
index cdbc4ca..b9db043 100644
--- a/protonmail/config.ss
+++ b/protonmail/config.ss
@@ -14,15 +14,8 @@
     config-errors
     redact-secret)
 
-  (import (except (chezscheme)
-                  make-hash-table hash-table?
-                  sort sort!
-                  printf fprintf
-                  path-extension path-absolute?
-                  with-input-from-string with-output-to-string
-                  iota 1+ 1-
-                  partition
-                  make-date make-time))
+  (import (rnrs)
+          (only (jerboa core) getenv))
 
   (define (make-config host port user password tls)
     (vector host port user password tls))
diff --git a/protonmail/imap/client.ss b/protonmail/imap/client.ss
index 185afd3..8fa4e7d 100644
--- a/protonmail/imap/client.ss
+++ b/protonmail/imap/client.ss
@@ -11,15 +11,7 @@
     imap-quote
     imap-final-ok?)
 
-  (import (except (chezscheme)
-                  make-hash-table hash-table?
-                  sort sort!
-                  printf fprintf
-                  path-extension path-absolute?
-                  with-input-from-string with-output-to-string
-                  iota 1+ 1-
-                  partition
-                  make-date make-time)
+  (import (rnrs)
           (protonmail config)
           (protonmail imap parser)
           (protonmail imap transport)
@@ -49,17 +41,17 @@
       (string-append "A" (number->string n))))
 
   (define (imap-quote s)
-    (let ([out (open-output-string)])
-      (write-char #\" out)
-      (let loop ([i 0])
-        (when (< i (string-length s))
-          (let ([ch (string-ref s i)])
-            (when (or (char=? ch #\\) (char=? ch #\"))
-              (write-char #\\ out))
-            (write-char ch out)
-            (loop (+ i 1)))))
-      (write-char #\" out)
-      (get-output-string out)))
+    (call-with-string-output-port
+      (lambda (out)
+        (write-char #\" out)
+        (let loop ([i 0])
+          (when (< i (string-length s))
+            (let ([ch (string-ref s i)])
+              (when (or (char=? ch #\\) (char=? ch #\"))
+                (write-char #\\ out))
+              (write-char ch out)
+              (loop (+ i 1)))))
+        (write-char #\" out))))
 
   (define (imap-command c command)
     (map imap-entry-line (imap-command-entries c command)))
@@ -162,24 +154,25 @@
 
   (define (imap-with-session cfg proc)
     (let ([client #f])
-      (guard (e [#t
-                 (when client
-                   (safe-close-client client))
-                 (raise e)])
-        (set! client (open-client cfg))
-        (require-ok 'imap-capability
-                    (imap-command client "CAPABILITY"))
-        (require-ok 'imap-login
-                    (imap-command client
-                                  (string-append
-                                    "LOGIN "
-                                    (imap-quote (config-user cfg))
-                                    " "
-                                    (imap-quote (config-password cfg)))))
-        (let ([result (proc client)])
-          (safe-logout client)
-          (safe-close-client client)
-          result))))
+      (dynamic-wind
+        (lambda () #t)
+        (lambda ()
+          (set! client (open-client cfg))
+          (require-ok 'imap-capability
+                      (imap-command client "CAPABILITY"))
+          (require-ok 'imap-login
+                      (imap-command client
+                                    (string-append
+                                      "LOGIN "
+                                      (imap-quote (config-user cfg))
+                                      " "
+                                      (imap-quote (config-password cfg)))))
+          (let ([result (proc client)])
+            (safe-logout client)
+            result))
+        (lambda ()
+          (when client
+            (safe-close-client client))))))
 
   (define (nth xs n default)
     (cond
diff --git a/protonmail/imap/parser.ss b/protonmail/imap/parser.ss
index 1b47f2a..326e36f 100644
--- a/protonmail/imap/parser.ss
+++ b/protonmail/imap/parser.ss
@@ -16,15 +16,7 @@
     imap-literal-marker?
     imap-literal-marker-length)
 
-  (import (except (chezscheme)
-                  make-hash-table hash-table?
-                  sort sort!
-                  printf fprintf
-                  path-extension path-absolute?
-                  with-input-from-string with-output-to-string
-                  iota 1+ 1-
-                  partition