SPB Git

spb/zyquo-mlx Public MIT

The local MLX foundry for your Mac — run, fine-tune, quantize, and ship models. Nothing leaves your machine.

Swift 93.4% Python 3.8% Makefile 2.2% Shell 0.5%

phase8: Developer ID signing + notarization — spctl 'Notarized Developer ID' on first submission, stapled app + DMG, minimal entitlements; Definition of Done review complete

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
simon-pierre boucher committed 11 days ago (Jul 31, 2026) parent 4e5bbb4

Showing 3 changed files with +115 and −5

modified Makefile +55 −4
@@ -30,7 +30,7 @@ BUILD_DIR := .build
30 30 CONFIG_TITLE = $(shell echo $(CONFIG) | awk '{print toupper(substr($$0,1,1)) substr($$0,2)}')
31 31 BIN_DIR = $(BUILD_DIR)/out/Products/$(CONFIG_TITLE)
32 32
33 .PHONY: build build-xcodebuild app dev run icon clean release check-toolchain
33 +.PHONY: build build-xcodebuild app dev run icon icon-if-missing clean release dmg check-toolchain
34 34
35 35 check-toolchain:
36 36 @test -d $(XCODE_DEV_DIR) || { echo "error: Xcode toolchain not found at /Applications/Xcode.app — see docs/BUILD.md §2"; exit 1; }
@@ -70,9 +70,60 @@ icon:
70 70 @# Phase 5: SVG → PNGs → .iconset → .icns (rsvg-convert + iconutil)
71 71 bash scripts/make-icon.sh
72 72
73 release: app
74 @# Phase 8: Developer ID signing + notarization + stapling (docs/BUILD.md §4)
75 @echo "release target completed in Phase 8 — currently ad-hoc only"; exit 1
73 +# Phase 8: Developer ID signing + notarization + stapling (docs/BUILD.md §4).
74 +# Identity/profile reused from the zyquo-term pipeline (charter Phase 8).
75 +IDENTITY := Developer ID Application: Simon-Pierre Boucher (3YM54G49SN)
76 +KEYCHAIN_PROFILE := MacLustr-Notarize
77 +ENTITLEMENTS := Support/entitlements.plist
78 +ZIP := $(DIST)/ZyquoMLX.zip
79 +DMG := $(DIST)/ZyquoMLX.dmg
80 +
81 +release: build icon-if-missing
82 + rm -rf "$(APP)"
83 + mkdir -p "$(APP)/Contents/MacOS" "$(APP)/Contents/Resources"
84 + cp Support/Info.plist "$(APP)/Contents/Info.plist"
85 + cp "$(BIN_DIR)/$(EXEC_NAME)" "$(APP)/Contents/MacOS/$(EXEC_NAME)"
86 + @for b in "$(BIN_DIR)"/*.bundle; do \
87 + [ -e "$$b" ] && cp -R "$$b" "$(APP)/Contents/Resources/" && echo "bundled $$(basename $$b)"; \
88 + done; true
89 + cp assets/icon/AppIcon.icns "$(APP)/Contents/Resources/AppIcon.icns"
90 + @echo "=== Signing (Developer ID, hardened runtime, nested-first) ==="
91 + @# Resource bundles carry no Mach-O code (metallib + .py are data) but we
92 + @# sign any nested code defensively before the outer bundle.
93 + @find "$(APP)/Contents/Resources" -type f \( -name "*.dylib" -o -name "*.so" \) -exec \
94 + codesign --force --options runtime --timestamp --sign "$(IDENTITY)" {} \; 2>/dev/null; true
95 + codesign --force --options runtime --timestamp \
96 + --entitlements $(ENTITLEMENTS) \
97 + --sign "$(IDENTITY)" "$(APP)/Contents/MacOS/$(EXEC_NAME)"
98 + codesign --force --options runtime --timestamp \
99 + --entitlements $(ENTITLEMENTS) \
100 + --sign "$(IDENTITY)" "$(APP)"
101 + codesign --verify --deep --strict --verbose=2 "$(APP)"
102 + @echo "=== Notarizing (profile: $(KEYCHAIN_PROFILE)) ==="
103 + rm -f "$(ZIP)"
104 + ditto -c -k --keepParent "$(APP)" "$(ZIP)"
105 + xcrun notarytool submit "$(ZIP)" --keychain-profile "$(KEYCHAIN_PROFILE)" --wait
106 + xcrun stapler staple "$(APP)"
107 + @echo "=== Verifying ==="
108 + spctl -a -vv "$(APP)"
109 + xcrun stapler validate "$(APP)"
110 + @echo "→ $(APP) (Developer ID signed, notarized, stapled)"
111 +
112 +dmg:
113 + rm -f "$(DMG)"
114 + rm -rf $(DIST)/dmg_temp
115 + mkdir -p $(DIST)/dmg_temp
116 + cp -R "$(APP)" $(DIST)/dmg_temp/
117 + ln -s /Applications "$(DIST)/dmg_temp/Applications"
118 + hdiutil create -volname "$(APP_NAME)" -srcfolder $(DIST)/dmg_temp -ov -format UDZO "$(DMG)"
119 + rm -rf $(DIST)/dmg_temp
120 + codesign --force --sign "$(IDENTITY)" --timestamp "$(DMG)"
121 + xcrun notarytool submit "$(DMG)" --keychain-profile "$(KEYCHAIN_PROFILE)" --wait
122 + xcrun stapler staple "$(DMG)"
123 + @echo "→ $(DMG) (signed, notarized, stapled)"
124 +
125 +icon-if-missing:
126 + @test -f assets/icon/AppIcon.icns || bash scripts/make-icon.sh
76 127
77 128 clean:
78 129 rm -rf $(BUILD_DIR) $(DIST)
added Support/entitlements.plist +23 −0
@@ -0,0 +1,23 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + entitlements.plist
4 + Zyquo MLX
5 +
6 + Author: Simon-Pierre Boucher
7 + Mail: contact@spboucher.ai
8 +
9 + Minimal posture per docs/BUILD.md §4: Hardened Runtime is enabled at signing
10 + time; MLX's runtime kernel compilation goes through the Metal framework
11 + (GPU shader compile), NOT process-writable-executable memory, so no
12 + allow-jit / unsigned-executable-memory is needed (verified: notarization
13 + passes without them). The app is a local ML workbench needing broad
14 + file/compute access and spawns its own provisioned Python — no App Sandbox
15 + (same rationale as the rest of the Zyquo family).
16 +-->
17 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
18 +<plist version="1.0">
19 +<dict>
20 + <key>com.apple.security.cs.allow-jit</key>
21 + <false/>
22 +</dict>
23 +</plist>
modified docs/PLAN.md +37 −1
@@ -177,4 +177,40 @@ model types plus training/convert/hub/python-env. Three real-world defects
177 177 were found *by* verification and fixed: cancelled runs mis-recorded as
178 178 completed, decoder-style embedding repos mis-typed as LLM, and stale sharded
179 179 indexes in upstream repos rejected by validation. Results in VERIFICATION.md.
180 ## Phase 8 — Signing & Notarization (not started)
180 +## Phase 8 — Signing & Notarization ✅ (completed 2026-07-30)
181 +
182 +- [x] zyquo-term setup inspected: identity `Developer ID Application: Simon-Pierre Boucher (3YM54G49SN)` + profile `MacLustr-Notarize` reused; identity verified present in keychain (no secrets printed/committed)
183 +- [x] `Support/entitlements.plist` — Hardened Runtime, minimal posture (no sandbox, no allow-jit — MLX's Metal runtime compile is GPU shader compilation, not process JIT)
184 +- [x] `make release`: nested-first signing → `ditto` zip → `notarytool submit --wait` → staple → verify
185 +- [x] Signed + notarized + stapled DMG (`make dmg`)
186 +- [x] **PHASE GATE GREEN on first submission:** `spctl -a -vv``accepted, source=Notarized Developer ID`; `stapler validate` OK; notarized app launches
187 +- [x] Final coherence sweep: headers ✓, naming ✓, no TODO/dead code ✓, zero warnings ✓
188 +
189 +**Phase 8 summary:** `make release` produces a Developer ID-signed, notarized,
190 +stapled `Zyquo MLX.app` (plus DMG) entirely from the command line — no Xcode
191 +IDE, no `.xcodeproj`. Notarization passed on the first attempt with the
192 +minimal entitlements posture, confirming BUILD.md §4's analysis. The
193 +bundled-Python notarization minefield never arises because the venv lives in
194 +Application Support, outside the signed bundle.
195 +
196 +---
197 +
198 +## Definition of Done — final review (2026-07-30)
199 +
200 +-`make release` → Developer ID-signed, notarized, stapled app, no Xcode IDE
201 +- ✅ Inference across MLX-supported types: LLM/VLM/embeddings/speech verified
202 + with streaming + memory release (image-gen: documented deferral — upstream
203 + pipelines are script-only; Swift StableDiffusion lib is the follow-up path)
204 +- ✅ Real LoRA + QLoRA end-to-end: live metrics, checkpoints, cancel, warm
205 + resume, fuse, export — with two upstream landmines found and handled
206 +- ✅ Conversion + quantization produce valid, running MLX models (Swift-native
207 + + Python paths); export lands in the shared Models library
208 +- ✅ Datasets import/validate/split/preview; Hub browse + resumable downloads +
209 + curated catalog live-verified (16/16 + 43-repo sweep)
210 +- ✅ MemoryAdvisor: device-derived gating, verdicts on every surface, size
211 + prediction measured at 0.1 % accuracy; blocked-config UX verified
212 +- ✅ Copper-on-slate icon (Z dominant, anvil/spark story), SVG source of truth,
213 + Dock-verified; light theme flagship + derived dark, token-only styling
214 +- ✅ Naming coherent (`Zyquo MLX` / `com.zyquo.mlx` / `ZyquoMLX`); headers on
215 + every code file (swept); zero warnings; phase-prefixed commit history
216 +- ✅ All Phase 0 research docs complete and traceable into the implementation
181 217