security: add seatbelt path exec worker profile

ober

90786ed0c95d94607b90b3aa566035429c7a9165

diff --git a/docs/kimi3-security-recommmendations.md b/docs/kimi3-security-recommmendations.md
index 2bb815d..bd7c77e 100644
--- a/docs/kimi3-security-recommmendations.md
+++ b/docs/kimi3-security-recommmendations.md
@@ -157,14 +157,14 @@ never pattern-based:
   kill, output caps, audit-log start/end records, and fail-closed refused axes.
   Memory limits are installed through native pre-exec `setrlimit` on supported
   platforms and refuse before launch where unavailable.
-  macOS Seatbelt no-write/no-network profiles are also installed through
-  native pre-exec `sandbox_init` for supported worker axes.
+  macOS Seatbelt deny-default cage profiles are also installed through
+  native pre-exec `sandbox_init` for supported worker `fs`/`exec`/`net`
+  axes, with explicit runtime read and executable path grants.
   The standard FreeBSD worker-eval helper can enter Capsicum at process start,
   before reading stdin or evaluating the supplied expression.
   `sandbox-launch` still returns status 126 `pre-exec-refused`,
   `supervise-available?` returns `#f`, and the remaining P0-02 work is native
-  Seatbelt path/exec parity plus arbitrary-command/proxy-aware Capsicum parity
-  for the worker path.
+  arbitrary-command/proxy-aware Capsicum parity for the worker path.
 
 ---
 
@@ -221,7 +221,7 @@ when" must be answerable from `dist/release-evidence/` in minutes.
 | Self-confinement (pledge/unveil-style `cage!`) | `(std security cage)` | exists; documented in the security module inventory | security-reference §12 |
 | Privilege-separation pipe channels (no launcher) | `(std security privsep)` | channels only, by design | security-reference §5 |
 | Launch policy planner + egress policy objects | `(std os limits sandbox)` | passive planner remains `pre-exec-refused`; `(std os limits)` can emit rlimit triples for the worker/aproc backend | [limits.md](limits.md) |
-| Exec restricted worker facade | `(std security worker)` | exists; pure env, deadline, process-group kill, output caps, audit records, fail-closed refused axes, explicit `sandbox:` axis requests, egress proxy env wiring, memory rlimits installed pre-exec on supported platforms, Linux syscall/ptrace seccomp plus Landlock filesystem/TCP-connect setup for requested axes, macOS Seatbelt no-write/no-network setup for supported axes, and standard FreeBSD worker-eval Capsicum entry before payload read/eval; Seatbelt path/exec parity plus arbitrary-command/proxy-aware Capsicum parity pending | security-reference §5 |
+| Exec restricted worker facade | `(std security worker)` | exists; pure env, deadline, process-group kill, output caps, audit records, fail-closed refused axes, explicit `sandbox:` axis requests, egress proxy env wiring, memory rlimits installed pre-exec on supported platforms, Linux syscall/ptrace seccomp plus Landlock filesystem/TCP-connect setup for requested axes, macOS Seatbelt deny-default path/exec/no-network setup for supported axes, and standard FreeBSD worker-eval Capsicum entry before payload read/eval; arbitrary-command/proxy-aware Capsicum parity pending | security-reference §5 |
 | Native async exec launcher (collect-safe) | `(std os aproc)` | exists — the primitive P0-02 should build on | [aproc.md](aproc.md) |
 | Parser hardening (depth/size/backtrack budgets: reader, JSON, XML, YAML, DNS, HTTP/2, WS, zlib, base64, hex, CSV, pregexp, format) | various | phases 1–4 done, 42 tests; phase 5 FFI audit inventory has per-site provisional verdicts, with remediation rules still open | security-reference §7; [ffi-audit.md](ffi-audit.md) |
 | Safe deserialization (tagged-JSON envelope, no native FASL on untrusted paths) | `(std safe-fasl)`, `(std fasl)` (trusted-only) | exists; raw `read`/FASL/`load` sites are classified by manifest and gated by `make audit` | [safety-guide.md](safety-guide.md) §10 |
@@ -288,8 +288,8 @@ Keep this section to baseline facts only.
    `tests/test-pipeline.ss`, `tests/test-security2-parsers.ss` in the
    checklist appended to `security-reference.md` — re-verify all four.
 2. **The exec worker facade exists and now has native confinement on the main
-   supported paths** (see §2.3). The remaining worker gaps are Seatbelt
-   path/exec parity plus arbitrary-command/proxy-aware Capsicum parity.
+   supported paths** (see §2.3). The remaining worker gap is
+   arbitrary-command/proxy-aware Capsicum parity.
 3. **Fuzz seed corpora and crash/rejection regressions exist; CI needed
    scheduled fuzzing** — the deterministic regression runner is wired into
    `make fuzz-smoke`, and CI now runs smoke fuzzing normally plus deep fuzzing
@@ -315,7 +315,7 @@ Keep this section to baseline facts only.
 | Goal | Where we stand | The gap |
 |---|---|---|
 | G1 shrink target | Managed core is memory-safe; parsers budgeted; safe prelude exists; FFI audit inventory, per-site provisional verdicts, FFI hazard scanner rules, a gated `vendor/jsqlite` accepted-risk decision, native unsafe invariant comments, and a gated native export review exist | FFI remediation remains: Scheme per-binding safety review |
-| G2 cap blast radius | Capabilities, taint, kernel sandbox, egress policy objects, worker facade, memory rlimit pre-exec path, egress proxy env wiring, Linux pre-exec seccomp/Landlock setup, macOS Seatbelt no-write/no-network setup, standard FreeBSD worker-eval Capsicum entry, authenticated actor transport/envelopes, Ring HTTP source taint, and WebSocket/fiber WebSocket source taint all exist | Native Seatbelt path/exec parity and arbitrary-command/proxy-aware Capsicum worker parity remain open |
+| G2 cap blast radius | Capabilities, taint, kernel sandbox, egress policy objects, worker facade, memory rlimit pre-exec path, egress proxy env wiring, Linux pre-exec seccomp/Landlock setup, macOS Seatbelt deny-default path/exec/no-network setup, standard FreeBSD worker-eval Capsicum entry, authenticated actor transport/envelopes, Ring HTTP source taint, and WebSocket/fiber WebSocket source taint all exist | Arbitrary-command/proxy-aware Capsicum worker parity remains open |
 | G3 find it first | 13 harnesses, checked-in seed/regression corpora, scanner w/ rule DB, lint, exploit-shaped K3 suite | Standing AI-red-team and ongoing corpus growth remain open |
 | G4 fail closed | Raw-fork launchers retired correctly; `allow-degraded?` explicit | New controls must keep the invariant; degraded-mode warnings must be test-locked |
 | G5 recover fast | SBOM/repro/signing gates exist | TCB accounting manual; doc drift (stale tables, undocumented modules); independent-builder reproducibility not yet routine |
@@ -362,9 +362,9 @@ requested worker axes landed 2026-07-28 through `(std os aproc)`
 `seccomp-lock?:`. Native pre-exec Landlock filesystem/TCP-connect setup for
 requested worker axes landed 2026-07-28 through `(std os aproc)`
 `landlock-rules:` and `landlock-net-connect?:`. Native pre-exec Seatbelt
-no-write/no-network setup for supported macOS worker axes landed 2026-07-28
-through `(std os aproc)` `seatbelt-profile:`. Seatbelt path/exec parity and
-arbitrary-command/proxy-aware Capsicum parity remain open. The standard
+deny-default path/exec/no-network setup for supported macOS worker axes landed
+2026-07-28 through `(std os aproc)` `seatbelt-profile:`.
+Arbitrary-command/proxy-aware Capsicum parity remains open. The standard
 FreeBSD worker-eval helper now enters Capsicum before reading stdin or
 evaluating the supplied expression when `sandbox:` requests a supported
 Capsicum axis.
@@ -884,12 +884,12 @@ Chez cannot heap-cap a thread; `run-safe-eval` rightly refuses
   `syscalls`/`ptrace` sandbox axes through native aproc pre-exec seccomp on
   Linux, routes requested `fs`/`exec`/`net` axes through native aproc pre-exec
   Landlock filesystem and TCP-connect rules on Linux, and routes supported
-  `fs`/`net` axes through native aproc pre-exec Seatbelt no-write/no-network
-  profiles on macOS. On FreeBSD, the standard worker-eval helper enters
+  `fs`/`exec`/`net` axes through native aproc pre-exec Seatbelt deny-default
+  path/exec/no-network profiles on macOS. On FreeBSD, the standard worker-eval
+  helper enters
   Capsicum before reading stdin or evaluating the supplied expression for
