SPB Git

spb/zyquo-atlas Public License

The AI-native macOS web browser — every surface, intelligent.

Swift 75.2% JavaScript 22% Shell 2% Makefile 0.9%

phase8: Developer ID signing + notarization — notarize.sh (inside-out sign, notarytool --wait, staple, spctl), universal single-command build, Hardened Runtime entitlements; make release → Notarized Developer ID (verified). All 9 phases complete.

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

Showing 6 changed files with +117 and −29

modified Makefile +7 −15
@@ -59,28 +59,20 @@ bundle:
59 59 @for r in Resources/MenuBarIcon.png Resources/MenuBarIcon@2x.png; do [ -f "$$r" ] && cp "$$r" "$(APP_DIR)/Contents/Resources/" || true; done
60 60 scripts/write-info-plist.sh "$(APP_DIR)" "$(APP_NAME)" "$(EXEC_NAME)" "$(BUNDLE_ID)" "$(VERSION)" "$(BUILD_NUM)" "$(MIN_MACOS)"
61 61
62 # Both arch builds land in .build/out/Products/Release, so each slice is copied
63 # aside before lipo. The -U flag allows the swiftCompatibility56 force-load
64 # symbol (CLT ships it arm64-only; never referenced at runtime on macOS 13+).
62 +# SwiftPM builds a fat binary in one command (arm64 + x86_64); products land in
63 +# .build/apple/Products/Release. Verifies the slices with lipo.
64 +PRODUCTS := .build/apple/Products/Release
65 65 universal:
66 66 @echo "=== Building universal binary (arm64 + x86_64) ==="
67 mkdir -p .build/universal
68 swift build -c release --arch arm64
69 cp .build/out/Products/Release/$(EXEC_NAME) .build/universal/$(EXEC_NAME).arm64
70 swift build -c release --arch x86_64 -Xlinker -U -Xlinker '__swift_FORCE_LOAD_$$_swiftCompatibility56'
71 cp .build/out/Products/Release/$(EXEC_NAME) .build/universal/$(EXEC_NAME).x86_64
72 lipo -create \
73 .build/universal/$(EXEC_NAME).arm64 \
74 .build/universal/$(EXEC_NAME).x86_64 \
75 -output .build/universal/$(EXEC_NAME)
76 lipo -archs .build/universal/$(EXEC_NAME)
67 + swift build -c release --arch arm64 --arch x86_64
68 + lipo -archs "$(PRODUCTS)/$(EXEC_NAME)"
77 69
78 70 release: universal
79 71 @echo "=== Assembling $(APP_DIR) (universal) ==="
80 72 rm -rf "$(APP_DIR)"
81 73 mkdir -p "$(APP_DIR)/Contents/MacOS" "$(APP_DIR)/Contents/Resources"
82 cp .build/universal/$(EXEC_NAME) "$(APP_DIR)/Contents/MacOS/$(EXEC_NAME)"
83 @for b in .build/out/Products/Release/*.bundle; do [ -e "$$b" ] && cp -R "$$b" "$(APP_DIR)/Contents/Resources/" || true; done
74 + cp "$(PRODUCTS)/$(EXEC_NAME)" "$(APP_DIR)/Contents/MacOS/$(EXEC_NAME)"
75 + @for b in "$(PRODUCTS)"/*.bundle; do [ -e "$$b" ] && cp -R "$$b" "$(APP_DIR)/Contents/Resources/" || true; done
84 76 @if [ -f Resources/AppIcon.icns ]; then cp Resources/AppIcon.icns "$(APP_DIR)/Contents/Resources/AppIcon.icns"; fi
85 77 @for r in Resources/MenuBarIcon.png Resources/MenuBarIcon@2x.png; do [ -f "$$r" ] && cp "$$r" "$(APP_DIR)/Contents/Resources/" || true; done
86 78 scripts/write-info-plist.sh "$(APP_DIR)" "$(APP_NAME)" "$(EXEC_NAME)" "$(BUNDLE_ID)" "$(VERSION)" "$(BUILD_NUM)" "$(MIN_MACOS)"
modified README.md +9 −3
@@ -18,12 +18,18 @@ surface, powered by the same providers/models and encrypted key vault as
18 18 ```sh
19 19 make dev # release build → dist/Zyquo Atlas.app, ad-hoc signed (local iteration)
20 20 make run # make dev, then launch the app
21 make test # run the test suite (Command Line Tools only)
22 make release # universal, Developer ID signed, notarized, stapled (Phase 8)
23 make verify # Phase 7 API verification with real keys (source .env.keys)
21 +make test # run the test suite
22 +make release # universal (arm64+x86_64), Developer ID signed, notarized, stapled
23 +make icon # regenerate AppIcon.icns from assets/icon/zyquo-atlas.svg
24 +make verify # API verification with real keys (source .env.keys)
24 25 make clean
25 26 ```
26 27
28 +`make release` produces a **Notarized Developer ID** `Zyquo Atlas.app` (verified:
29 +`spctl -a -vv``source=Notarized Developer ID`), universal, built without the
30 +Xcode IDE. Signing identity, Team ID, and the `notarytool` keychain profile are
31 +reused from the Zyquo family pipeline; no secrets live in this repo.
32 +
27 33 ### Toolchain note
28 34
29 35 The Command Line Tools' default SDK (macOS 27 beta) declares SwiftUI's `@State`
modified Resources/ZyquoAtlas.entitlements +10 −7
@@ -7,16 +7,19 @@
7 7 Author: Simon-Pierre Boucher
8 8 Mail: contact@spboucher.ai
9 9
10 Phase 8 finalizes the Hardened Runtime posture. A WKWebView browser uses
11 WebKit XPC/child processes; the minimal set that lets WKWebView, downloads,
12 and the app's own HTTPS API calls work is chosen and validated in Phase 8.
13 Not App-Sandboxed in v1 (Developer ID distribution, not Mac App Store).
10 + Signed with the Hardened Runtime (Developer ID distribution). Zyquo Atlas is
11 + a WKWebView browser: WebKit runs page JavaScript in Apple's own out-of-process
12 + WebContent/Networking/GPU XPC services (WebKit.framework is a *system*
13 + framework, not embedded), so the host app needs no JIT or network
14 + entitlement for WKWebView, downloads, or its own HTTPS API calls to function.
15 + Not App-Sandboxed (like Safari-alternative browsers and terminals); a browser
16 + loads arbitrary content and manages its own data outside a container. This is
17 + the minimal posture that notarizes and runs — matching the Zyquo family
18 + pipeline (zyquo-term).
14 19 -->
15 20 <plist version="1.0">
16 21 <dict>
17 22 <key>com.apple.security.cs.allow-jit</key>
18 <true/>
19 <key>com.apple.security.network.client</key>
20 <true/>
23 + <false/>
21 24 </dict>
22 25 </plist>
modified docs/PLAN.md +29 −0
@@ -165,3 +165,32 @@ launchable, single window; browser core is Phase 2).
165 165 **Failures found & fixed:** first sweep 173/184 → three genuine failures fixed — two reasoning models (Together `Qwen3.5-9B`, DeepInfra `Kimi-K2.5`) were unflagged in the catalog and exhausted the token budget on hidden thinking → flagged `reasoning` + raised the reasoning token budget (harness 8000, AIService 8192, a real product fix so reasoning models reach their answer); one non-functional model (Together `google/gemma-4-31B-it`, streams empty) removed from the catalog (now **169 models**). Remaining failures were **transient provider-side network errors** (Cerebras/DeepInfra/Together 503/timeout during concurrent bursts) that all cleared on retry — recorded in `docs/VERIFICATION.md` in their re-verified green state.
166 166
167 167 **Final: 183 checks · 183 passed · 0 failed.** No keys or browsing data committed. **Phase 7 gate PASSED.**
168 +
169 +## Phase 8 — Signing & Notarization — COMPLETE (Notarized Developer ID)
170 +
171 +- [x] Inspected `zyquo-term` signing setup and reused it verbatim: identity `Developer ID Application: Simon-Pierre Boucher (3YM54G49SN)`, Team `3YM54G49SN`, notarytool keychain profile `MacLustr-Notarize` (both confirmed present/live on this machine)
172 +- [x] `Resources/ZyquoAtlas.entitlements` — Hardened Runtime posture; **no special entitlements needed** because WKWebView runs page JS in Apple's system WebContent XPC processes (WebKit.framework is not embedded); not App-Sandboxed (browser). Minimal, matches the family.
173 +- [x] `scripts/notarize.sh` — signs inside-out (nested SPM resource bundle, then the app) with `--options runtime --timestamp --entitlements`, `ditto -c -k --keepParent` zip, `notarytool submit --wait`, `stapler staple` + `validate`, `spctl` assessment
174 +- [x] Makefile `universal` fixed to SwiftPM single-command multi-arch (`swift build -c release --arch arm64 --arch x86_64` → fat binary in `.build/apple/Products/Release`; the old two-build+lipo produced two arm64 slices under the now-installed Xcode 26.6 toolchain)
175 +- [x] `make release` run end-to-end: **Apple notarization status: Accepted** (submission `2003be13-…`), stapled + validated
176 +
177 +**Verified independently:** `lipo -archs``x86_64 arm64`; `codesign -dvv``flags=0x10000(runtime)`, Authority `Developer ID Application …`, `TeamIdentifier=3YM54G49SN`; `stapler validate` → worked; **`spctl -a -vv``accepted, source=Notarized Developer ID`**. **Phase 8 gate PASSED.**
178 +
179 +Notes: `make dev` (ad-hoc) retained for iteration. Optional signed+stapled DMG is an easy follow-up (the app is already notarized+stapled). Vendored `Content/Readability*.js` (Mozilla, Apache-2.0, unmodified) keep Mozilla's copyright header rather than the Atlas author header — correct for license integrity; attribution in `Content/LICENSE-Readability.txt`.
180 +
181 +---
182 +
183 +## Definition of Done — final check (2026-07-30)
184 +
185 +- [x] `make release` → Developer ID–signed, **notarized, stapled** `Zyquo Atlas.app`, universal, built without the Xcode IDE (SPM/CLI); `spctl` = Notarized Developer ID
186 +- [x] Fast multi-tab WKWebView browser: profiles, private mode, favorites, full-text history, downloads, find, reader
187 +- [x] AI everywhere: omnibox ask, chat-with-page, selection actions, summarize/translate/rewrite, multi-tab compare — streaming, grounded, every provider/model (Phase 7 table green, 169 models)
188 +- [x] Theming/customization engine: themes, backgrounds, top/left tabs, density, custom start page — live, per profile
189 +- [x] Privacy model: page content leaves only on user action; keys in the reused AES-256-GCM vault (no Keychain)
190 +- [x] Teal-indigo "atlas" SVG icon → `.icns` + template glyph; sibling of Cloud/Local/Agent
191 +- [x] Light theme matches the Phase 4 spec; dark derived
192 +- [x] Naming coherent (`Zyquo Atlas`, `com.zyquo.atlas`, `ZyquoAtlas`); every authored code file carries the header (verified)
193 +- [x] `docs/PLAN.md` all phases complete; `AI-BROWSER-RESEARCH.md`, `PROVIDER-REUSE.md`, `VERIFICATION.md` complete
194 +- [~] Deferred enhancements documented in Phase 6 (tab groups/drag-reorder gesture, hover thumbnails, in-field writing-assist, auto/hover summaries, citation-chip rendering, menu-bar extra)
195 +
196 +**All 9 phases (0–8) complete. Zyquo Atlas builds, verifies green, and ships as a notarized Developer ID app.**
modified docs/VERIFICATION.md +4 −4
@@ -18,8 +18,8 @@ come from the environment and are never logged.
18 18
19 19 | Source | Page | Test | Result | Latency | Detail |
20 20 |---|---|---|---|---|---|
21 | — | `article` | extract | ✅ | 2.3s | quality=reader, words=9210, ~17278tok, headings=27 |
21 +| — | `article` | extract | ✅ | 1.9s | quality=reader, words=9210, ~17278tok, headings=27 |
22 22 | — | `docs` | extract | ✅ | 1.7s | quality=reader, words=83, ~190tok, headings=0 |
23 | — | `js-app` | extract | ✅ | 1.9s | quality=reader, words=1174, ~2830tok, headings=23 |
24 | — | `long-article` | extract | ✅ | 1.9s | quality=reader, words=28685, ~49595tok, headings=40 |
25 | — | `selection` | extract | ✅ | 2.4s | selection=In cartography, a map is a two-dimension, words=18106 |
23 +| — | `js-app` | extract | ✅ | 1.8s | quality=reader, words=1174, ~2830tok, headings=23 |
24 +| — | `long-article` | extract | ✅ | 1.8s | quality=reader, words=28685, ~49595tok, headings=40 |
25 +| — | `selection` | extract | ✅ | 2.1s | selection=In cartography, a map is a two-dimension, words=18106 |
added scripts/notarize.sh +58 −0
@@ -0,0 +1,58 @@
1 +#!/bin/bash
2 +#
3 +# notarize.sh
4 +# Zyquo Atlas
5 +#
6 +# Author: Simon-Pierre Boucher
7 +# Mail: contact@spboucher.ai
8 +#
9 +# Developer ID signing + notarization + stapling for an assembled
10 +# Zyquo Atlas.app (Phase 8). Identity, Team ID, and the notarytool keychain
11 +# profile are reused from the Zyquo family pipeline (zyquo-term).
12 +#
13 +# Usage: notarize.sh <app-dir> <identity> <notary-profile> <entitlements>
14 +# Signs inside-out (nested resource bundles, then the app) with the Hardened
15 +# Runtime, zips with ditto, submits with notarytool --wait, staples, and
16 +# verifies spctl == "Notarized Developer ID". Secrets are never printed.
17 +#
18 +set -euo pipefail
19 +cd "$(dirname "$0")/.."
20 +
21 +APP_DIR="$1"
22 +IDENTITY="$2"
23 +KEYCHAIN_PROFILE="$3"
24 +ENTITLEMENTS="$4"
25 +ZIP_PATH="${APP_DIR%.app}.zip"
26 +
27 +[ -d "$APP_DIR" ] || { echo "ERROR: $APP_DIR missing — run 'make release' (assembles the bundle first)" >&2; exit 1; }
28 +
29 +echo "=== Signing (Developer ID, Hardened Runtime) ==="
30 +# Sign nested code first (SPM resource bundles carrying the Content/*.js and
31 +# icon), inside-out, so the outer signature stays valid.
32 +find "$APP_DIR/Contents" -name "*.bundle" -maxdepth 3 -print0 2>/dev/null \
33 + | while IFS= read -r -d '' bundle; do
34 + echo " nested: $(basename "$bundle")"
35 + codesign --force --options runtime --timestamp \
36 + --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" "$bundle"
37 + done
38 +# Sign the app bundle (also signs Contents/MacOS/ZyquoAtlas).
39 +codesign --force --options runtime --timestamp \
40 + --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" "$APP_DIR"
41 +codesign --verify --deep --strict --verbose=2 "$APP_DIR"
42 +echo "Signature valid."
43 +
44 +echo "=== Packaging for notarization (ditto) ==="
45 +rm -f "$ZIP_PATH"
46 +ditto -c -k --keepParent "$APP_DIR" "$ZIP_PATH"
47 +
48 +echo "=== Submitting to notarytool (profile: $KEYCHAIN_PROFILE) ==="
49 +xcrun notarytool submit "$ZIP_PATH" --keychain-profile "$KEYCHAIN_PROFILE" --wait
50 +
51 +echo "=== Stapling ==="
52 +xcrun stapler staple "$APP_DIR"
53 +xcrun stapler validate "$APP_DIR"
54 +
55 +echo "=== Gatekeeper assessment ==="
56 +spctl -a -vvv --type execute "$APP_DIR"
57 +
58 +echo "Done: $APP_DIR is Developer ID–signed, notarized, and stapled."
59