netusage.sqlite: macOS Per-Process Network Usage
netusage.sqlite records which processes used the network on a Mac, with first and last seen times and bytes over Wi-Fi, wired and cellular links.
- Location
- /private/var/networkd/db/netusage.sqlite
- Proves
- That a process used the network, when it was first and last seen, how much data it moved per interface type, and which networks the Mac attached to
- Timestamps
- Mac absolute time (seconds since 2001-01-01 UTC)
- Access
- root; the networkd folder is protected, collect with Full Disk Access or from an image
- Retention
- Rows persist for long periods; counters are cumulative and records are pruned by networkd on its own schedule
- Collection
- mac_apt, ditto, Disk image
Tools
Compare all tools- mac_aptCLI · open source
- APOLLOCLI · open source
- sqlite3CLI · built into macOS
What it is
networkd keeps a Core Data SQLite database of network usage per process and per network attachment. It is the closest macOS counterpart to the Windows SRUM network usage table: a record, independent of the Unified Logs, that a named process exchanged data, with running totals of bytes in and out.
For intrusions, it answers questions that are hard to answer otherwise: did this unsigned binary ever talk to the network, and roughly how much did it send?
Where it lives
| Version | Path |
|---|---|
| 10.15 Catalina and earlier | /private/var/networkd/netusage.sqlite |
| 11 Big Sur and later | /private/var/networkd/db/netusage.sqlite |
Collect the -wal and -shm files with the database: recent rows are often still in the write-ahead log. On a dead-box image look under /System/Volumes/Data/private/var/networkd/.
What it proves
- A process (by name, sometimes with bundle identifier) used the network at least once, with the first and last time networkd saw it.
- Cumulative bytes in and out, split by Wi-Fi, wired and cellular (WWAN).
- The networks the Mac attached to (
ZNETWORKATTACHMENT), with first and last seen times, which complements Wi-Fi networks.
It does not record destinations: no IP addresses, domains or ports. Pair it with the Unified Logs, proxy or firewall logs for that.
Key fields
| Table | Columns | Meaning |
|---|---|---|
ZPROCESS | ZPROCNAME, ZBUNDLENAME | Process name, bundle ID when known |
ZPROCESS | ZFIRSTTIMESTAMP, ZTIMESTAMP | First seen, last seen |
ZLIVEUSAGE | ZHASPROCESS | Foreign key to ZPROCESS.Z_PK |
ZLIVEUSAGE | ZWIFIIN, ZWIFIOUT, ZWIREDIN, ZWIREDOUT, ZWWANIN, ZWWANOUT | Bytes per interface type |
ZLIVEUSAGE | ZTIMESTAMP | Time of the usage record |
ZNETWORKATTACHMENT | ZIDENTIFIER, ZFIRSTTIMESTAMP, ZTIMESTAMP | Network identity and seen times |
ZLIVEROUTEPERF | ZBYTESIN, ZBYTESOUT, ZTIMESTAMP | Per-network route statistics |
Z_PRIMARYKEY | Z_NAME | Entity name for each Z_ENT |
Column sets change between releases. Check PRAGMA table_info(ZPROCESS); before trusting a saved query.
Timestamps
All timestamps are Mac absolute time. Add 978307200 to get Unix time. Byte counters are cumulative over the life of the row, not per session: two snapshots of the database taken at different times (for example live and from an APFS snapshot) are needed to measure activity in between.
Retention
networkd keeps rows for a long time, often months, but prunes on its own logic and version-dependent limits, which are not documented. A process that used the network once and was then deleted from disk can still be listed, which is the main value of this source.
Collection
sudo ditto /private/var/networkd/db ./case/networkd_db
The Terminal or collector needs Full Disk Access on current releases. mac_apt NETUSAGE copies and parses the database; APOLLO ships netusage_zprocess, netusage_zliveusage and netusage_zliverouteperf modules.
Parsing
-- Processes with traffic, newest first
SELECT p.ZPROCNAME,
datetime(p.ZFIRSTTIMESTAMP + 978307200, 'unixepoch') AS first_seen_utc,
datetime(p.ZTIMESTAMP + 978307200, 'unixepoch') AS last_seen_utc,
lu.ZWIFIIN, lu.ZWIFIOUT, lu.ZWIREDIN, lu.ZWIREDOUT
FROM ZPROCESS p
LEFT JOIN ZLIVEUSAGE lu ON lu.ZHASPROCESS = p.Z_PK
ORDER BY p.ZTIMESTAMP DESC;
python3 mac_apt.py -o out E01 image.E01 NETUSAGE
python3 apollo.py extract -o sql -p apple -v 10.15 -k modules ./case
APOLLO module queries are version-scoped, and its version list stops at older releases (10.15 / 10.16 for macOS). On newer images the queries usually still run, but check the schema first or use the SQL above.
Investigator tips
- Sort by
ZFIRSTTIMESTAMParound the suspected intrusion time: new process names appearing for the first time are strong leads. - Unusual names (random strings, names imitating Apple daemons, interpreters such as
python3,osascriptorcurl) with large outbound totals deserve a look in launch agents and daemons and the Unified Logs. - A high
OUTversusINratio for a non-backup process suggests upload or exfiltration; confirm with other sources before asserting it. - Browsers, cloud sync clients and update agents dominate totals; filter them before reviewing.