-  supported `fs`/`exec`/`net`/`capsicum` axes. Seatbelt path/exec parity,
-  arbitrary-command/proxy-aware Capsicum parity, and per-platform CI parity
-  remain open.
+  supported `fs`/`exec`/`net`/`capsicum` axes. Arbitrary-command/proxy-aware
+  Capsicum parity and per-platform CI parity remain open.
 
 ### K3-P1-09 — Confused-deputy defenses: capability plans enforced at runtime
 **Serves:** G2. **Effort:** 1 week.
@@ -1220,7 +1220,7 @@ Track these in `docs/status.md` per release:
 | Metric | Baseline (2026-07-27) | Target |
 |---|---|---|
 | Build balance clean | `pattern.ss` repaired 2026-07-27; `source-balance` in `make audit` | always clean |
-| Confined worker exists | facade, audit lifecycle, output caps, deadline, process-group kill, memory rlimit pre-exec path, explicit sandbox-axis refusal, egress proxy env wiring, Linux syscall/ptrace seccomp pre-exec setup, Linux Landlock filesystem/TCP-connect setup, macOS Seatbelt no-write/no-network setup, and standard FreeBSD worker-eval Capsicum entry landed; Seatbelt path/exec parity and arbitrary-command/proxy-aware Capsicum parity pending | yes, tested (P0-02) |
+| Confined worker exists | facade, audit lifecycle, output caps, deadline, process-group kill, memory rlimit pre-exec path, explicit sandbox-axis refusal, egress proxy env wiring, Linux syscall/ptrace seccomp pre-exec setup, Linux Landlock filesystem/TCP-connect setup, macOS Seatbelt deny-default path/exec/no-network setup, and standard FreeBSD worker-eval Capsicum entry landed; arbitrary-command/proxy-aware Capsicum parity pending | yes, tested (P0-02) |
 | Unclassified raw `read`/FASL/`load` sites | scanner-driven manifest classification gate closed 2026-07-27; trusted build, compiler, REPL/dev, and test surfaces are centrally justified by exact source line | 0 |
 | FFI bindings audited | scanner inventory and provisional verdicts in [ffi-audit.md](ffi-audit.md); native exports gated and justified | 100% with final verdicts |
 | Un-annotated Rust `unsafe` blocks | 0 unsafe review sites without nearby `SAFETY:` comments in [ffi-audit.md](ffi-audit.md) | 0 |
@@ -1319,11 +1319,11 @@ fake confidence happens.
   platforms. It now exposes explicit `sandbox:` fail-closed axis requests and
   `egress-policy:` proxy env wiring, installs Linux seccomp plus Landlock
   filesystem/TCP-connect controls before `exec` for requested axes, and
-  installs macOS Seatbelt no-write/no-network profiles before `exec` for
-  supported axes, and enters Capsicum at startup in the standard FreeBSD
-  worker-eval helper before reading the payload or evaluating it. Native
-  Seatbelt path/exec parity and arbitrary-command/proxy-aware Capsicum worker
-  parity remain pending (P0-02/P1-08).
+  installs macOS Seatbelt deny-default path/exec/no-network profiles before
+  `exec` for supported axes, and enters Capsicum at startup in the standard
+  FreeBSD worker-eval helper before reading the payload or evaluating it.
+  Arbitrary-command/proxy-aware Capsicum worker parity remains pending
+  (P0-02/P1-08).
 - The committed `pattern.ss` balance blocker named in P0-01 was repaired on
   2026-07-27. The `pipeline.ss` and `test-pipeline.ss` reports were traced to
   escaped-identifier false positives in the balance scanner; the scanner now
diff --git a/docs/security-reference.md b/docs/security-reference.md
index d7f602a..bc560a1 100644
--- a/docs/security-reference.md
+++ b/docs/security-reference.md
@@ -16,7 +16,7 @@ Jerboa's security model is layered defense-in-depth. No single layer is trusted 
 | **Taint tracking** | Mark untrusted data, reject at dangerous sinks | `(std security taint)` |
 | **Kernel enforcement** | Landlock filesystem rules, seccomp-BPF syscall filtering | `(std security landlock)`, `(std security seccomp)` |
 | **Privilege separation** | Pipe channels for separately exec'd supervisor/worker processes | `(std security privsep)` |
