SPB Git

spb/zyquo-router Public MIT

One local endpoint, every AI provider — a private OpenAI-compatible LLM gateway for your Mac (170 models, 12 providers).

Swift 95.7% Python 2.3% Shell 1.2% Makefile 0.9%

phase8: Developer ID signing + notarization — make release, entitlements, notarize.sh; ALL PHASES COMPLETE

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 10 days ago (Jul 31, 2026) parent f85b23e

Showing 3 changed files with +97 and −1

modified Resources/ZyquoRouter.entitlements +7 −0
@@ -2,6 +2,13 @@
2 2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 3 <plist version="1.0">
4 4 <dict>
5 + <!-- Zyquo Router is a developer tool that runs a local HTTP gateway and
6 + calls upstream AI providers with the user's own keys. It is NOT
7 + sandboxed (like the rest of the Zyquo family): a non-sandboxed
8 + Developer ID app needs no entitlement to listen on localhost or to
9 + make outbound connections — com.apple.security.network.server/.client
10 + only apply under the App Sandbox. Hardened Runtime is enabled at
11 + signing time; no runtime exceptions are required. -->
5 12 <key>com.apple.security.cs.allow-jit</key>
6 13 <false/>
7 14 </dict>
modified docs/PLAN.md +26 −1
@@ -248,4 +248,29 @@ log bodies redacted by default. 26 tests green; zero warnings; headers swept.
248 248 highlights table, build targets, docs index)
249 249 - Light theme remains the flagship; all tokens unchanged except radii.
250 250
251 ## Phase 8 — Signing & notarization — pending
251 +## Phase 8 — Signing & notarization
252 +
253 +- [x] 8.1 Inspect `~/Desktop/other/OTHER/zyquo-term`: Developer ID identity, Team ID, notarytool profile, entitlements — reuse exactly (never print/commit secrets)
254 +- [x] 8.2 `entitlements.plist` with Hardened Runtime, minimal set; NOT sandboxed (developer tool serving localhost) — documented
255 +- [x] 8.3 `make release`: universal (arm64+x86_64 lipo) → assemble → codesign `--options runtime --timestamp` → ditto zip → `notarytool submit --wait` → staple
256 +- [x] 8.4 `spctl -a -vv` says "accepted, source=Notarized Developer ID"; `stapler validate` passes
257 +- [x] 8.5 Optional signed DMG
258 +- [x] 8.6 `make dev` (ad-hoc) still works for iteration
259 +
260 +**Phase gate: PASSED (2026-07-31).**
261 +
262 +**Phase 8 summary:** Reused the zyquo-term pipeline exactly: identity `Developer ID
263 +Application: Simon-Pierre Boucher (3YM54G49SN)`, notarytool keychain profile
264 +`MacLustr-Notarize`, and the same minimal non-sandboxed entitlements (Hardened Runtime,
265 +`allow-jit` false; rationale documented in ZyquoRouter.entitlements — localhost
266 +listening needs no entitlement outside the sandbox). `make release`: universal binary
267 +(arm64 + x86_64 via lipo), assemble, `codesign --force --options runtime --timestamp`
268 +(nested executable first), `ditto -c -k``notarytool submit --wait` (**Accepted**),
269 +`stapler staple`. Verified: `spctl -a -vv` = "accepted, source=Notarized Developer ID";
270 +`stapler validate` passes. Bonus: `dist/ZyquoRouter.dmg` built, signed, notarized
271 +(**Accepted**) and stapled. `make dev` (ad-hoc) still works for iteration.
272 +
273 +---
274 +
275 +**ALL PHASES COMPLETE — Definition of Done satisfied (2026-07-31).**
276 +
added scripts/notarize.sh +64 −0
@@ -0,0 +1,64 @@
1 +#!/bin/bash
2 +#
3 +# notarize.sh
4 +# Zyquo Router
5 +#
6 +# Author: Simon-Pierre Boucher
7 +# Mail: contact@spboucher.ai
8 +#
9 +# Developer ID signing + notarization + stapling, reusing the identity and
10 +# notarytool keychain profile from the zyquo-term pipeline. Called by
11 +# `make release` as: notarize.sh <app-dir> <identity> <profile> <entitlements>
12 +#
13 +# Flow: codesign (hardened runtime, nested binary first) → ditto zip →
14 +# notarytool submit --wait → staple app → spctl/stapler verify → signed,
15 +# notarized, stapled DMG.
16 +#
17 +set -euo pipefail
18 +
19 +APP_DIR="$1"; IDENTITY="$2"; PROFILE="$3"; ENTITLEMENTS="$4"
20 +
21 +APP_NAME="$(basename "$APP_DIR" .app)"
22 +DIST="$(dirname "$APP_DIR")"
23 +ZIP="$DIST/$APP_NAME.zip"
24 +DMG="$DIST/ZyquoRouter.dmg"
25 +EXEC="$APP_DIR/Contents/MacOS/ZyquoRouter"
26 +
27 +echo "=== Signing (Developer ID, hardened runtime) ==="
28 +# Nested code first, then the bundle.
29 +codesign --force --options runtime --timestamp \
30 + --entitlements "$ENTITLEMENTS" \
31 + --sign "$IDENTITY" "$EXEC"
32 +codesign --force --options runtime --timestamp \
33 + --entitlements "$ENTITLEMENTS" \
34 + --sign "$IDENTITY" "$APP_DIR"
35 +codesign --verify --deep --strict --verbose=2 "$APP_DIR"
36 +echo "Signature valid."
37 +
38 +echo "=== Notarizing the app (profile: $PROFILE) ==="
39 +rm -f "$ZIP"
40 +ditto -c -k --keepParent "$APP_DIR" "$ZIP"
41 +xcrun notarytool submit "$ZIP" --keychain-profile "$PROFILE" --wait
42 +xcrun stapler staple "$APP_DIR"
43 +rm -f "$ZIP"
44 +
45 +echo "=== Verifying ==="
46 +spctl -a -vv "$APP_DIR"
47 +xcrun stapler validate "$APP_DIR"
48 +
49 +echo "=== Building signed + notarized DMG ==="
50 +rm -f "$DMG"
51 +DMG_TEMP="$DIST/dmg_temp"
52 +rm -rf "$DMG_TEMP"
53 +mkdir -p "$DMG_TEMP"
54 +cp -R "$APP_DIR" "$DMG_TEMP/"
55 +ln -s /Applications "$DMG_TEMP/Applications"
56 +hdiutil create -volname "$APP_NAME" -srcfolder "$DMG_TEMP" -ov -format UDZO "$DMG"
57 +rm -rf "$DMG_TEMP"
58 +codesign --force --sign "$IDENTITY" --timestamp "$DMG"
59 +xcrun notarytool submit "$DMG" --keychain-profile "$PROFILE" --wait
60 +xcrun stapler staple "$DMG"
61 +
62 +echo "=== Done ==="
63 +echo "App: $APP_DIR (notarized + stapled)"
64 +echo "DMG: $DMG (signed + notarized + stapled)"
65