The house has a front door on the internet, and the door has a list. Addresses on the list may knock; everyone else gets the sound of nothing. The phone, which is the one thing that knocks from outside every day, changes its address several times a day, on trains, in cafés, and most reliably about eight seconds after arriving home. For years the answer was dynamic DNS, which is a way of telling the door a rumour and hoping the rumour has not expired.
Dynamic DNS is a rumour with a time-to-live. The replacement is a phone that introduces itself, and a door that forgets the introduction on a schedule.
The phone measures itself
An automation app on the phone watches for every change of connectivity: Wi-Fi joined, Wi-Fi dropped, mobile data back. It waits eight seconds for the dust to settle, asks a public "what is my address" service, and posts the answer to a webhook on the house. It does this at boot too, and every six hours regardless, because an app that only speaks when something changes cannot tell you it has died.
The house checks the answer the way you would check a stranger's ID: it must look like an address, it must not be a private one, and it must not be from the shared ranges carriers hand out to a thousand phones at once. If it passes and it differs from the last one, the house asks the router to put it on the list, with a comment saying who did it and an expiry of two days, and tells us. Old entries with the same comment are removed. That is the entire mechanism.
Five ways this did not work first
The source address was right there. The obvious plan was to ignore what the phone says and read where the request came from. The webhook trigger exposes no headers and no peer address, and behind a proxy it would have been the proxy anyway. So the phone has to measure itself, which is less elegant and more true.
The JSON that was not. The phone's IPv6 lookup fails on some networks and returns an error message. The app pasted the message into the JSON body, quotes and all, and the body stopped being JSON. The house answered 200 OK and ran nothing, because the parser failed before the automation was consulted, and a parser failing before your code is the most silent failure there is. The body is now form-encoded, a format that a stray quotation mark cannot break. The wallet feed learned the same lesson a month earlier, which is embarrassing, and is why it is written down twice.
The variable that was a string. A template variable that returns a dictionary returns a string that looks like a dictionary, and the first attempt to read a field from it read a character instead. Fields are now read inside one template, which is the boring correct answer to most template questions.
The port. An afternoon went on the wrong one. A later hotel then blocked the respectable-looking port and let the odd one through, which settled the argument in the odd one's favour and is the closest thing to a code review a hotel Wi-Fi will ever give you.
The shared address. On mobile data the phone's "public" address is carrier-grade NAT: a thousand strangers wearing the same coat. The list admits the coat; the login still has to admit the person, which it does, and which is why the list was never the only lock. IPv6 gives the phone a prefix of its own, and that one is honest.
The diary
A side effect nobody designed: the house now keeps a log of when the phone changed its mind about where it was, with timestamps. It reads like a commute. Home, train, office, café, train, home, roughly four changes a day, the last one always at the front door, eight seconds after the key. If the log ever shows a change at three in the morning we will have learned something, though possibly not about networking.
There is a proper answer to all of this, an overlay network that makes the phone a resident wherever it stands, and it is installed and waiting. The list is what works today, and today, as ever, is where the house lives.