Field notes from a proof-of-concept food-diary tool built alongside one NHS dietetic team,
not an official NHS product, no patient data anywhere in it. A privacy guardrail blocked its
repository from going public. Traced through the code, it protected nothing that going public would
have exposed.
A rule sounded exactly right, protected exactly nothing, and stood as the one thing blocking real
work, until someone traced it back to what the code actually does.
The rule was simple. Before the food diary's repository could go public, it needed a guardrail
against identifiable data leaking through the diary's own free-text fields, chiefly a description a
client might type next to a reference photo. Someone could type their own name into it without
thinking, to help remember whose entry it was. That risk was real, agreed on at the time in the
project's own words as "hard to guard... loose enough to say go public," and it sat there as the one
condition standing between the repository and a public GitHub Pages migration everything else was
ready for.
The condition held for a day. Then, traced through the actual code rather than argued about again,
it didn't survive the first look.
Where the description actually goes
The description a client types is captured in the app, stored as a note on a log entry in IndexedDB
on the device, and exported into a spreadsheet the dietitian handles. It is never committed, never
pushed, never served. The repository's visibility has no effect on it whatsoever, because it was
never in the repository to begin with. A guardrail on that field would have protected data that
going public put at no risk at all.
A second, independent reason cut just as deep. The app ships with no build step at all, just
straight unminified JavaScript, so its entire source has been downloaded verbatim by every single
visitor to the live site since the day it launched. Repo visibility was never protecting the
application code either, because the browser was already handing that code to anyone who asked. The
"code stays private" preference behind two earlier hosting rejections had, for the part of the
project a visitor could actually reach, never once been true.
The place nobody checked
If the free-text field was never the risk, going public should have been safe from day one. It
wasn't, quite, and the reason was somewhere else entirely: roughly forty tracked files sitting
outside the application folder, never served to a visitor and shielded from view by nothing but the
repository itself being private, among them migration notes, deploy docs, session logs, and a stray
config file. Grepping through all of it for anything infrastructure-shaped turned up exactly one
genuinely sensitive string: a hosting-account username, and not even this project's own account. It
belonged to someone else's.
| In the repo | Repo privacy hides it | |
|---|---|---|
| Free-text descriptions | no | no |
| Application source | yes | no |
| ~40 files outside the app folder | yes | yes |
Three things the repository question could have been about. Only the last is both tracked in the
repository and hidden by nothing else, so it is the only one that going public actually exposes.
The thing everyone had been scared of couldn't actually happen. The thing worth being scared of
had been sitting in a folder nobody had opened, the whole time the guardrail argument was running.
A fix that doesn't depend on remembering
The fix that shipped doesn't ask anyone to remember anything. Rather than scrub the sensitive files
out of a single shared repository and trust every future session to keep scrubbing, the project
split in two: one private repository holding everything, full history intact, and one public
repository synced from it that holds only the application directory's own contents. Nothing outside
app/ can leak, because nothing outside app/ is ever in the public repo to begin with.
Purging the sensitive files from one shared repository's history was considered and turned down on
purpose. NOTES.md gets written every session, which means every future session is a fresh chance
to type an identifier back into a file that's now public, and a miss like that is silent and
unrecoverable the moment the repository is cloned. The split doesn't have that failure mode. There's
nothing to miss, because the sensitive files are never in the repo that goes public in the first
place.
One faster fix was on the table too, and turned down for a completely different kind of reason.
GitHub offers private-repo Pages hosting on its paid tier, for around four dollars a month, which
would have made the whole question go away without touching a single file. Food diary's own author
turned it down outright, on the spot, with two words: "not pro." No technical argument followed it,
because none was needed. It was a spending decision, not an engineering one, and the project didn't
pretend otherwise.
What the guardrail actually protects now
The free-text field is still a real concern. It existed before this repository was ever considered
for public release, and it will keep existing after, because it's a property of what the app lets
someone type, not of who can see the code. It just isn't a hosting decision, and treating it as one
is the mistake this piece is actually about. It has its own design question to answer, on its own
schedule, and the fix for it won't be a line in a hosting migration plan.
The pattern generalises past one repository. The project specified a control against a threat that
sounded exactly right and had never been traced to the actual place the data flows through. It still
held up real work, however briefly. And the thing worth guarding against the whole time was sitting
in plain sight, in a directory nobody had thought to check, because the argument had already used up
everyone's attention on the door that was never actually open.
Originally published at thekilted.dev/protected-nothing.
Top comments (0)