macOS App Store / App Sandbox support + packaging inputs#33
Merged
techomancer merged 7 commits intoJun 11, 2026
Conversation
Adds an `appstore` cargo feature and a `macos_sandbox` module that mints app-scoped security-scoped bookmarks for user-selected files (disk images, PROM, ISOs, NFS dir) at save time and resolves them at startup, so a sandboxed macOS build can reopen them across launches. Gated on `target_os = "macos"` + `feature = "appstore"`; a no-op everywhere else, so non-macOS and non-sandboxed builds are unaffected. - macos_sandbox.rs: make_bookmark/start_access over objc2-foundation NSURL, plus config_paths/harvest/restore helpers. Objc round-trip and the harvest/restore logic are unit-tested (cargo test --features appstore). - GuiSettings: persist bookmarks (path -> bytes); save() harvests across all machines before writing. - objc2 / objc2-foundation added as macOS-target deps (already in the build graph via eframe; just enabling a few type features). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per the path-based fork policy, the GUI's packaging metadata + desktop entry,
the macOS/Windows installer configs, the macOS build script, the workspace
profiling profile, and the App-Store CI-tab gating are all build inputs that
should track upstream rather than diverge. Mirror them here.
- iris-gui/Cargo.toml: deb/rpm packaging metadata, description/license.
- iris-gui/iris-gui.desktop, scripts/build-macos.sh, installer/{entitlements,iss}.
- iris-gui/src/{config_ui,main}.rs: hide the CI/Automation tab under the
`appstore` feature (Tab::visible), matching the sandbox build.
- Cargo.toml/profile.sh: profiling-profile + flamegraph helper tweaks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror of the sandbox disk fixes: - machine.rs: skip an unattachable disk (warn) instead of process::exit when embedded — release builds are panic="abort", so catch_unwind can't save the GUI, and unwinding across the libchdman/JIT FFI would be UB. - chd_disk.rs: redirect the compressed-HD `.diff.chd` sidecar via IRIS_CHD_DIFF_DIR so it isn't created next to the parent (which the sandbox forbids). - iris-gui: set IRIS_CHD_DIFF_DIR to the data dir on the appstore build; probe an actual read in the Start preflight; hold the resolved security-scoped NSURL so bookmark access persists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Not a recognized Cargo profile key; emitted 'unused manifest key' on every build and did nothing. Matches upstream. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cranelift (the MIPS JIT and the always-on REX3 draw-shader JIT) allocates executable memory with mmap+mprotect, not MAP_JIT. The macOS App Sandbox only permits MAP_JIT pages (com.apple.security.cs.allow-jit), so the first JITed REX3 draw is killed with SIGKILL / CODESIGNING "Invalid Page" on the REX3-Processor thread: the emulator window opens, then dies before any video. Gate the JIT off for the sandboxed build only; JIT stays on by default everywhere else: - iris-gui main.rs sets IRIS_NO_JIT=1 under cfg(appstore), before any worker thread starts. - Rex3::new builds rex_jit: None + jit_enabled: false when IRIS_NO_JIT is set (must skip *constructing* RexJit — its warm-up compiler thread allocates executable memory immediately). - run_jit_dispatch: IRIS_NO_JIT overrides IRIS_JIT. The interpreter is the normal fall-through for both JITs, so correctness is unaffected — only draw/CPU throughput. Notarized Developer-ID builds keep the JIT via an allow-unsigned-executable-memory entitlement (handled in the fork pipeline). See rules/jit/macos-app-sandbox-kills-cranelift-jit-no-map_jit.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related additions for distributing iris-gui as a real app, both inert for normal
cargo builds:appstorecargo feature that is a no-op for every existing build. The bulk is making disk I/O work under the App Sandbox and ensuring the GUI is never torn down by a disk it can't open.1. macOS App Store / App Sandbox support
appstorecargo feature (off by default) gates all sandbox-only behavior.Security-scoped bookmarks (
iris-gui/src/macos_sandbox.rs): under thesandbox the app may only touch files the user explicitly picks, and only for
that launch. We mint an app-scoped bookmark for each user-selected file (disk
image, PROM, ISO, NFS dir) at save and resolve it at startup so they reopen
across launches. macOS-only; no-op stubs elsewhere. Objc round-trip is
unit-tested. The resolved
NSURLis held for the process lifetime (thesecurity-scoped access is bound to it).
in place, so writes go to a
.diff.chdsidecar created next to the parent,which the sandbox forbids.
diff_path_fornow honorsIRIS_CHD_DIFF_DIRtoplace the diff somewhere writable.
process::exiton a failed disk attach (src/machine.rs): releasebuilds use
panic = "abort"(unwinding across the libchdman/JIT FFI is UB), soan embedder can't
catch_unwind. When embedded (IRIS_NO_EXIT_ON_POWEROFF) welog a warning and skip just that device instead of killing the app. CLI still
fails loud.
iris-gui/src/main.rs): the sandbox can permitopen()yet denyread()once a grant lapses, so the preflight probes a realread and routes bad disks to the missing-disk modal.
appstore; add entitlements(
installer/iris-gui.entitlements); add macOS-targetobjc2/objc2-foundationdeps (already in the tree via eframe).
Tested end-to-end on a sandboxed bundle: a compressed IRIX CHD attaches, IRIX
boots to the desktop, and the app no longer crashes on an unreadable disk.
2. Packaging + build inputs
installer/iris-gui.iss— Inno Setup script for a Windows installer.iris-gui/iris-gui.desktop— Linux desktop entry.scripts/build-macos.sh— builds a signed.appbundle (and.gitignoreignores the resulting
IRIS.app/).iris-gui/Cargo.toml—[package.metadata.deb|generate-rpm]+ description/license for Linux packaging.
profile.sh— flamegraph helper tweak.Notes
appstorebuilds; Linux/Windows CI unaffected.