-| **Exec worker** | Run restricted eval in a fresh argv-exec'd process with pure env, deadline, output caps, supported memory rlimits, Linux seccomp/Landlock, and macOS Seatbelt no-write/no-network profiles | `(std security worker)` |
+| **Exec worker** | Run restricted eval in a fresh argv-exec'd process with pure env, deadline, output caps, supported memory rlimits, Linux seccomp/Landlock, and macOS Seatbelt deny-default path/exec/no-network profiles | `(std security worker)` |
 | **Parser hardening** | Depth limits, size limits, backtracking budgets | Various (see section 7) |
 | **Crypto** | AEAD, CSPRNG, HMAC, KDF, timing-safe comparison, secure memory | `(std crypto ...)` |
 | **Input sanitization** | Context-aware escaping for HTML, SQL, paths, headers, URLs | `(std security sanitize)` |
@@ -500,14 +500,16 @@ Landlock TCP connect port; host allowlisting still belongs to the proxy because
 Landlock's network ABI is port-scoped. Diagnostics include
 `native-landlock-requested?` and `native-landlock-installed?`.
 
-When `sandbox:` includes `fs`, `net`, or `seatbelt` on macOS, the worker passes
-an SBPL profile to `(std os aproc)`. The native child installs Seatbelt with
-`sandbox_init` after fd and rlimit setup and before `exec`. The current worker
-profile uses reliable no-write enforcement for `fs`/`seatbelt` and no-network
-enforcement for `net` without an egress proxy. Seatbelt `exec` path confinement
-and proxy-specific network allowlisting remain degraded and therefore refuse
-under the default `fail-closed?: #t`. Diagnostics include
-`native-seatbelt-requested?` and `native-seatbelt-installed?`.
+When `sandbox:` includes `fs`, `exec`, `net`, or `seatbelt` on macOS, the worker
+passes a deny-default SBPL cage profile to `(std os aproc)`. The native child
+installs Seatbelt with `sandbox_init` after fd and rlimit setup and before
+`exec`. The generated profile grants the runtime read paths needed by Chez and
+dyld, grants executable paths for the worker command and system launch helpers,
+keeps regular-file reads path-scoped, permits inherited stdin pipe reads, and
+omits network access when `net` is requested without an egress proxy. Proxy-
+specific network allowlisting remains degraded and therefore refuses under the
+default `fail-closed?: #t`. Diagnostics include `native-seatbelt-requested?`
+and `native-seatbelt-installed?`.
 
 When `sandbox:` includes `fs`, `exec`, `net`, or `capsicum` on FreeBSD for the
 standard `worker-run-eval` helper, the parent marks the pure child environment
@@ -569,12 +571,11 @@ Current limitation: the worker has a real exec boundary, pure environment,
 parent deadline, process-group kill through `aproc`, returned output caps,
 audit-log start/end records, memory rlimits on supported platforms, Linux
 syscall/ptrace seccomp setup, Linux Landlock filesystem and TCP-connect setup
-for requested axes, macOS Seatbelt no-write/no-network setup for supported
-axes, explicit fail-closed sandbox-axis requests, and allow-proxy environment
-wiring. The standard FreeBSD eval helper can enter Capsicum before reading the
-payload or evaluating it. Seatbelt path/exec parity, arbitrary-command
-Capsicum worker parity, and proxy-aware Capsicum networking remain tracked by
-the K3 handoff.
+for requested axes, macOS Seatbelt deny-default path/exec/no-network setup for
+supported axes, explicit fail-closed sandbox-axis requests, and allow-proxy
+environment wiring. The standard FreeBSD eval helper can enter Capsicum before
+reading the payload or evaluating it. Arbitrary-command Capsicum worker parity
+and proxy-aware Capsicum networking remain tracked by the K3 handoff.
 
 ---
 
diff --git a/docs/status.md b/docs/status.md
index 11bd87b..fba4274 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -14,7 +14,7 @@ release artifacts are built as Jerboa multicall binaries with `jerboa`,
 | CLI | `jerboa`, `jerboa run`, `jerboa eval`, `jerboa test`, `jerboa build`, and `jerboa pkg` are the documented entry points. |
 | Language | `(jerboa prelude)` is the default import for user code and resolves to the safe surface for direct scripts; use `--unsafe-prelude` or `(jerboa prelude unsafe)` only for explicit raw access. `.sls` files remain implementation internals. |
 | Standard library | `lib/std` and `lib/jerboa` contain 647 `.ss` modules at this snapshot. See [libraries.md](libraries.md) and [api-index.md](api-index.md). |
