thekilteddev

Field notes / threat modelling

Protected nothing

One guardrail stood between a food-diary repository and going public. Someone traced it through the code a day later and found the field it named had never been in the repository. What did need guarding was in a file nobody was arguing about.

2026-08-22 ·5 min read ·repo visibility·hosting

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.

This is a proof-of-concept built by one technical person working alongside an NHS dietetic team, not an official NHS product, and no patient data lives anywhere in it.

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.

never in the repoWhere 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.

WHERE A TYPED DESCRIPTION ACTUALLY GOES on the device typed in the app a note on a log entry in IndexedDB exported to a spreadsheet the dietitian handles it from there the repository never committed, never pushed, never served
The whole path runs on the device and ends in the dietitian's spreadsheet. The repository is not a stop on it, which is why its visibility could not have protected the field the guardrail named.

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 reach, never once been true.

~40 files, 1 sensitive stringThe 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. A Namecheap migration plan and the spec beside it, DEPLOY.md, NOTES.md, DECISIONS.md, and a stray .cpanel.yml. 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.

two repos, not oneA 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.

not a hosting decisionWhat 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 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 open.