gledach / the shelf / talk

talk · slot 06 · shipped

Which API hosts your
coding agents connect to.
On your own machine.

Samples TCP connections on Windows, follows each process up to its parent, and reads the certificate name of a new public host. A Node helper under Cursor counts as Cursor.

MIT licensed · no account · 0 runtime dependencies · Windows

node bin/talk.mjs once real output
== 2026-10-24 01:24:30
conns 13380   orphaned 3647   agents 15

ALERT  22124  Cursor.exe  9113
       us4p.cursorvm.com
       us3p.cursorvm.com
ALERT  32664  died before the first sample  3591
       us3p.cursorvm.com

Pid     Alive  Total  Name          Parent
22124   true   9113   Cursor.exe    Cursor.exe
22736   true   16     Cursor.exe    Cursor.exe
34332   true   12     claude.exe    pwsh.exe
8728    true   10     grok.exe      pwsh.exe
43492   true   8      Grok Bot.exe  explorer.exe
One sample, top of the table. A Cursor helper held 9,113 connections to two of its own hosts. A process that had already exited still held 3,591, and could no longer be named. The client port range on Windows is 16,384.

The situation

It looks like the network died.

A long agent session can open connections and never close them. When the port range fills, curl fails in about 20 milliseconds and tests die with EADDRINUSE.

The owner is a helper

The process in that sample was not the window you clicked. It was Cursor.exe running as a Node helper. The same helper type is used by VS Code, so the name of the utility is not enough. talk walks up to the parent.

The host is on the certificate

The URL path stays encrypted. The certificate name does not. talk opens one extra connection to a new public address, reads that name, and stores the name. It does not store a prompt or a header.

What was missing

A record you can leave running, so the next leak is caught while the process is still alive: who, which host, and whether the count is climbing. It does not close a socket and it does not restart anything.

Running it

There is no npm install, because there is nothing to install.

A fresh clone with Node 22 does everything below. The sampler is core/win-snapshot.ps1, inside the repository. Logs go to data/, which is gitignored.

git clone https://github.com/gledach/talk && cd talk
node bin/talk.mjs once       # one sample
node bin/talk.mjs watch      # every 60 seconds, until you stop it
node bin/talk.mjs doctor     # node, powershell, and the sampler in this clone

The command is gledach-talk. npm link puts that name on your PATH. The copy block above calls the same file and needs no link. Nothing named talk is installed. That name is an npm package and a Unix program. gledach-talk once --no-cert skips the certificate read. gledach-talk watch --interval=30 --peeks=4 changes the cadence and how many new hosts are named per sample. gledach-talk report prints the last one.

The commands

Command What it does
gledach-talk once One sample. Prints the agents, the hosts it could name, and any alert.
gledach-talk watch The same sample on a loop. The default is 60 seconds. It stays in the terminal. It does not install a service.
gledach-talk report The last sample, from data/latest.json.
gledach-talk doctor Node, the platform, PowerShell, and whether the sampler file is in this clone.

A line is written when a process holds 500 connections or more, when an agent's count climbs by 30 in one interval, when orphaned sockets grow, or when an agent asks for a host it has not used before.

What it records

Who, which host, and whether the count is climbing.

The sampler is read only. It never closes a socket and it never kills a process. Restarting the agent releases sockets it still owns. Orphans from a dead process stay until reboot, or an admin restart of winnat.

The parent, not the subtype

Cursor, Claude, Grok, Codex, Windsurf and Aider count, and so does a child whose parent is one of them. VS Code's Node helper uses the same utility subtype and does not count, unless the path or the parent says otherwise.

The name, once

A new public address on port 443 is looked up by reading its certificate, then cached. The default budget is four lookups a sample, spent on the busiest agent first. Private addresses are left alone.

The log stays local

samples.jsonl, alerts.log, and a small cache of hosts already named. Nothing is uploaded. There is no account and no key.

What it cannot see

Stated here rather than buried in a wiki.

Every tool on this shelf ships a written audit of its own blind spots. This is talk's, in full, from its own README.

  • A hostname that was never sent in the TLS handshake. The certificate read connects to the IP, a few times per sample, heaviest agent first. A server that will not present a name without the right hostname comes back blank.

  • The path after the host. /v1/messages versus a telemetry route is encrypted, and this tool does not decrypt it. Prompts and Authorization headers are never stored.

  • Encrypted DNS that never presents a certificate name. An agent that resolves names that way, and never shows a name on the certificate, is a gap.

  • A process that exited before the first sample. Its sockets still count, and the owner can no longer be named.

  • Anything that is not TCP. The sampler reads the Windows TCP table.

  • Any operating system other than Windows. report can print a log you already have. once and watch cannot take a sample elsewhere.

0 runtime dependencies. The offline tests check who counts as an agent, including a Cursor helper and a VS Code helper that must not match, and they check that a one-element parent chain still counts. They do not open a socket.