<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Privacy on PHP Boy Scout</title><link>https://phpboyscout.uk/tags/privacy/</link><description>Recent content in Privacy on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Sat, 06 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://phpboyscout.uk/tags/privacy/index.xml" rel="self" type="application/rss+xml"/><item><title>The consent you can't ask for</title><link>https://phpboyscout.uk/the-consent-you-cant-ask-for/</link><pubDate>Sat, 06 Jun 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/the-consent-you-cant-ask-for/</guid><description>&lt;img src="https://phpboyscout.uk/the-consent-you-cant-ask-for/cover-the-consent-you-cant-ask-for.png" alt="Featured image of post The consent you can't ask for" /&gt;&lt;p&gt;There&amp;rsquo;s a comfortable story going round about telemetry, and it goes like this.
There are two kinds. There&amp;rsquo;s the creepy kind, the usage data a vendor harvests to
work out who you are and what you do, and that kind needs your permission. And
there&amp;rsquo;s the innocent kind, the operational data a service emits so the people
running it can keep it up, and that kind is just plumbing, nobody&amp;rsquo;s business, no
permission required. Two neat boxes, and only one of them has a lock on it.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think the boxes are that neat. And I think a fair few of the people
drawing them that way know it.&lt;/p&gt;
&lt;p&gt;Because there&amp;rsquo;s no clean line where operational data stops being personal. A web
service&amp;rsquo;s logs carry IP addresses. Its traces carry the path you walked through
the system, the ids of the things you touched, sometimes the very fields you sent.
Point at almost any of it and a GDPR lawyer will cheerfully tell you it can be
personal data, and that the law doesn&amp;rsquo;t much care whether you filed it under
&amp;ldquo;analytics&amp;rdquo; or &amp;ldquo;observability&amp;rdquo;. The word you picked to describe the data was never
the thing that decided whether it was personal. The data decided that, and a lot
of operational data is personal.&lt;/p&gt;
&lt;p&gt;So if you can&amp;rsquo;t hide behind the box marked &amp;ldquo;just plumbing&amp;rdquo;, what do you actually
do?&lt;/p&gt;
&lt;h2 id="where-im-coming-from"&gt;Where I&amp;rsquo;m coming from
&lt;/h2&gt;&lt;p&gt;I should say up front that I haven&amp;rsquo;t always been this relaxed about it. I spent a
good few years in righteous fury at every tool that phoned home, every &amp;ldquo;we collect
anonymous telemetry to improve the product&amp;rdquo; I never agreed to. Then I started
building the tools, and I needed the data myself: the kind that tells you which
features people actually use and which command falls over on first run, the kind
that lets you make the next decision with something better than a hunch. And it
softened me. Not into thinking it&amp;rsquo;s fine to take it without asking. Into
understanding why everyone wants to.&lt;/p&gt;
&lt;p&gt;What the fury left me with, the one thing I&amp;rsquo;ve never talked myself out of, is
being pro-choice. Not pro-collection, not anti. Pro-choice. Any tool I ask another
person to run will never quietly opt them into sending me a thing. It asks. On
first run it &lt;a class="link" href="https://phpboyscout.uk/telemetry-that-asks-first/" &gt;makes its case&lt;/a&gt;,
says what it wants and why, and lets them say no and mean it. I&amp;rsquo;ll try hard to win the yes, because the data is genuinely useful and a
tool gets better when people share it. But I won&amp;rsquo;t presume it. The choice is
theirs, and the prompt exists so they actually get to make it.&lt;/p&gt;
&lt;h2 id="the-trouble-with-a-service"&gt;The trouble with a service
&lt;/h2&gt;&lt;p&gt;Which is a lovely principle right up until you build a web service. Because who,
exactly, do you prompt? An API doesn&amp;rsquo;t have a first run. It has a thousand callers
a second, none of them sat at a terminal waiting to tick a box. You can&amp;rsquo;t show a
consent dialog to a webhook. The answer the industry reaches for is &amp;ldquo;consent is
implied by use&amp;rdquo;, and&amp;hellip; maybe. It&amp;rsquo;s a grey area, full stop. Implied consent is the
same hand-wave that gave us the cookie banner, the thing we all click through
without reading. I&amp;rsquo;m not going to stand here and call it clean.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s a version of the principle that survives the grey, and it&amp;rsquo;s the one I
&lt;a class="link" href="https://phpboyscout.uk/telemetry-that-asks-and-telemetry-that-doesnt/" &gt;built the framework around&lt;/a&gt;. Consent belongs to whoever can actually give it. For a
command-line tool, that&amp;rsquo;s the person running it, so you ask them. For a web
service, the person who can give it was never the end user at all, because you
can&amp;rsquo;t reach them. It&amp;rsquo;s the engineer who deploys the thing. They know what their
service collects, who its users are, which law they sit under, whether they owe
anyone a privacy notice. They are the one party in the whole chain who can make
the call with any of the facts in front of them. So that&amp;rsquo;s where the choice goes.&lt;/p&gt;
&lt;p&gt;Which is why, in go-tool-base, the web-service telemetry is a switch. On or off,
the engineer&amp;rsquo;s hand on it, collecting only what you need to keep the lights on by
default. There&amp;rsquo;s no consent prompt, not because consent stopped mattering, but
because there&amp;rsquo;s nobody in the loop I could ask. The accountability sits with the
person who can hold it.&lt;/p&gt;
&lt;h2 id="the-part-ill-own"&gt;The part I&amp;rsquo;ll own
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m pro-choice on telemetry, which is exactly why I built a way to switch it off
and a way to force it on. Because for a web service the person holding the choice
was never the end user, it&amp;rsquo;s the engineer who ships it, and &amp;ldquo;pro-choice&amp;rdquo; has to
mean putting the switch in their hand, not pretending a popup would have meant
anything.&lt;/p&gt;
&lt;p&gt;That force-it-on part is the bit I&amp;rsquo;ll answer for. I built a way for a tool author
to bypass the first-run prompt entirely and bake the consent in. There&amp;rsquo;s a real
use case behind it, the enterprise tool deployed under a policy where collection
is contractual rather than optional. But I also know I&amp;rsquo;ve handed someone a way to
take the choice away, and I did it deliberately. Rightly or wrongly, I made the
framework flexible enough to do the wrong thing, and the line I care about is now
only as safe as the judgement of whoever picks it up.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the uncomfortable place this lands, and I&amp;rsquo;ve come to think it&amp;rsquo;s the true
one. A framework can put the choice in the right hands. It cannot make the right
choice. I can build the prompt, build the switch, set the defaults to the modest
thing, and after that I have to trust the engineer on the other side to use it
justly and with some wisdom, because there is nothing further down the stack that
makes them. When the blame gets shared out, and it&amp;rsquo;s always shared, a piece of it
has my name on it, for every escape hatch I left in.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m at peace with that, mostly. Not because the grey went away, but because the
alternative, pretending there&amp;rsquo;s a clean line and that &amp;ldquo;operational&amp;rdquo; means &amp;ldquo;not
your problem&amp;rdquo;, is the real dodge. I&amp;rsquo;d rather say it plainly: this data can be
personal, the consent is real even when there&amp;rsquo;s nobody to ask, and the most a tool
can do is hand the decision to the person who can make it, and trust them with it.&lt;/p&gt;</description></item><item><title>Telemetry that asks first</title><link>https://phpboyscout.uk/telemetry-that-asks-first/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/telemetry-that-asks-first/</guid><description>&lt;img src="https://phpboyscout.uk/telemetry-that-asks-first/cover-telemetry-that-asks-first.png" alt="Featured image of post Telemetry that asks first" /&gt;&lt;p&gt;Usage telemetry is genuinely useful. Knowing which commands people actually run, where the errors cluster, whether anyone ever touched the feature you spent a fortnight on&amp;hellip; that&amp;rsquo;s the stuff that makes you a better maintainer. Wanting it is completely legitimate.&lt;/p&gt;
&lt;p&gt;The trouble is that the &lt;em&gt;usual&lt;/em&gt; way of getting it, on by default and quietly hoovering up everything, is a small betrayal of the people who installed your tool to get a job done. I wasn&amp;rsquo;t willing to build that, so go-tool-base&amp;rsquo;s telemetry starts from a different question.&lt;/p&gt;
&lt;h2 id="the-data-you-want-and-the-line-you-shouldnt-cross"&gt;The data you want, and the line you shouldn&amp;rsquo;t cross
&lt;/h2&gt;&lt;p&gt;If you maintain a tool, you want to know how it&amp;rsquo;s actually used. Which commands matter and which are dead weight. Where the error rate spikes. Whether anyone touched the feature you spent that fortnight on. That information makes you a better maintainer, and, to say it again, wanting it is completely legitimate.&lt;/p&gt;
&lt;p&gt;The trouble is the standard way of getting it. Telemetry on by default. An opt-out buried three levels down in a settings file nobody reads. And once it&amp;rsquo;s running, it quietly collects far more than it ever admitted to: the arguments people passed, the paths they were working in, an IP address for good measure.&lt;/p&gt;
&lt;p&gt;Every one of those is a small betrayal of someone who installed your tool to get a job done, not to become a data point. And the cost when users notice isn&amp;rsquo;t a slap on the wrist. It&amp;rsquo;s trust, and trust in a developer tool does not grow back quickly. A tool that surprises you once with what it was quietly collecting is a tool you uninstall and warn your colleagues about.&lt;/p&gt;
&lt;p&gt;So go-tool-base&amp;rsquo;s telemetry started from a different question. Not &amp;ldquo;how do we collect the most data&amp;rdquo; but &amp;ldquo;how do we collect &lt;em&gt;useful&lt;/em&gt; data without ever putting the user in a position they didn&amp;rsquo;t choose&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="rule-one-it-is-off-until-you-say-otherwise"&gt;Rule one: it is off until you say otherwise
&lt;/h2&gt;&lt;p&gt;The foundation is the simplest possible rule, and it&amp;rsquo;s absolute. Telemetry is &lt;strong&gt;never enabled by default.&lt;/strong&gt; A freshly installed tool built on go-tool-base sends nothing. Not a heartbeat, not a ping, nothing at all.&lt;/p&gt;
&lt;p&gt;It only starts collecting when the user makes an explicit, visible choice to let it. Three honest doors: they run &lt;code&gt;telemetry enable&lt;/code&gt;, they say yes to a clear prompt during &lt;code&gt;init&lt;/code&gt;, or they set &lt;code&gt;TELEMETRY_ENABLED&lt;/code&gt; themselves. All three are deliberate acts. None of them is a pre-ticked box or a default they have to discover and then undo.&lt;/p&gt;
&lt;p&gt;This is opt-&lt;em&gt;in&lt;/em&gt;, and the distinction from a well-hidden opt-&lt;em&gt;out&lt;/em&gt; is the entire point. Opt-out telemetry treats consent as something to be assumed and grudgingly reversed. Opt-in treats it as something that has to be &lt;em&gt;given&lt;/em&gt;. Only one of those is actually consent.&lt;/p&gt;
&lt;h2 id="rule-two-no-personally-identifiable-information-full-stop"&gt;Rule two: no personally identifiable information, full stop
&lt;/h2&gt;&lt;p&gt;Consent to &amp;ldquo;some telemetry&amp;rdquo; is not consent to &amp;ldquo;any telemetry&amp;rdquo;, so the second rule constrains what can ever be collected, even from a user who&amp;rsquo;s opted in.&lt;/p&gt;
&lt;p&gt;No personally identifiable information. The framework does not record command arguments (they routinely contain paths, hostnames, the occasional secret someone&amp;rsquo;s pasted in). It does not record file contents. It does not record IP addresses.&lt;/p&gt;
&lt;p&gt;It does need &lt;em&gt;some&lt;/em&gt; notion of &amp;ldquo;distinct installations&amp;rdquo; for the numbers to mean anything, so it derives a machine ID from a handful of system signals and runs it through &lt;a class="link" href="https://gitlab.com/phpboyscout/go-tool-base/-/blob/5c78fc9/pkg/telemetry/machine.go#L12" target="_blank" rel="noopener"
 &gt;SHA-256&lt;/a&gt;. What leaves the machine is a hash. It tells you &amp;ldquo;this is the same install as last week&amp;rdquo; and tells you precisely nothing about whose install it is, and the hash can&amp;rsquo;t be walked backwards into the signals it came from.&lt;/p&gt;
