build(macos): use a distinct bundle id for local packaging

Local 'pnpm build' carried the release appId com.stablyai.orca under a
different (ad-hoc/local) signature; macOS TCC keys grants to bundle id +
signature, so local builds collided with and corrupted the installed
release's grant identity. Non-release packaging now uses
com.stablyai.orca.local, gated on ORCA_MAC_RELEASE — the same env that
enables release signing — so release artifacts are byte-identical.
Knock-ons covered: sidecar peer-trust allowlist, notification-status
sidecar bundle id, and the notification settings deep-link now trusts
the launchd-provided id.

Refs #9756
This commit is contained in:
Brennan Benson
2026-07-22 12:21:48 -07:00
parent dfbc2e8ba7
commit 2a1c2b0af1
5 changed files with 57 additions and 4 deletions
+6
View File
@@ -225,6 +225,12 @@ module.exports = {
include: resolve(__dirname, 'nsis', 'daemon-host-uninstall.nsh')
},
mac: {
// Why: macOS TCC keys permission grants to bundle id + code signature. A
// local build carrying the release appId under a different (ad-hoc/local)
// signature collides with the installed release's grant identity (#9756),
// so non-release packaging gets its own id. Gated on the same env that
// enables release signing/notarization, so release artifacts are untouched.
...(isMacRelease ? {} : { appId: 'com.stablyai.orca.local' }),
icon: 'resources/build/icon.icns',
entitlements: 'resources/build/entitlements.mac.plist',
entitlementsInherit: 'resources/build/entitlements.mac.plist',
@@ -3603,9 +3603,11 @@ private func isTrustedOrcaApplication(_ pid: pid_t) -> Bool {
else {
return false
}
// Why: dev validation runs from per-worktree wrapper apps with stable
// Orca-owned bundle ids; the sidecar peer check must still authorize them.
// Why: dev validation runs from per-worktree wrapper apps and local
// packaging uses its own bundle id (#9756); the sidecar peer check must
// still authorize these Orca-owned identities.
return bundleId == "com.stablyai.orca" ||
bundleId == "com.stablyai.orca.local" ||
bundleId.hasPrefix("com.stablyai.orca.dev.") ||
bundleId == "com.github.Electron"
}
+1 -1
View File
@@ -74,7 +74,7 @@
"build:unpack": "pnpm run build && pnpm run ensure:electron-runtime && electron-builder --config config/electron-builder.config.cjs --dir",
"build:win": "pnpm run build:desktop && pnpm run ensure:electron-runtime && electron-builder --config config/electron-builder.config.cjs --win",
"build:icons": "bash resources/icon-source/generate.sh",
"build:mac": "pnpm run build:desktop && pnpm run build:computer-macos && pnpm run build:notification-status-macos && pnpm run ensure:electron-runtime && electron-builder --config config/electron-builder.config.cjs --mac",
"build:mac": "pnpm run build:desktop && pnpm run build:computer-macos && pnpm run build:notification-status-macos --bundle-id com.stablyai.orca.local && pnpm run ensure:electron-runtime && electron-builder --config config/electron-builder.config.cjs --mac",
"build:mac:release": "node config/scripts/verify-macos-release-env.mjs && ORCA_MAC_RELEASE=1 pnpm run build:desktop && ORCA_MAC_RELEASE=1 pnpm run build:computer-macos && ORCA_MAC_RELEASE=1 pnpm run build:notification-status-macos && pnpm run ensure:electron-runtime && ORCA_MAC_RELEASE=1 electron-builder --config config/electron-builder.config.cjs --mac",
"build:linux": "pnpm run build:desktop && pnpm run ensure:electron-runtime && electron-builder --config config/electron-builder.config.cjs --linux AppImage deb",
"test:e2e": "pnpm run ensure:electron-runtime && npx playwright test --config tests/playwright.config.ts --project=electron-headless",
+40
View File
@@ -238,6 +238,46 @@ describe('registerNotificationHandlers', () => {
}
})
it('falls back to the launchd bundle id for packaged local builds', async () => {
const originalPlatform = process.platform
const originalDevBundleId = process.env.ORCA_DEV_MACOS_BUNDLE_ID
const originalLaunchdBundleId = process.env.__CFBundleIdentifier
Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true })
delete process.env.ORCA_DEV_MACOS_BUNDLE_ID
process.env.__CFBundleIdentifier = 'com.stablyai.orca.local'
try {
registerNotificationHandlers({
getSettings: () => ({
notifications: {
enabled: true,
agentTaskComplete: true,
terminalBell: true,
suppressWhenFocused: true
}
})
} as never)
const handler = getOpenSystemSettingsHandler()
handler({})
expect(shellOpenExternalMock).toHaveBeenCalledWith(
'x-apple.systempreferences:com.apple.Notifications-Settings.extension?id=com.stablyai.orca.local'
)
} finally {
Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true })
if (originalDevBundleId === undefined) {
delete process.env.ORCA_DEV_MACOS_BUNDLE_ID
} else {
process.env.ORCA_DEV_MACOS_BUNDLE_ID = originalDevBundleId
}
if (originalLaunchdBundleId === undefined) {
delete process.env.__CFBundleIdentifier
} else {
process.env.__CFBundleIdentifier = originalLaunchdBundleId
}
}
})
it('opens Windows notification settings', async () => {
const originalPlatform = process.platform
Object.defineProperty(process, 'platform', { value: 'win32', configurable: true })
+6 -1
View File
@@ -187,7 +187,12 @@ function probeNotificationDelivery(): Promise<NotificationDeliveryProbeResult> {
}
function getMacNotificationSettingsUrl(): string {
const bundleId = process.env.ORCA_DEV_MACOS_BUNDLE_ID ?? MACOS_PACKAGED_BUNDLE_ID
// Why: local packaging uses com.stablyai.orca.local (#9756), so trust the
// launchd-provided id before falling back to the release id.
const bundleId =
process.env.ORCA_DEV_MACOS_BUNDLE_ID ??
process.env.__CFBundleIdentifier ??
MACOS_PACKAGED_BUNDLE_ID
return `${MACOS_NOTIFICATION_SETTINGS_URL}?id=${encodeURIComponent(bundleId)}`
}