Skip to content

feat: sign and notarize the macOS app, fetch FastVLM on first run - #16

Merged
xtanion merged 1 commit into
mainfrom
feat/perception-rust
Sep 7, 2026
Merged

feat: sign and notarize the macOS app, fetch FastVLM on first run#16
xtanion merged 1 commit into
mainfrom
feat/perception-rust

Conversation

@xtanion

@xtanion xtanion commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Signing was producing an invalid bundle: build_app.sh re-signed ad-hoc with --deep, and build.sh then copied the daemon, helpers and models into Contents/Resources afterwards, breaking the seal ("a sealed resource is missing or invalid"). The app is now built unsigned and signed once, after staging, inside-out: nested executables first, app bundle last, then notarize, staple, and sign/notarize/staple the .dmg.

Fixes found along the way:

  • ENABLE_APP_SANDBOX was YES in Release. The ad-hoc re-sign was stripping it, so it never bit; a real Developer ID signature would have sandboxed the app and broken spawning the daemon, the Accessibility API, and ~/.patcha writes.
  • MACOSX_DEPLOYMENT_TARGET was 26.2 while LSMinimumSystemVersion claimed 13.0. Lowered to 14.0, which builds clean with no code changes.
  • The daemon shells out to osascript, but NSAppleEventsUsageDescription was missing. Under hardened runtime that crashes the process rather than showing a denial.
  • PatchaSourceRoot is stripped at staging time so releases do not ship a local filesystem path.

The FastVLM captioner model (~810 MB) is no longer bundled; the daemon fetches it to ~/.patcha/models/fastvlm on first run, resuming via HTTP Range and publishing progress to ~/.patcha/model_download.json, which the menu bar displays. Availability is re-checked per caption so gist starts working when the download lands rather than needing a restart, and is now keyed to the full file set instead of two files, which a partial download could satisfy.

This takes the .dmg from 1.0 GB to 84 MB. It also drops decoder_model_merged_q4f16.onnx, 269 MB the captioner never loads.

Adds docs/RELEASING.md, a tag-triggered release workflow, and patcha fetch-models for pre-seeding.

Claude-Session: https://claude.ai/code/session_01PLRQp3DqAuLw8A8UPeDf4p

What does this change?

Related issues

How was it tested?

  • cargo build passes (in rust/)
  • cargo clippy --all-targets is clean
  • cargo fmt --all applied
  • cargo test passes locally (some collector tests require macOS permissions)

Checklist

  • Commit messages follow the type: description convention (feat:, fix:, chore:)
  • I did not commit secrets or personal activity data (~/.patcha, .env)
  • Docs updated if behavior or configuration changed

Signing was producing an invalid bundle: build_app.sh re-signed ad-hoc with
--deep, and build.sh then copied the daemon, helpers and models into
Contents/Resources afterwards, breaking the seal ("a sealed resource is
missing or invalid"). The app is now built unsigned and signed once, after
staging, inside-out: nested executables first, app bundle last, then
notarize, staple, and sign/notarize/staple the .dmg.

Fixes found along the way:

- ENABLE_APP_SANDBOX was YES in Release. The ad-hoc re-sign was stripping it,
  so it never bit; a real Developer ID signature would have sandboxed the app
  and broken spawning the daemon, the Accessibility API, and ~/.patcha writes.
- MACOSX_DEPLOYMENT_TARGET was 26.2 while LSMinimumSystemVersion claimed 13.0.
  Lowered to 14.0, which builds clean with no code changes.
- The daemon shells out to osascript, but NSAppleEventsUsageDescription was
  missing. Under hardened runtime that crashes the process rather than
  showing a denial.
- PatchaSourceRoot is stripped at staging time so releases do not ship a
  local filesystem path.

The FastVLM captioner model (~810 MB) is no longer bundled; the daemon fetches
it to ~/.patcha/models/fastvlm on first run, resuming via HTTP Range and
publishing progress to ~/.patcha/model_download.json, which the menu bar
displays. Availability is re-checked per caption so gist starts working when
the download lands rather than needing a restart, and is now keyed to the full
file set instead of two files, which a partial download could satisfy.

This takes the .dmg from 1.0 GB to 84 MB. It also drops
decoder_model_merged_q4f16.onnx, 269 MB the captioner never loads.

Adds docs/RELEASING.md, a tag-triggered release workflow, and
`patcha fetch-models` for pre-seeding.

Claude-Session: https://claude.ai/code/session_01PLRQp3DqAuLw8A8UPeDf4p
@xtanion
xtanion merged commit 89f5e49 into main Sep 7, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant