Notifio is a desktop app that watches rental search pages and tells you when a new listing appears. It is Electron, it ships as two Mac DMGs and a Windows installer from the download page, and the entire Mac half of its build configuration is about forty lines of package.json.
I wanted to know what is actually in the bundle those forty lines produce. Not the 278 MB of binaries, which I measured last week, but the metadata: the Info.plist that macOS reads to decide what this application is called, what it is allowed to do, and who owns it.
So I diffed it against the one it came from. Electron ships a complete Info.plist inside node_modules, in the stock Electron.app that every packaged app is a copy of:
plutil -convert json -o /tmp/stock.json \ node_modules/.pnpm/electron@43.1.1/node_modules/electron/dist/Electron.app/Contents/Info.plist plutil -convert json -o /tmp/ship.json \ release/mac-arm64/Notifio.app/Contents/Info.plist stock Electron 43.1.1 31 keys shipped Notifio 1.1.0 32 keys added by the build 1 changed by the build 10 byte for byte identical 21 Two thirds of what macOS knows about our application was written by Electron and shipped through us without being read.
The ten that changed, and the five lines that moved nine of them
key stock shipped CFBundleIdentifier com.github.Electron app.notifio CFBundleName Electron Notifio CFBundleDisplayName Electron Notifio CFBundleExecutable Electron Notifio CFBundleIconFile electron.icns icon.icns CFBundleShortVersionString 43.1.1 1.1.0 CFBundleVersion 43.1.1 1.1.0 LSApplicationCategoryType developer-tools utilities ElectronAsarIntegrity default_app.asar app.asar NSAppTransportSecurity (see below) (see below) Nine of those ten come from five values we actually typed:
"build": { "appId": "app.notifio", "productName": "Notifio", "directories": { "buildResources": "assets" }, "mac": { "category": "public.app-category.utilities" } } plus version at the top of the same file. appId becomes the bundle identifier, productName becomes three separate keys, the icon comes from the build resources directory by filename convention, category becomes the Launch Services category, and version becomes both version strings. That is the whole of our authored identity: five inputs, nine outputs, and nothing in the repo that names any of those keys directly.
The tenth, ElectronAsarIntegrity, is a SHA-256 of the archive holding our compiled code. Electron put the hash of its own default_app.asar there; electron-builder recomputed it for ours. It is the only hash anywhere in the bundle, and it is interesting enough to be its own post.
The 21 that nobody touched
CFBundleInfoDictionaryVersion NSAudioCaptureUsageDescription CFBundlePackageType NSBluetoothAlwaysUsageDescription DTCompiler NSBluetoothPeripheralUsageDescription DTSDKBuild NSCameraUsageDescription DTSDKName NSMicrophoneUsageDescription DTXcode NSHighResolutionCapable DTXcodeBuild NSMainNibFile LSEnvironment NSPrefersDisplaySafeAreaCompatibilityMode LSMinimumSystemVersion NSPrincipalClass NSRequiresAquaSystemAppearance NSQuitAlwaysKeepsWindows NSSupportsAutomaticGraphicsSwitching Most of that list is Electron being Electron. NSPrincipalClass is AtomApplication, a name the project stopped using in 2016. The five DT* keys record the Xcode and SDK build that compiled the Electron binary on someone else's machine, which is the honest answer to "what toolchain built this": not ours. LSEnvironment sets MallocNanoZone to 0, a workaround for a Chromium allocator crash. LSMinimumSystemVersion is 12.0, which is also the macOS version our download page advertises, and that agreement is luck rather than engineering: nothing in our build reads it.
Then there are these four, verbatim, in a rental alerts app:
NSCameraUsageDescription This app needs access to the camera NSMicrophoneUsageDescription This app needs access to the microphone NSAudioCaptureUsageDescription This app needs access to audio capture NSBluetoothAlwaysUsageDescription This app needs access to Bluetooth grep -rin "getUserMedia|camera|bluetooth|microphone|mediaDevices" src/ # no matches Nothing in Notifio has ever asked for any of those four. There is no call anywhere in the product that could trigger the prompt. Electron ships the strings because Electron is a framework and any app built on it might want them, and electron-builder passes them through because deleting a key it did not add is not its job.
These are not decoration. A UsageDescription string is exactly what macOS puts in the permission dialog, in quotes, under your app's name. If some dependency three levels down ever reached for the microphone, the user would get a system prompt attributed to Notifio whose entire justification reads "This app needs access to the microphone". That sentence is not our copy, it was never reviewed by anyone here, and it is sitting in a shipped artefact that 300 MB of users have on disk.
The fix is one line per key in mac.extendInfo, setting each to null so electron-builder strips it. I have not shipped it yet, which is the honest state of this post: I found four claims in our bundle that we did not write and cannot keep, and the next build will drop them.
The key that turns off transport security
stock: NSAllowsArbitraryLoads: true shipped: NSAllowsArbitraryLoads: true NSAllowsLocalNetworking: true NSExceptionDomains: 127.0.0.1 -> insecure HTTP allowed, min TLS 1.0, forward secrecy not required localhost -> insecure HTTP allowed, min TLS 1.0, forward secrecy not required Electron disables App Transport Security wholesale, because a browser engine cannot promise that every URL a page loads is HTTPS. electron-builder then adds the two localhost exceptions and the local networking flag on top, which is a careful statement about loopback traffic layered over a key that has already allowed everything.
We do need the loopback part. Notifio's window is a React app served over HTTP from a server running inside the same process, which is why 127.0.0.1 is the only authentication on its routes. What we do not need is NSAllowsArbitraryLoads, and the two keys together mean the narrow exception is doing no work at all. It is a reasonable framework default and a bad application default, and the only reason it is not worth fixing today is that the parts of the app that talk to the network at all talk to one HTTPS origin of ours and to the rental sites you point it at.
The one key electron-builder wrote by itself
NSHumanReadableCopyright Copyright © 2026 Notifio Not in stock Electron. Not in our config either. electron-builder generates it from author, and author in that package.json is an empty string, so it fell back to productName and produced a copyright notice assigned to a product rather than to a person or a company.
Meanwhile the site is specific. Our terms name CogniPrep Ltd, registered in England and Wales, company number 17407266, and the about page says the same thing. So the copyright line a Mac user can read in Finder's Get Info panel is the one place in the product that attributes Notifio to nobody at all, and it got there because a field was left blank.
That is the pattern in all three findings. A packaged Electron app is mostly someone else's defaults, and the defaults are not neutral: they make claims about hardware you do not use, transport security you did not loosen, and ownership you did not assign. None of it appears in your source, all of it appears in your artefact, and the only way to see it is to open the bundle and diff it against the thing it was copied from.
plutil -p release/mac-arm64/Notifio.app/Contents/Info.plist Thirty seconds, once per release. Worth it for the four sentences about a camera alone.