-| Security | Landlock, seccomp, capability plans, taint-aware safe-prelude sinks, restricted evaluation, safe FASL, the exec worker facade, memory rlimit pre-exec setup, worker syscall/ptrace seccomp setup on Linux, worker Landlock filesystem/TCP-connect setup on Linux, worker Seatbelt no-write/no-network setup on macOS, standard worker-eval Capsicum entry on FreeBSD, security regression tests, and release evidence are implemented. Seatbelt path/exec parity and arbitrary-command Capsicum worker parity remain open hardening work. |
+| Security | Landlock, seccomp, capability plans, taint-aware safe-prelude sinks, restricted evaluation, safe FASL, the exec worker facade, memory rlimit pre-exec setup, worker syscall/ptrace seccomp setup on Linux, worker Landlock filesystem/TCP-connect setup on Linux, worker Seatbelt deny-default path/exec/no-network setup on macOS, standard worker-eval Capsicum entry on FreeBSD, security regression tests, and release evidence are implemented. Arbitrary-command/proxy-aware Capsicum worker parity remains open hardening work. |
 | Native Rust | `libjerboa_native` backs crypto, secure memory, compression, regex, selected databases, OS integrations, packet capture, and rustls TLS. |
 | Packaging | `jpkg` is shipped through the Jerboa multicall binary and documented in [jpkg-guide.md](jpkg-guide.md). |
 | Build and release | `make binary` is the local non-Linux release build, `make docker-build` is the Linux release build, and `make release-evidence` collects audit evidence. |
@@ -27,7 +27,7 @@ release artifacts are built as Jerboa multicall binaries with `jerboa`,
 | Kimi security handoff | [kimi3-security-recommmendations.md](kimi3-security-recommmendations.md) is the backlog. Dated evidence and review manifests live under [reviews/](reviews/). | Keep new security evidence in dated review records and summarize the current release state here. |
 | FFI audit phase 5 | [ffi-audit.md](ffi-audit.md) records the scanner output, provisional Scheme binding verdicts, and native Rust export inventory. `make native-export-review-check` gates native export decisions. The worker-launch native path in `aproc.rs`/`seccomp.rs`, crypto FFI buffer path in `crypto.rs`, secure-memory region lifecycle in `secure_mem.rs`, secure filesystem capability boundary in `secure_fs.rs`, TLS native ABI in `tls.rs`, Ed25519/X25519 key-agreement buffers, compression buffers, HTTP parse/writev boundary, embed-crypto ABI, integrity ABI, regex-native ABI, process-control ABI, pcap ABI, inotify ABI, epoll/eventfd ABI, antidebug ABI, SOCKS5 server ABI, PostgreSQL native ABI, Landlock native ABI, wasmi sandbox ABI, SpiderMonkey WASM ABI, DuckDB native ABI, X.509 native ABI, and SQLite native ABI now have nearby `SAFETY:` comments or equivalent checked FFI invariants. Generated counts are at 0 unannotated native unsafe sites and 0 Scheme blocking bindings missing `__collect_safe`. | Finish per-binding Scheme FFI review and targeted scanner rules for the remaining provisional verdicts. |
 | Native Rust exports | The native export review now has 190 exported functions: 183 tracked Scheme references and 7 retained standalone C/binary helpers. The previous 35 no-Scheme-reference removal candidates no longer have C ABI export markers. | Re-run `make native-export-review-check` whenever adding or removing native exports. |
-| Confined worker | `(std security worker)` provides the facade, audit lifecycle, output caps, deadlines, process-group kill, memory rlimit pre-exec setup, Linux syscall/ptrace seccomp pre-exec setup, Linux Landlock filesystem/TCP-connect setup for requested axes, macOS Seatbelt no-write/no-network setup for supported axes, standard worker-eval Capsicum entry on FreeBSD, explicit sandbox-axis refusal, and egress proxy env wiring. | Keep Linux/macOS/FreeBSD parity tests current; finish Seatbelt path/exec confinement and arbitrary-command/proxy-aware Capsicum worker paths. |
+| Confined worker | `(std security worker)` provides the facade, audit lifecycle, output caps, deadlines, process-group kill, memory rlimit pre-exec setup, Linux syscall/ptrace seccomp pre-exec setup, Linux Landlock filesystem/TCP-connect setup for requested axes, macOS Seatbelt deny-default path/exec/no-network setup for supported axes, standard worker-eval Capsicum entry on FreeBSD, explicit sandbox-axis refusal, and egress proxy env wiring. | Keep Linux/macOS/FreeBSD parity tests current; finish arbitrary-command/proxy-aware Capsicum worker paths. |
 | Fuzzing | `tests/fuzz/corpus/` has 15 checked-in seed inputs, `tests/fuzz/regression/` has 11 crash/rejection regressions, `make fuzz-smoke` runs the deterministic regression gate first, and GitHub CI runs smoke fuzzing normally plus deep fuzzing on scheduled daily runs. | Keep adding minimized corpus and regression inputs for every parser/security bug found. |
 | Safe surface | Direct scripts default to the safe prelude; raw access requires `--unsafe-prelude` or `(jerboa prelude unsafe)`. | Continue moving risky APIs behind explicit unsafe imports as new modules land. |
 
