SPB Git

spb/zyquo-agent Public MIT

The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.

Swift 94.7% Shell 4.1% Python 0.7% Makefile 0.5%

phase8: Developer ID signing + notarization — entitlements, notarize.sh, notarized+stapled universal app (spctl: Notarized Developer ID)

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

Showing 3 changed files with +140 and −1

added Resources/ZyquoAgent.entitlements +8 −0
@@ -0,0 +1,8 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 +<plist version="1.0">
4 +<dict>
5 + <key>com.apple.security.automation.apple-events</key>
6 + <true/>
7 +</dict>
8 +</plist>
modified docs/PLAN.md +26 −1
@@ -58,7 +58,32 @@
58 58
59 59 **Phase 7 checkpoint (2026-07-30):** 77/80 models green on tool calling (3 external failures documented); 11/11 scenarios and 8/8 safety tests pass. Two real bugs found and fixed by live testing: (1) AgentLoop recorded `completed` instead of `cancelled` when interrupted mid-stream; (2) harness sentinel `sleep 60` collided with an unrelated system daemon, making the cancellation leak-check unfalsifiable. Self-checks after all changes: build 0 warnings, policy 38/38, mock 0, ui-smoke pass.
60 60
61 ## Phase 8 — Signing & notarization (reuse zyquo-term identity)
61 +## Phase 8 — Signing & notarization (reuse zyquo-term identity) — ✅ COMPLETE
62 +- [x] `Resources/ZyquoAgent.entitlements`: Hardened Runtime ON, App Sandbox deliberately OFF (the app must spawn /bin/bash + /usr/bin/osascript), single entitlement `com.apple.security.automation.apple-events` paired with `NSAppleEventsUsageDescription`. NOTE: the file must stay pure ASCII with no XML comments — codesign's AMFI parser rejects both (hit live: "AMFIUnserializeXML: syntax error").
63 +- [x] `scripts/notarize.sh`: signs nested code inside-out, then the executable, then the bundle (`--options runtime --timestamp --entitlements`), verifies `--deep --strict`, ditto-zips, submits to notarytool `--wait`, staples, and **fails loudly unless spctl reports "Notarized Developer ID"**; optional signed+stapled DMG via a `dmg` argument.
64 +- [x] `make release` green end-to-end: universal binary (x86_64 + arm64 via lipo) → Info.plist → sign → notarize (submission ffe1330f-6832-46e3-9cba-ff6257d4e839, status **Accepted**) → staple → verify.
65 +- [x] Independent verification: `spctl -a -vv` = **accepted, source=Notarized Developer ID**, `stapler validate` OK, CodeDirectory `flags=0x10000(runtime)`, TeamIdentifier 3YM54G49SN, entitlements as intended, `lipo -archs` = x86_64 arm64, AppIcon.icns + menu-bar PNGs in the bundle.
66 +- [x] The shipped bundle's own binary passes `--verify-policy` 38/38 and `--run-mock`, and the app launches and renders the command center.
67 +
68 +**Phase 8 checkpoint (2026-07-30):** Notarized, stapled, Gatekeeper-accepted universal `Zyquo Agent.app` built entirely without the Xcode IDE. Final sweep: every code file carries the mandatory header, no secrets in any commit (`.secrets/` never tracked), dist artifacts gitignored.
69 +
70 +---
71 +
72 +## Definition of Done — status
73 +-`make release` → Developer ID-signed, notarized, stapled app (spctl-verified), no Xcode IDE
74 +- ✅ Full plan→act→observe→reflect loop on the same providers/models as Zyquo Cloud; 77/80 agent-capable models green on native tool calling (docs/VERIFICATION.md)
75 +- ✅ bash + AppleScript + workspace file tools with live streaming; every action through the policy gate; destructive/elevated always require approval; complete audit log
76 +- ✅ Memory compaction keeps long tasks in-window (proven live: 4 compactions, task still correct); workspaces isolate and track task files
77 +- ✅ Violet SVG icon → icns + menu-bar template, sibling of Cloud/Local
78 +- ✅ Violet light theme per spec + derived dark theme, quality gate passed
79 +- ✅ Naming coherent (`Zyquo Agent` / `com.zyquo.agent` / `ZyquoAgent`), keys in the reused AES-256-GCM vault (no Keychain)
80 +- ✅ Mandatory header on every code file (repo-wide sweep)
81 +- ✅ docs/: PLAN, AGENT-RESEARCH, PROVIDER-REUSE, VERIFICATION, EVALUATION
82 +
83 +### Known gaps (documented, not hidden)
84 +- `requireApprovalForAppleScript` and `workspaceEscapePolicy=deny` are persisted and surfaced in Settings but not yet enforced by dedicated engine hooks (the engine already asks for AppleScript in Manual/Guarded and always asks on workspace escape) — see `AgentSettingsStore` header.
85 +- `swift test` cannot run on a CommandLineTools-only toolchain (no XCTest module); the XCTest files exist for CI/Xcode, and `--verify-policy` / `--run-mock` / `--run-ui-smoke` are the executable equivalents.
86 +- 3 of 80 models fail tool calling for external reasons: DeepInfra's two Gemini re-hosts strip Google's required `thought_signature`, and `kimi-k3` is capacity-throttled upstream.
62 87
63 88 **Facts gathered from `~/Desktop/other/OTHER/zyquo-term` (2026-07-30):**
64 89 - Identity: `Developer ID Application: Simon-Pierre Boucher (3YM54G49SN)` (Team ID `3YM54G49SN`)
added scripts/notarize.sh +106 −0
@@ -0,0 +1,106 @@
1 +#!/bin/bash
2 +#
3 +# notarize.sh
4 +# Zyquo Agent
5 +#
6 +# Author: Simon-Pierre Boucher
7 +# Mail: contact@spboucher.ai
8 +#
9 +# Developer ID signing, notarization, stapling, and verification for
10 +# Zyquo Agent. Identity, Team ID, and the notarytool keychain profile are the
11 +# same ones the Zyquo family already uses (see ~/Desktop/other/OTHER/zyquo-term).
12 +#
13 +# Usage: notarize.sh <app-dir> <identity> <keychain-profile> <entitlements>
14 +# notarize.sh <app-dir> <identity> <keychain-profile> <entitlements> dmg
15 +#
16 +# Signs nested code first, then the bundle, with the Hardened Runtime and a
17 +# secure timestamp; submits a ditto zip to Apple, waits, staples the ticket,
18 +# and fails loudly unless `spctl` reports "Notarized Developer ID".
19 +#
20 +# Entitlements posture (Resources/ZyquoAgent.entitlements): Hardened Runtime
21 +# ON, App Sandbox deliberately OFF (Zyquo Agent must spawn /bin/bash and
22 +# /usr/bin/osascript to do its job, like Terminal.app or the sibling Zyquo
23 +# Term); the only declared entitlement is
24 +# com.apple.security.automation.apple-events, paired with
25 +# NSAppleEventsUsageDescription in Info.plist, for AppleScript automation.
26 +# Keep that file pure ASCII with no XML comments: the AMFI parser codesign
27 +# uses rejects both.
28 +#
29 +set -euo pipefail
30 +
31 +APP_DIR="${1:?app bundle path required}"
32 +IDENTITY="${2:?signing identity required}"
33 +KEYCHAIN_PROFILE="${3:?notarytool keychain profile required}"
34 +ENTITLEMENTS="${4:?entitlements plist required}"
35 +MAKE_DMG="${5:-}"
36 +
37 +APP_NAME="$(basename "$APP_DIR" .app)"
38 +DIST="$(dirname "$APP_DIR")"
39 +EXEC_NAME="ZyquoAgent"
40 +ZIP_PATH="$DIST/${EXEC_NAME}.zip"
41 +DMG_PATH="$DIST/${EXEC_NAME}.dmg"
42 +
43 +[ -d "$APP_DIR" ] || { echo "ERROR: $APP_DIR missing — run make release/bundle first" >&2; exit 1; }
44 +[ -f "$ENTITLEMENTS" ] || { echo "ERROR: $ENTITLEMENTS missing" >&2; exit 1; }
45 +
46 +# ---------------------------------------------------------------- signing --
47 +echo "=== Signing (Developer ID, hardened runtime) ==="
48 +# Nested code first (frameworks/bundles/helpers), then the main executable,
49 +# then the bundle itself — codesign requires inside-out order.
50 +while IFS= read -r -d '' nested; do
51 + echo " nested: $nested"
52 + codesign --force --options runtime --timestamp \
53 + --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" "$nested"
54 +done < <(find "$APP_DIR/Contents" \
55 + \( -name '*.framework' -o -name '*.dylib' -o -name '*.bundle' \) -print0 2>/dev/null)
56 +
57 +codesign --force --options runtime --timestamp \
58 + --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" \
59 + "$APP_DIR/Contents/MacOS/$EXEC_NAME"
60 +codesign --force --options runtime --timestamp \
61 + --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" "$APP_DIR"
62 +
63 +echo "=== Verifying signature ==="
64 +codesign --verify --deep --strict --verbose=2 "$APP_DIR"
65 +codesign --display --entitlements - --verbose=2 "$APP_DIR" 2>&1 | sed -n '1,12p'
66 +
67 +# ------------------------------------------------------------ notarizing --
68 +echo "=== Submitting to Apple notary service (profile: $KEYCHAIN_PROFILE) ==="
69 +rm -f "$ZIP_PATH"
70 +ditto -c -k --keepParent "$APP_DIR" "$ZIP_PATH"
71 +xcrun notarytool submit "$ZIP_PATH" --keychain-profile "$KEYCHAIN_PROFILE" --wait
72 +
73 +echo "=== Stapling ==="
74 +xcrun stapler staple "$APP_DIR"
75 +xcrun stapler validate "$APP_DIR"
76 +
77 +echo "=== Gatekeeper assessment ==="
78 +assessment="$(spctl -a -vv "$APP_DIR" 2>&1)"
79 +echo "$assessment"
80 +case "$assessment" in
81 + *"source=Notarized Developer ID"*)
82 + echo "✅ spctl: accepted, source=Notarized Developer ID" ;;
83 + *)
84 + echo "❌ spctl did not report a notarized Developer ID signature" >&2
85 + exit 1 ;;
86 +esac
87 +
88 +# Re-zip the stapled bundle so the distributed archive carries the ticket.
89 +rm -f "$ZIP_PATH"
90 +ditto -c -k --keepParent "$APP_DIR" "$ZIP_PATH"
91 +echo "Archive: $ZIP_PATH"
92 +
93 +# ------------------------------------------------------------- optional dmg --
94 +if [ "$MAKE_DMG" = "dmg" ]; then
95 + echo "=== Building signed + notarized DMG ==="
96 + rm -f "$DMG_PATH"
97 + hdiutil create -volname "$APP_NAME" -srcfolder "$APP_DIR" \
98 + -ov -format UDZO "$DMG_PATH"
99 + codesign --force --timestamp --sign "$IDENTITY" "$DMG_PATH"
100 + xcrun notarytool submit "$DMG_PATH" --keychain-profile "$KEYCHAIN_PROFILE" --wait
101 + xcrun stapler staple "$DMG_PATH"
102 + spctl -a -vv -t open --context context:primary-signature "$DMG_PATH" 2>&1 | tail -3
103 + echo "DMG: $DMG_PATH"
104 +fi
105 +
106 +echo "=== Done: $APP_DIR is signed, notarized, and stapled ==="
107