Monitor network I/O
Chalk can also record networking state at the time of the report. Let’s set up a heartbeat report that tracks network state.
cat << EOF > networkconfig.c4m
# Turn on heartbeating.
exec.heartbeat.run: true
exec.heartbeat.rate: <<2 seconds>>
# Which Chalk keys we want to track here.
report_template network_focus {
key._OP_TCP_SOCKET_INFO.use = true
key._OP_UDP_SOCKET_INFO.use = true
key._OP_IPV4_ROUTES.use = true
key._OP_IPV6_ROUTES.use = true
key._OP_IPV4_INTERFACES.use = true
key._OP_ARP_TABLE.use = true
key._PROCESS_PID.use = true
key._TIMESTAMP.use = true
}
# Tell heartbeating to use this report.
outconf.heartbeat.report_template: "network_focus"
EOF
Now load the configuration into Chalk.
chalk load networkconfig.c4m
Note: Chalk configuration is immutable. If you ever want to edit a
config you need to pass --replace to chalk load.
Now let’s archive 100kB of the Hacker News website and report on it with Chalk.
$ chalk exec -- wget --mirror --continue --wait 1 --directory-prefix . --quota=100k https://news.ycombinator.com
--2026-03-30 16:57:43-- https://news.ycombinator.com/
Resolving news.ycombinator.com (news.ycombinator.com)... 209.216.230.207, 2606:7100:1:67::26
Connecting to news.ycombinator.com (news.ycombinator.com)|209.216.230.207|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘./news.ycombinator.com/index.html’
news.ycombinator.com/index.html [ <=> ] 34.46K --.-KB/s in 0.09s
Last-modified header missing -- time-stamps turned off.
2026-03-30 16:57:43 (394 KB/s) - ‘./news.ycombinator.com/index.html’ saved [35288]
... Output truncated ...
FINISHED --2026-03-30 16:57:50--
Total wall clock time: 7.6s
Downloaded: 7 files, 129K in 0.3s (494 KB/s)
Download quota of 100K EXCEEDED!
Finally let’s query the JSON Chalk logs to find any network activity connected to non-local IP addresses, and the time where we observed the connection (not necessarily the time when the connection happened).
$ cat ~/.local/chalk/chalk.log | jq -rs '.[][] | . as $entry |
$entry._OP_TCP_SOCKET_INFO[]?
| select(
.[4] == "ESTABLISHED" and
(.[2] | test("^127\\.|^192\\.168\\.|^10\\.|^172\\.(1[6-9]|2[0-9]|3[0-1])\\.") | not)
)
| "Connected to \(.[2]) at \($entry._TIMESTAMP / 1000 | strftime("%Y-%m-%d %H:%M:%S UTC"))"
'
Connected to 209.216.230.207 at 2026-03-30 16:57:43 UTC
Connected to 209.216.230.207 at 2026-03-30 16:57:45 UTC
Connected to 209.216.230.207 at 2026-03-30 16:57:47 UTC
Connected to 209.216.230.207 at 2026-03-30 16:57:49 UTC
And if we weren’t aware what was running (because we generically
wrapped all user execution in chalk exec) we could grab that IP and
figure out who it is.
$ dig -x 209.216.230.207 +short
news.ycombinator.com.