&lt;p&gt;The events themselves are deliberately thin. Which command ran, roughly how long it took, whether it errored. The shape of usage, not a transcript of it.&lt;/p&gt;
&lt;h2 id="rule-three-the-author-picks-the-destination"&gt;Rule three: the author picks the destination
&lt;/h2&gt;&lt;p&gt;Even with consent given and PII excluded, there&amp;rsquo;s a third question: where does the data actually &lt;em&gt;go&lt;/em&gt;? go-tool-base doesn&amp;rsquo;t answer that for you, because it can&amp;rsquo;t. A corporate internal tool, an open-source CLI and an air-gapped utility have completely different right answers.&lt;/p&gt;
&lt;p&gt;So the backend is the tool author&amp;rsquo;s choice. The framework ships several (a noop backend, stdout, a file, plain HTTP, and OpenTelemetry over OTLP) and supports custom ones. The noop backend matters more than it looks: it lets a tool wire up the whole telemetry surface, commands and all, while sending data precisely nowhere. A perfectly reasonable, fully supported configuration.&lt;/p&gt;
&lt;p&gt;Pluggable backends also mean the data never has to touch any infrastructure I run. It goes where the tool&amp;rsquo;s author decides, on their terms. The framework provides the plumbing and stays well out of the destination.&lt;/p&gt;
&lt;h2 id="and-a-way-back-out"&gt;And a way back out
&lt;/h2&gt;&lt;p&gt;One last thing, because it&amp;rsquo;s the part that makes the opt-in real rather than decorative. A user who opted in can opt straight back out, and the package includes a &lt;a class="link" href="https://gitlab.com/phpboyscout/go-tool-base/-/blob/5c78fc9/pkg/telemetry/deletion.go#L24" target="_blank" rel="noopener"
 &gt;GDPR-aligned deletion path&lt;/a&gt;, so &amp;ldquo;stop, and remove what you have&amp;rdquo; is an actual supported request rather than a polite fiction.&lt;/p&gt;
&lt;p&gt;Consent you can&amp;rsquo;t withdraw isn&amp;rsquo;t consent. It&amp;rsquo;s a one-way door with a friendly sign on it. The deletion path is what keeps the front door an actual door.&lt;/p&gt;
&lt;h2 id="the-bottom-line"&gt;The bottom line
&lt;/h2&gt;&lt;p&gt;Telemetry is genuinely useful to a maintainer and genuinely dangerous to the trust of the people running the tool, and the usual implementation (on by default, opt-out buried, collecting everything) spends that trust recklessly. go-tool-base&amp;rsquo;s telemetry holds three lines: never enabled without an explicit user action, never collecting personally identifiable information even once enabled, and always sending data to a destination the tool&amp;rsquo;s author chose, up to and including nowhere. A real deletion path makes the opt-in something you can take back.&lt;/p&gt;
&lt;p&gt;You can have your usage numbers. You just have to ask for them, the way you would for anything else that wasn&amp;rsquo;t yours to begin with.&lt;/p&gt;</description></item></channel></rss>