TL;DR
- I built a tool that files deletion requests across 20 data brokers. First cycle: 13 submitted, 7 blocked by CAPTCHAs, phone gates, and matches too ambiguous to risk.
- The design constraint that shaped everything: my actual personal information never enters the agent's context. It lives in a 0600 local file the agent is forbidden to read, and the agent only ever sees a redacted report.
- That separation cost me maybe forty lines of code and removed an entire category of failure. Most enterprise agent designs skip it because handing over the credential is one less moving part.
- The honest ceiling matters more than the automation: AI is good at orchestration, form-filling, and remembering to do it again in 30 days. It cannot prove you are you.
My phone number was for sale online. So was my address history, my approximate age, and the first names of people I am related to. I knew this in the abstract way everyone knows it. Then on a Sunday morning in late July I actually looked, and the abstraction became a list of twenty websites with my life on them.
I spent the rest of the day building something to take it down. That part is mildly interesting. The part I keep thinking about is a decision I made in the first thirty minutes, before writing any real code, about what the automation was allowed to know.
The problem with the obvious design
The obvious way to build this is to hand the agent your profile. Name, address history, phone numbers, email addresses, employer, relatives. It needs all of that to fill out an opt-out form, so you give it all of that, and you move on to the actually hard part, which is that every broker's form is different.
I have watched a lot of enterprise agent architectures make exactly this call. The agent needs to query the warehouse, so it gets the warehouse credential. It needs to send mail as the team, so it gets the mailbox. It needs to file the ticket, so it gets a service account with more rights than any human on the team. Each decision is locally reasonable. The aggregate is a system where the blast radius of a prompt injection is your entire data estate.
Here the stakes were personal enough to make me slow down. If I put my full identity into a model's context, that context goes into logs. It goes into whatever the provider retains. It goes into my own local transcripts, which sync, which get backed up. I would be solving a data exposure problem by creating four new ones.
So I inverted it. The agent gets the capability to act on my identity. It never gets the identity.
What that looks like in practice
Everything private lives in one file outside the repository, mode 0600, that the automation reads directly at runtime and never echoes:
~/.config/data-broker-eraser/profile.env
The scripts refuse to run if the permissions on that file are wrong. Not a warning, a hard exit. The README says, in the one place a person will actually read it, not to open the populated version in an editor with an AI assistant attached.
Everything the agent may report on lives somewhere else:
~/.local/share/data-broker-eraser/redacted_report.json
That file contains counts, broker identifiers, and statuses. Thirteen submitted. Two blocked on CAPTCHA. One blocked on a phone gate. No names. No addresses. No listing URLs, because a people-search listing URL is itself an identifier. I can paste that report into any chat window, any ticket, any blog post, and disclose nothing.
The whole separation cost maybe forty lines: a config loader, a permissions check, a redaction function, and a rule about which paths get written where. It is not clever. It is just a decision made early instead of retrofitted after a security review.
There is a second boundary in the same file, and it is the one I would have
forgotten if I had built this faster. A BLOCKED_RECIPIENT_DOMAINS setting
lists my employer's domain. Any outbound privacy request that would send to, or
even mention, an address at those domains raises and stops. Automating a
personal task with a work address in the loop is how a Sunday project becomes a
compliance conversation.
The honest results
Twenty brokers. First live cycle:
| Outcome | Count |
|---|---|
| Submitted, awaiting email confirmation | 13 |
| Blocked: CAPTCHA (Spokeo, Radaris) | 2 |
| Blocked: phone verification (Whitepages) | 1 |
| Blocked: ambiguous match | 3 |
| Blocked: broker changed its flow | 1 |
Thirteen and seven. I am publishing the seven because the ratio is the whole lesson.
Three of those blockers are CAPTCHAs and SMS codes, which is the internet correctly refusing to believe a script is a person. I am not going to route around that, and neither should any tool you buy. Anyone selling a fully automated privacy eraser is either overstating it or paying someone offshore to click through your challenges, which means a stranger is holding your identity while they do it.
The three ambiguous matches are the ones I am most pleased with. People-search results are noisy. There are other people with my name, and there is a version of this tool that submits anyway and reports a nicer number. Mine declines. A wrong submission asks a broker to delete a stranger's record, and I would rather work a short manual queue than automate a small harm at scale.
What the automation is genuinely good at: maintaining a catalog of twenty
brokers with different form layouts, drafting statute-correct CCPA language,
tracking which requests are outstanding, and re-running the whole cycle every
thirty days through launchd. That last one matters more than it sounds.
Brokers re-ingest. A one-time opt-out is theater. The value is not the first
run, it is the thirteenth.
Why I think this generalizes
Strip out the data brokers and the pattern is: an autonomous process acting on sensitive material, where the material and the action can be separated.
Almost every enterprise agent I review conflates the two. The agent needs to read from Snowflake, so the agent holds the Snowflake credential in a context window that also holds arbitrary retrieved text. The agent needs to send on behalf of the team, so it holds the mailbox. And then someone asks the obvious question in a design review... what happens if a poisoned document tells the agent to exfiltrate what it is holding... and the honest answer is that nothing in the architecture prevents it.
The alternative is not exotic. Put the secret behind a local function. Let the agent call the function. Return the outcome, not the material. The agent orchestrates, decides, retries, and reports. It never sees the thing that would hurt you if it leaked.
This costs you something real, and I want to be straight about it. The agent is less capable in the narrow sense. It cannot reason about the contents of what it is handling, which means it cannot catch a typo in my street address or notice that a broker matched an old employer. Those checks stay with me. I traded some capability for a hard ceiling on the damage, and for this workload that trade was obvious. It will not always be.
What I would push on, if you are designing these systems for a company rather than a Sunday: the default has drifted the wrong way. Handing the agent everything is the path of least resistance, and the failure mode does not show up in testing. It shows up once, publicly, in an incident report.
Where the code is
github.com/Brianletort/data-broker-eraser, MIT licensed. Twenty brokers in the catalog, twenty-one tests, no network calls in the test suite. You will need Playwright and about ten minutes to fill in your own profile.
Brokers change their forms constantly, so the catalog has a verified_on date
and will go stale. A pull request fixing a selector is the most useful thing
anyone can send me.
If you build it and the ratio comes out differently than thirteen and seven, I would like to know. That number is the honest measure of how much of this AI can actually do, and it should move over time.