An app with no networking code: what that means and how to check
· 7 min read
“No networking code” means the app’s binary contains nothing that can open a connection: no URL loading, no sockets, no networking framework linked in. That is a stronger claim than “we do not collect data,” because a promise can change in an update or be broken by a bug, while an app with no networking code cannot phone home even if it wanted to. You can check the claim yourself on any Mac app with a couple of Terminal commands, and here is how, along with what those commands cannot prove.
Three levels of “this app does not send data”
App privacy claims come in three strengths, and they are worth telling apart.
- Policy. “We do not collect personal data.” A statement about intent. The app can make network calls; the developer says the calls are harmless. You verify it by trusting them, or by watching the traffic (covered in how-to-check-what-an-app-sends-over-the-network-on-a-mac.html).
- Sandbox without network entitlement. The app runs in the macOS App Sandbox and has not declared the network client entitlement. The system blocks outgoing connections. Strong, but only meaningful if the app is actually sandboxed, and many apps outside the App Store are not.
- No networking code. The compiled app contains no networking symbols at all. There is nothing to block. A future update could add some, but the current binary cannot connect to anything, and you can inspect it.
An app at level three is in a category of its own: the question “what does it send” has no answer because there is no sending.
What “networking code” looks like in a Mac binary
Mac apps talk to the internet through a handful of system pieces, and each leaves a fingerprint in the binary as a symbol it imports:
- URL loading:
NSURLSession,NSURLConnection,CFURLRequest,CFURLConnection. This is how nearly every app fetches a web address. - The Network framework: symbols beginning
nw_such asnw_connection_create. - BSD sockets:
socket,connect,getaddrinfo. Lower level, used by some libraries. - Linked frameworks:
CFNetwork.frameworkorNetwork.frameworklisted as a dependency.
An app with no networking code has none of these. An app that “only checks for updates” has at least the first.
How to check an app yourself
You need Terminal and the developer command-line tools, which macOS offers to install the first time you run one of these commands. Replace the path with the app you are checking. The executable is inside the bundle at Contents/MacOS and usually has the app’s name.
- List the frameworks the app links against.
otool -L /Applications/Example.app/Contents/MacOS/Example
Look for CFNetwork or Network in the output. Absence here is not proof (Foundation can reach CFNetwork indirectly), but presence is a clear sign the app was built to connect.
- Scan the imported symbols for URL loading.
nm -um /Applications/Example.app/Contents/MacOS/Example | grep -iE "NSURLSession|CFURLRequest|CFURLConnection"
The -u flag lists undefined symbols, which are the ones the app imports from the system. If this prints nothing, the app does not use the standard URL loading classes. If it prints lines, the app can fetch web addresses.
- Scan for the lower-level routes.
nm -um /Applications/Example.app/Contents/MacOS/Example | grep -iE "nw_connection|_connect$|getaddrinfo"
Same idea, for the Network framework and raw sockets.
- Look for web addresses baked into the binary.
strings /Applications/Example.app/Contents/MacOS/Example | grep -iE "^https?://"
Analytics endpoints, update servers and license servers usually appear here as literal URLs.
- Check the sandbox and entitlements.
codesign -d --entitlements - --xml /Applications/Example.app
Look for com.apple.security.app-sandbox (is it sandboxed at all?) and com.apple.security.network.client (may it connect out?). A sandboxed app without the client entitlement is blocked by the system regardless of its code.
- Check bundled helpers too. Look inside Contents/Frameworks, Contents/PlugIns and Contents/Library/LoginItems, and run the same scans on anything there. Networking is often in a bundled analytics framework rather than the main executable.
If all five scans come back empty on the main executable and every bundled binary, the app cannot connect with its own code.
What these checks cannot prove
Be honest about the limits, because a checklist that overstates itself is worse than none.
- Subprocesses. An app can launch the system’s
curlor a shell script. Scanning the binary would not show it. Look in Contents for scripts, and watch Activity Monitor’s Network tab while the app runs. - Opening a URL in your browser. An app that calls the system “open this link” function is not networking; your browser does the fetching. That is fine and common (a Help menu, a Buy button).
- Future updates. A binary with no networking today can gain it tomorrow. The check is per version. Apps that commit to the property as a rule are rare, and worth noticing.
- Obfuscation. A determined developer could hide symbol use. This is unusual in legitimate Mac software and not what you are guarding against; you are guarding against the ordinary case of an analytics library nobody mentioned.
For an everyday app the scans above settle the question for the ordinary case, and the ordinary case is what matters.
Where Punchcard stands
Punchcard is built to sit at level three, and it enforces it mechanically. The release build runs exactly the symbol scan in step 2 on the compiled app and fails if NSURLSession, CFURLRequest or CFURLConnection appears. There is no update checker, no license server call, no crash reporter, no analytics, because any one of those would trip the gate and the build would not ship.
What that means for the data it holds: the day’s app names and minutes live in one SQLite file at ~/Library/Application Support/Punchcard/punchcard.sqlite, and the only ways out are the ones you trigger (the PNG export, copy as text, CSV export). The license key arrives by email and is entered in the app; nothing about it travels anywhere, because there is no code that could carry it. You can run the commands above on Punchcard and see the empty result for yourself; that is the intended experience.
It also means there are things Punchcard cannot offer: no sync between Macs (two Macs each keep their own roll), no web dashboard, no team features, no automatic updates inside the app. Those are the price of a binary that cannot connect, and the trade is stated plainly rather than hidden. There is more on why this matters for a tracker in time-tracking-that-needs-no-account-why-that-matters.html.
A quick way to sort your existing apps
You do not have to scan everything. Pick the apps that hold sensitive permissions (Accessibility, Screen Recording, Full Disk Access) or that run at login, and scan those. An app that can read your screen and can also connect out is the combination that deserves scrutiny. An app that can do one but not the other is far less of a concern.
- Open System Settings, Privacy & Security, and note the apps under Accessibility and Screen & System Audio Recording.
- For each, run steps 1 and 2 above.
- Any app that both reads other apps and imports URL loading goes on a list to watch in the Network tab of Activity Monitor during a normal day.
Ten minutes, and you know which of your always-on apps could be talking about what they see.
Questions
If an app has no networking code, how does it get updates?
Outside the app. You download a new version from the developer’s site, or a package manager does. The app itself cannot check. This is an inconvenience, and it is the honest cost of the property.
Does the macOS firewall stop apps from sending data?
The built-in firewall controls incoming connections only. Outgoing connections from apps are not blocked by it. To block outbound traffic per app you need a third-party outbound firewall, or an app that has no outbound code to begin with.
The nm command prints “no symbols” or an error. What now?
Some binaries are stripped or are universal (Apple silicon plus Intel). Try adding -arch arm64 or -arch x86_64 to the nm command, and make sure the path points at the executable inside Contents/MacOS, not at the .app folder.
Is “no networking code” the same as “works offline”?
Not quite. Many apps work offline but sync when a connection returns. No networking code means there is nothing to sync and never a connection, online or not.