macOS-DMG-Cutover → native App (v2.0.0) — Design¶
Datum: 2026-06-15
Scope: Distributions-/Packaging-Wechsel: die native SwiftUI-App (apple/PhysarumApp, macOS-Target) wird die offizielle macOS-DMG und löst den WKWebView-Wrapper (macos/Physarum.swift) ab. App-Code bleibt fast unberührt (neu: eine macOS-Entitlements-Datei; Version-Bump). Engine unberührt. Web-PWA (Codeberg Pages) unangetastet.
Status: Approved (Brainstorm 2026-06-15)
Problem / Kontext¶
Die ausgelieferte macOS-DMG ist der WKWebView-Wrapper (macos/Physarum.swift + gebündelte Web-App, gebaut von scripts/build-macos-app.sh) — sie kann nur, was die Web-App kann. Die gesamte native Politur (Authoring-Editor, nativer Audio-Stack, Capture/Export, iPad/Mac-Workspace) steckt in der nativen App unter apple/ und ist nie in der DMG gelandet. Das Cockpit führt den „Cutover" als Plan („native App löst die WKWebView-DMG bei Editor-Reife ab"); Editor/Audio/Capture sind nativ portiert → Editor-Reife erreicht.
Verifizierter Ausgangsstand (2026-06-15):
- Die native App ist ein Multiplatform-Target (project.yml supportedDestinations: [macOS, iOS, tvOS], macOS-Deployment 14.0) und baut/läuft heute auf macOS (xcodebuild -scheme Physarum -destination 'platform=macOS' build → BUILD SUCCEEDED, diese Session mehrfach).
- macOS-Signing in project.yml ist ad-hoc (CODE_SIGN_IDENTITY[sdk=macosx*]: "-"), App ist unsandboxed, kein Hardened Runtime gesetzt, keine *.entitlements-Datei vorhanden.
- Mic-/Photos-Usage-Strings sind da (NSMicrophoneUsageDescription, NSPhotoLibraryAddUsageDescription).
- Developer-ID-Cert vorhanden: Developer ID Application: Johannes Kaindl (U9X7M39R56) im Keychain → PHYSARUM_SIGN_IDENTITY=auto.
- project.yml MARKETING_VERSION: "0.1.0" (der DMG-relevante CFBundleShortVersionString) ist nicht mit package.json (1.5.0) synchron.
- Native Bundle-ID ist bereits de.jkaindl.physarum; der Wrapper nutzte lab.physarum.app.
- Der Kaleidoskop-Aspect-Fix (381334d) ist über die geteilte WGSL→Metal-Transpilation bereits nativ enthalten.
Entscheidungen (aus dem Brainstorm)¶
- Bundle-ID
de.jkaindl.physarumüberall — bereits gesetzt, keine Änderung;lab.physarum.appentfällt mit dem Wrapper. Bewusst kein In-place-Update für Alt-DMG-Nutzer (winzige Nutzerbasis, Konsistenz > Continuity). - Version
2.0.0— SemVer-Major signalisiert den Architektur-Bruch (native App statt Wrapper). Eine gemeinsame Versionslinie (Web-PWA + DMG); in beiden Quellen gesetzt. - Release-Build via Script +
xcodebuild-Overrides +ExportOptions.plist(Developer-ID); Dev-Builds bleiben ad-hoc.scripts/build-macos-app.shwird repurposed (nicht zweites Script). - WKWebView-Wrapper wird gelöscht (nicht nur ungenutzt gelassen).
- Web-PWA unangetastet — der Cutover betrifft nur die DMG.
Architektur¶
① Packaging-Pipeline — scripts/build-macos-app.sh (neu verdrahtet)¶
Ersetzt den swiftc-Wrapper-/Web-Embed-/Icon-SVG-Pfad durch den nativen Archive→Export→Notarize→DMG-Fluss:
cd apple/PhysarumApp && xcodegen generatexcodebuild archive -scheme Physarum -destination 'generic/platform=macOS' -archivePath <build>/Physarum.xcarchivemit Release-Signing-Overrides:CODE_SIGN_STYLE=Manual,CODE_SIGN_IDENTITY="$SIGN_IDENTITY",DEVELOPMENT_TEAM=U9X7M39R56,ENABLE_HARDENED_RUNTIME=YES,CODE_SIGN_ENTITLEMENTS=<…>/Physarum.macOS.entitlements,CODE_SIGNING_REQUIRED=YES. Universal:ARCHS="arm64 x86_64"+ONLY_ACTIVE_ARCH=NO— matcht die Reichweite des alten Wrappers (macOS 14 läuft noch auf Intel); am Build verifizieren, dassPhysarumEnginefür beide Slices baut.xcodebuild -exportArchive -archivePath … -exportOptionsPlist scripts/ExportOptions.plist -exportPath <out>mitmethod: developer-id,teamID: U9X7M39R56.- notarisieren + stapeln der
.app(ditto -c -k→xcrun notarytool submit -p "$NOTARY_PROFILE" --wait→xcrun stapler staple). - DMG zusammenbauen (
.app+/Applications-Symlink +macos/INSTALL.txt,hdiutil create … UDZO) — wiederverwendet die bewährte Plumbing aus den alten Schritten 8–9. - DMG signieren + notarisieren + stapeln, dann
xcrun stapler validate+spctl -a -vvv -t open/-t exec.
Env-gated wie bisher: PHYSARUM_SIGN_IDENTITY (auto nimmt die Developer-ID-Application-Identity), PHYSARUM_NOTARY_PROFILE. Beide unset → ad-hoc (Dev). Output: dist-macos/Physarum.app + dist-macos/Physarum.dmg.
Neue Datei scripts/ExportOptions.plist (committet): method=developer-id, teamID=U9X7M39R56, signingStyle=manual.
② Signing & Entitlements¶
Neue Datei apple/PhysarumApp/Resources/Physarum.macOS.entitlements:
- unsandboxed (KEIN com.apple.security.app-sandbox).
- com.apple.security.device.audio-input = true — Mic-Zugriff unter Hardened Runtime (Audio-Reaktivität).
Hardened Runtime wird per Signing-Flag aktiviert (ENABLE_HARDENED_RUNTIME=YES), nicht per Entitlement. Photos-Add läuft bei der unsandboxed Developer-ID-App über die vorhandene NSPhotoLibraryAddUsageDescription (TCC-Grant, kein Entitlement) — am notarisierten Build zu verifizieren (Capture→Save-to-Photos auf echtem Mac). Wichtig: die Release-Signing-Settings (CODE_SIGN_IDENTITY, CODE_SIGN_STYLE=Manual, ENABLE_HARDENED_RUNTIME=YES, CODE_SIGN_ENTITLEMENTS, DEVELOPMENT_TEAM) werden als xcodebuild-CLI-Overrides im Script gesetzt — project.yml-macOS-Signing bleibt unverändert ad-hoc, damit lokale Dev-Builds unberührt bleiben (kein Cert-Zwang, kein versehentliches Signieren).
③ Version¶
2.0.0 in beiden Quellen: package.json und project.yml MARKETING_VERSION. Der Build trägt MARKETING_VERSION als CFBundleShortVersionString; CURRENT_PROJECT_VERSION/Build-No bleibt wie gehabt. (Adressiert AGENTS-Punkt PROF-NAT-02 minimal — voller Sync-Script ist Folgearbeit.)
④ Bundle-ID¶
de.jkaindl.physarum (unverändert). lab.physarum.app entfällt.
⑤ Wrapper-Retirement¶
Entfernt: macos/Physarum.swift (WKWebView) + die Wrapper-spezifischen Build-Schritte (swiftc-Compile, Web-dist-Embed, Icon-SVG→icns) im Script. Bleibt: macos/INSTALL.txt (im native DMG). Native App liefert ihr AppIcon aus Assets.xcassets. assets/icon-macos.svg BLEIBT — es wird von apple/scripts/build-viewer-app.sh (Ambiance-Viewer) genutzt; nur seine Verwendung im repurposed build-macos-app.sh entfällt.
⑥ Web-PWA unangetastet¶
Codeberg-Pages-Deploy (PWA) läuft unverändert; der Kaleidoskop-Fix geht beim nächsten Web-Build mit. Kein src/-Eingriff durch den Cutover.
⑦ Docs & Gatekeeper-Notice¶
docs/MACOS-APP.mdumschreiben: native App statt Wrapper (Build-Befehl, Signing/Notarize, Feature-Set).- README-DMG-Sektion +
CHANGELOG [2.0.0](Cutover beschreiben: native macOS-App mit Editor/Audio/Capture löst den Web-Wrapper ab). - DMG-Gatekeeper-Notice an allen 4 Stellen (README, MACOS-APP.md, INSTALL.txt, Codeberg-Release-Body) synchron halten (AGENTS-Pflicht). Notarisiert+stapled ⇒ Gatekeeper akzeptiert i.d.R. ohne Workaround; die „Trotzdem öffnen"-Anleitung bleibt als Fallback.
⑧ Verifikation & Geräte-Sign-off¶
- Script-Gates:
stapler validate= valid,spctl -a -t exec= accepted (sonst nicht ausliefern). - Build muss grün bleiben:
xcodebuild -scheme Physarum -destination 'platform=macOS' build+ bestehende App-Tests. - Geräte-Sign-off (Johannes): notarisierte DMG auf einem (idealerweise frischen) Mac öffnen → startet ohne Gatekeeper-Block; Editor/Audio (Mic-Permission)/Capture (Save-to-Photos/Export) laufen.
⑨ Creds-Handoff (gehören Johannes)¶
PHYSARUM_NOTARY_PROFILE— notarytool-Keychain-Profilname (nicht non-interaktiv ermittelbar).- Codeberg-Release — API-Token (
write:release) als Env-Var, oder Johannes publisht den Release; CC liefert DMG + Release-Body fertig zu.
Release-DoD (Cockpit-Politik)¶
Version-Bump (2.0.0, beide Quellen) → CHANGELOG [2.0.0] → notarisierte native DMG → Tag v2.0.0 → Codeberg-Release mit DMG → 4 Notice-Stellen synchron. Tag/Publish sind die outward-facing Schritte (Creds-gated).
Non-Goals¶
- Kein nativer iOS/tvOS-Release, kein App Store (Developer-ID-Direktvertrieb).
- Kein In-place-Update für Alt-
lab.physarum.app-Nutzer (bewusster Bruch). - Keine Engine-/Shader-/Param-Layout-Änderung; kein
src/-Eingriff (Web-PWA unverändert). - Kein Sandboxing (Direktvertrieb, unsandboxed wie der Wrapper).
Offene Punkte¶
- Notary-Profil-Name + Codeberg-Token (Creds, s. ⑨).
- Photos-Add unter Hardened Runtime ohne Entitlement — am notarisierten Build verifizieren; falls nötig, Entitlement nachrüsten.
- Universal-Build (arm64+x86_64) am Build verifizieren (
lipo -archsauf dem Binary; entschieden in ①). - Frischer-Mac-Sign-off (Gatekeeper-Verhalten der notarisierten DMG).