gledach / the shelf / phonehome

phonehome · slot 04 · shipped

See what your
devices talk to.
Nothing is uploaded.

A local DNS resolver that records which domains every device on your network contacts, classifies them, and tells you when one starts talking somewhere new.

MIT licensed · no account · 0 runtime dependencies · 64 offline tests

gledach-phonehome report real output
  device        queries  domains  busiest category  unknown
  living room   144      12       telemetry         40%
  doorbell      130      12       unknown           45%

  category     queries  share
  unknown      172      43%
  telemetry     59      15%   Product telemetry and crash reporting
  advertising   41      10%   Ad serving and real-time bidding
  doh           33       8%   Encrypted DNS, which routes around this tool

  Encrypted DNS
    doorbell -> dns.google
    A device that switches to encrypted DNS stops appearing here at all.
unknown stays visible and is counted. A classifier that quietly labels everything looks better and tells you less.

The situation

Finding out is a different job from filtering.

Nothing on your network tells you where it has been sending data, or when that changed. Plenty of tools will block traffic for you. Almost none will simply show you what is happening.

new is the one that earns its keep

A steady list of domains is background noise. A doorbell that contacts something at 03:00 which it has never contacted before is the thing worth knowing, and that is the command that reports it.

It observes, it does not act

It never blocks anything and it never modifies a response. It forwards packets and returns the upstream answer unchanged, so it cannot break name resolution by inventing one.

Nothing leaves the machine

Everything it observes stays on the machine you ran it on, and the default upstream is whatever your machine was already using. It does not send your queries anywhere new.

The fastest way to see something real

You may already have months of history on disk.

If you run Pi-hole, AdGuard Home, dnsmasq or Unbound, read those logs rather than re-pointing your network. The format is detected automatically.

git clone https://github.com/gledach/phonehome && cd phonehome
node bin/phonehome.mjs import /var/log/pihole.log --execute
node bin/phonehome.mjs report      # what talked to what
node bin/phonehome.mjs new         # what started somewhere new

There is no npm install, because there is nothing to install. A resolver and a JSONL file, and nothing else.

Otherwise, run the resolver

node bin/phonehome.mjs watch                        # binds 127.0.0.1:5335
node bin/phonehome.mjs watch --host=0.0.0.0 --port=5335

By default it binds 127.0.0.1:5335, which only this machine can use. That is deliberate: serving the whole house is a decision you make, not something that happens because you tried the tool. To see every device, bind the network and point your router's DHCP at that address. Port 53 is the one devices expect and needs privileges on most systems. Many routers let you set a custom DNS port, and where they do not, forward 53 to 5335.

The commands

Command What it does
gledach-phonehome watchRun the resolver and record what asks for what.
gledach-phonehome reportWhat happened, per device and per category.
gledach-phonehome newWhat started contacting somewhere it never had before.
gledach-phonehome devicesEverything that has asked, so you can name it.
gledach-phonehome importRead a DNS log you already have. Four formats, detected automatically.
gledach-phonehome doctorConfig, ports, upstream, and what this cannot see.

The command is gledach-phonehome. npm link puts that name on your PATH. The copy blocks above call the same file and need no link. Nothing named phonehome is installed: npm already publishes an unrelated package under that name. A report of IP addresses tells you almost nothing. A report of names tells you everything, so cp config/devices.default.mjs config/devices.local.mjs and map addresses to names. Run devices first: it prints the exact lines to paste. The local copy is gitignored, because your network layout is nobody else's business.

For agents

Five read-only tools over MCP.

Every result carries the blind-spot list and a trustEmptyResult flag, so an agent cannot report a clean bill of health that the data does not support.

node mcp-server.mjs

list_devices  ·  get_report  ·  new_contacts
classify_domain  ·  blind_spots

What it cannot see

This matters more than the feature list.

So it is not buried at the bottom of a wiki. A tool like this is dangerous precisely when it is trusted more than it deserves.

  • Encrypted DNS. A device using DoH or DoT resolves over HTTPS and never asks this resolver anything, so it becomes completely invisible. watch flags the moment a device looks up a known DoH endpoint, because that is usually the last thing you will ever see from it.

  • Content. This reads the question in a DNS packet and nothing else. A domain being contacted is not the same as data being sent.

  • Devices with hardcoded resolvers. Some hardware ignores DHCP and talks to a public resolver directly. Only a firewall rule catches that. This tool cannot.

  • Who, as opposed to what. It sees devices, not people. It is not a way to find out what somebody in your house is doing, and the report is built around devices and domains for that reason.

  • Classification coverage. The bundled list is deliberately short. Every report prints the share that landed in unknown, because a classifier that labels everything looks better and tells you less.

Tests: 64, fully offline, no network. The resolver is tested against a fake upstream and a documentation-range address, so the gate never sends a packet off the machine. That file alone found three real bugs: a reuseAddr flag that let two resolvers bind one port and split the queries between them, TCP listen errors being swallowed, and in-flight sockets leaking on shutdown.