diff --git a/lib/std/security/seatbelt.ss b/lib/std/security/seatbelt.ss
index af3baf6..dac5893 100644
--- a/lib/std/security/seatbelt.ss
+++ b/lib/std/security/seatbelt.ss
@@ -245,8 +245,11 @@
       "/System/Library"
       "/Library/Frameworks"
       "/Library/Apple"
-      ;; dyld shared cache
+      ;; dyld shared cache. Modern macOS may resolve dyld cache files
+      ;; through Cryptex roots before Scheme user code starts.
       "/private/var/db/dyld"
+      "/System/Cryptexes/OS"
+      "/System/Volumes/Preboot/Cryptexes/OS"
       ;; TLS certificate roots
       "/etc/ssl"
       "/private/etc/ssl"
@@ -379,10 +382,10 @@
         [else s])))
 
   (def (build-cage-sbpl rw-paths ro-paths exec-paths network?)
-    ;; Read access covers: all read-only paths AND all read-write paths
-    ;; (write access without read access is rarely useful, and Chez
-    ;; needs to read its boot files anyway).
-    (let* ([all-readable (append ro-paths rw-paths)]
+    ;; Read access covers read-only, read-write, and executable paths.
+    ;; macOS exec still needs to read/map the target and loader inputs;
+    ;; write access without read access is rarely useful.
+    (let* ([all-readable (append ro-paths rw-paths exec-paths)]
            [read-forms  (paths->sbpl-forms all-readable)]
            [write-forms (paths->sbpl-forms rw-paths)]
            [exec-forms  (paths->sbpl-forms exec-paths)])
@@ -397,6 +400,10 @@
         "(allow ipc-posix-shm*)"
         "(allow file-ioctl)"
         "(allow file-read-metadata)"
+        ;; Inherited stdin is a pipe/non-regular vnode. Keep regular-file
+        ;; reads path-scoped while allowing data reads from supervisor pipes.
+        "(if (defined? 'vnode-type)"
+        "  (allow file-read-data (require-not (vnode-type REGULAR-FILE))))"
         ;; Read access
         (if (null? all-readable)
           ""
@@ -405,10 +412,12 @@
         (if (null? rw-paths)
           ""
           (string-append "(allow file-write*" write-forms ")"))
-        ;; Execute access
+        ;; Execute access: process-exec gates the spawn. Executable mapping
+        ;; must also cover readable runtime library paths used by dyld/Chez.
         (if (null? exec-paths)
           ""
-          (string-append "(allow process-exec*" exec-forms ")"))
+          (string-append "(allow process-exec*" exec-forms ")"
+                         "(allow file-map-executable" read-forms ")"))
         ;; Network
         (if network?
           "(allow network*)"
diff --git a/lib/std/security/worker.ss b/lib/std/security/worker.ss
index 07386fb..976bb01 100644
--- a/lib/std/security/worker.ss
+++ b/lib/std/security/worker.ss
@@ -70,7 +70,10 @@
                 env-policy-validate-command
                 env-policy-audit-summary)
           (only (std security seatbelt)
-                seatbelt-available?)
+                seatbelt-available?
+                seatbelt-cage-profile
+                seatbelt-macos-system-read-paths
+                seatbelt-macos-system-execute-paths)
           (only (std security capsicum)
                 capsicum-available?))
 
@@ -382,7 +385,7 @@
 
   (def (worker-native-seatbelt-supported-axis? pol axis)
     (case axis
-      [(fs seatbelt) #t]
+      [(fs exec seatbelt) #t]
       [(net) (not (worker-policy-egress-policy pol))]
       [else #f]))
 
@@ -472,15 +475,31 @@
 
   (def (worker-seatbelt-profile pol command)
     (if (worker-native-seatbelt-requested? pol)
-        (let ([axes (worker-policy-sandbox pol)])
-          (string-append
-           "(version 1)(allow default)"
-           (if (or (memq 'fs axes) (memq 'seatbelt axes))
-               "(deny file-write*)"
-               "")
-           (if (memq 'net axes)
-               "(deny network*)"
-               "")))
+        (let* ([axes (worker-policy-sandbox pol)]
+               [cwd (or (worker-policy-cwd pol) (current-directory))]
+               [cmd0 (and (pair? command) (car command))]
+               [cmd-dir (and (string? cmd0) (path-dirname cmd0))]
+               [repo-root (absolute-path ".")]
+               [runtime-read (existing-paths
+                              (append (list cwd repo-root
+                                            (absolute-path "lib")
+                                            (absolute-path ".chez/lib")
+                                            (absolute-path "vendor/jsqlite/src")
+                                            (absolute-path "support/security-worker-main.ss")
+                                            "/etc/resolv.conf"
+                                            "/dev/urandom")
+                                      seatbelt-macos-system-read-paths))]
+               [runtime-exec (existing-paths
+                               (append (list cmd-dir
+                                             (absolute-path ".chez/bin")
+                                             (absolute-path ".chez/lib"))
+                                       seatbelt-macos-system-execute-paths))]
+               [network? (not (memq 'net axes))])
+          (seatbelt-cage-profile
+           'read-only: runtime-read
+           'read-write: '()
+           'execute: runtime-exec
+           'network: network?))
         #f))
   (def (egress-policy-summary pol)
     (let ([egress (worker-policy-egress-policy pol)])
diff --git a/tests/test-seatbelt.ss b/tests/test-seatbelt.ss
index 7311239..be0f926 100644
--- a/tests/test-seatbelt.ss
+++ b/tests/test-seatbelt.ss
@@ -142,11 +142,13 @@
                   "(allow network*)"))
   #t)
 
-(test "seatbelt-cage-profile execute: adds process-exec*"
-  (contains? (seatbelt-cage-profile
-               'read-only: '("/usr/lib")
-               'execute:   '("/usr/bin"))
-             "process-exec*")
+(test "seatbelt-cage-profile execute: adds exec, mapping, and pipe read grants"
+  (let ([p (seatbelt-cage-profile
+             'read-only: '("/usr/lib")
+             'execute:   '("/usr/bin"))])
+    (and (contains? p "process-exec*")
+         (contains? p "file-map-executable")
+         (contains? p "require-not (vnode-type REGULAR-FILE)")))
   #t)
 
 (test "seatbelt-cage-profile rejects unknown keyword"
diff --git a/tests/test-worker.ss b/tests/test-worker.ss
index 6b9d5a1..87f5c77 100644
--- a/tests/test-worker.ss
+++ b/tests/test-worker.ss
@@ -191,13 +191,13 @@
                (and (equal? (worker-result-status r) 126)
                     (pair? (worker-result-refused-axes r))))))))
 
-(test-pred "requested filesystem sandbox installs native kernel control or refuses"
+(test-pred "requested filesystem and exec sandbox installs native kernel control or refuses"
   (worker-run-eval
    "(+ 1 1)"
    (worker-policy
     'command: worker-command
     'timeout-ms: 3000
-    'sandbox: '(fs)
+    'sandbox: '(fs exec)
     'fail-closed?: #t))
   (lambda (r)
     (and (worker-result? r)
@@ -207,7 +207,9 @@
                     (or (and (eq? (alist-ref/default diag 'native-landlock-requested? #f) #t)
                              (eq? (alist-ref/default diag 'native-landlock-installed? #f) #t))
                         (and (eq? (alist-ref/default diag 'native-seatbelt-requested? #f) #t)
-                             (eq? (alist-ref/default diag 'native-seatbelt-installed? #f) #t))))
+                             (eq? (alist-ref/default diag 'native-seatbelt-installed? #f) #t))
+                        (and (eq? (alist-ref/default diag 'native-capsicum-requested? #f) #t)
+                             (eq? (alist-ref/default diag 'native-capsicum-installed? #f) #t))))
                (and (equal? (worker-result-status r) 126)
                     (pair? (worker-result-refused-axes r))))))))