← Weekend at Claude's / Vol. 17
Privacy  ·  Vol. 17

Fifteen Minutes to Rule Out a Privacy Problem

One open-ended question. A precise worst-case scenario. A fix that was already there.
Weekend at Claude's — all posts
Prologue Vol. 0 Vol. 1 Vol. 2 Vol. 3 Vol. 4 Vol. 5 Vol. 6 Vol. 7 Vol. 8 Vol. 9 Vol. 10 Vol. 11 Vol. 12 Vol. 13 Vol. 14 Vol. 15 Vol. 16 Vol. 17 Vol. 18 Vol. 19 Vol. 20 Vol. 21

Series: Weekend at Claude's — misadventures in building a production app with an AI anyone could mistake for the person who's going to make the whole thing happen

I asked one question. It took fifteen minutes to get a complete answer, a decision, and a shipped fix.

The question was simple to type and hard to actually answer: can anyone — an org admin, a platform admin, anyone — take a name string a user typed into the app and connect it to something that identifies them in the real world? And if so, what could they actually do with it?

I didn't have a hypothesis. I had a nagging feeling. We'd just finished designing four features that put names next to claimed items — "Sarah is bringing juice boxes," "Mike is driving Johnny to practice." Useful, exactly what the app is for. But every time you put a name on something, you should stop and ask what that name connects to.

So I asked. That's the whole prompt. No spec, no proposed solution, no "I think the issue is X." Just: walk me through who can connect a name to an identity, and tell me if that's a problem.

What came back wasn't a guess

The response didn't start with reassurance. It started with research — pulling the actual security rules, the actual Cloud Function code, the actual architecture doc sections on identity layers, before saying anything about whether there was a risk.

That mattered, because the answer depended on details I genuinely didn't have memorized. Aphilaty has three layers of identity by design: a device-level ID that never leaves our backend, a per-org anonymous hash used only in aggregate rollups, and a Firebase user ID that's meaningless outside our own system. None of those are "the user's name." The name is a separate, self-chosen field that members already see inside an org they belong to.

The actual finding, stated precisely: an org admin who wanted to misuse the system could see, within one org they administer, a name string plus a zip code plus a reliability score. That's it. They cannot see email. They cannot see other orgs that person belongs to. They cannot connect any of it to a device ID. The worst case is the same information a coach already has from a sign-up sheet at practice.

But there was one real gap: the name field is global. If you use the same name across every org you join, every admin in every one of those orgs sees that same name. Not a data leak — just an absence of a control that should exist.

The decision took one message

Three options came back, each with a one-line tradeoff: leave it as-is, let users set a different name per org, or split the behavior by whether the org is public or private.

I picked the middle one. That was the entire extent of my technical input — not a design, not an architecture, not even a strong opinion about implementation. Just: yes, do that one.

What I didn't have to do was specify how. The field for a per-org name already existed in the data model from an earlier session — displayNameOverride, sitting unused on every org membership. Nobody had built UI for it yet. The fix wasn't a new system. It was surfacing a lever that was already there, plus one Cloud Function to write to it, plus a one-line change to the function that already resolves names for the app to display.

By the time I'd finished reading the explanation, there was a complete implementation spec: the exact code change, the new function, the UI mockup for the settings screen, a privacy document laying out the full access matrix for every party who might ever touch this data, and an update to project decisions, history, and the task tracker — all done, all consistent with each other, all ready to hand to Claude Code.

Why this is the part worth writing about

The interesting thing isn't that the privacy question got answered. It's that asking a vague, anxious question — is this a problem? — produced a precise, falsifiable answer instead of a vague, reassuring one. "Don't worry, we built this with privacy in mind" would have been worthless. What I got instead was a worst-case scenario stated in exact terms, with the exact two fields an admin could see and the exact reason they couldn't see more.

That precision is what let the decision happen so fast. I wasn't deciding whether to trust an assurance. I was deciding between three clearly stated tradeoffs, one of which obviously fit how people actually use Aphilaty — families, teams, churches, schools, where everyone already knows each other by name in real life, but where nobody had given users a way to draw a line between their family group and a more public org.

Fifteen minutes. One open-ended question, three follow-up decisions, zero technical specification from me. The privacy review, the architecture-compliant fix, the implementation spec, and the documentation all arrived as one coherent unit — because the question got taken seriously enough to actually go check the code instead of summarizing what privacy-conscious apps usually do.

That's the lesson I keep relearning on this project: the value isn't in me knowing the answer. It's in asking the question precisely enough that a real answer is possible, and then being willing to act on it once it shows up.

Aphilaty is a privacy-first community coordination app. aphilaty.com

← Previous
The Screen Nobody Wants to Build
Next →
It Found Errors That Weren't Its Fault. Then I Made That the Rule.