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.

  1. 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).
  2. 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.
  3. 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 as nw_connection_create.
  • BSD sockets: socket, connect, getaddrinfo. Lower level, used by some libraries.
  • Linked frameworks: CFNetwork.framework or Network.framework listed 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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 curl or 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 sits between level two and level three, and it is worth being exact about where, because you can check it yourself with the commands above.

The app makes exactly one kind of network call in its life: activating a license key. That call goes to the payment provider, sends the key and this Mac’s name, and gets back a yes or no. It happens when you paste a key in, and once more on launch to catch a refund. Nothing else in the app can reach the network.

That boundary is enforced at build time rather than promised. The release build greps the sources and fails if URLSession, URLRequest or the Network framework appears anywhere except the one licensing file, and it fails if the compiled binary references any analytics, crash-reporting or telemetry symbol. So the guarantee is not “this app cannot connect”, which would be false. It is “the only thing this app can do over the network is check a license, and the build breaks if that stops being true”.

Run the scan from step 2 on Punchcard and you will find NSURLSession, because of that one call. What you will not find is an analytics SDK, a crash reporter, or an update checker. That is the honest result, and it is the one worth checking for in any app: not whether networking exists, but what it is for.

What it means for the data: 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). Your tracked day is never sent anywhere. Tracking itself works with no connection at all; only activation needs one.

It also means there are things Punchcard still does not offer: no sync between Macs (two Macs each keep their own roll), no web dashboard, no team features. Those are choices rather than consequences now, 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.

  1. Open System Settings, Privacy & Security, and note the apps under Accessibility and Screen & System Audio Recording.
  2. For each, run steps 1 and 2 above.
  3. 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.