Summary
The geohash k-anonymity system only protects the discover query path. Two other paths bypass it entirely, leaking precise user locations to the server.
🔴 Critical
1. Profile update bypasses the entire decoy system
When updateProfile is called, it sends [currentLoc.geohash, ...neighbors(currentLoc.geohash)] — the real cell + its direct neighbors, no decoys. The server stores these verbatim in geohash_cells. The decoy system only protects the query side, not the storage side. The server knows exactly where you are.
2. DM invitations leak precision-7 location (~150m)
chat.ts grabs geohashCells[0] and sends it as senderGeohashCell — no decoys, no truncation. The server stores it raw, and the recipient sees it raw. That's a 150m×150m cell handed over in plaintext.
🟠 High
3. Discover endpoint sorting leaks the real cell
The server scores results by longest shared prefix between query cells and stored profile cells. An attacker querying with an expanding grid can observe which cell gets the highest proximity score — that's the real one.
4. Invitation geohash isn't truncated
The discover endpoint truncates returned geohashes to precision 5 (~5km). The invitation endpoint doesn't. So DMs leak ~40x more precise location than discovery does.
🟡 Medium
5. IP sent to third-party GeoIP service
The /location/ip fallback sends the user's IP to ip-api.com. That's a third party logging users' IPs — undermines the privacy story.
6. Decoys are all geographically clustered
All 11 decoys are neighbors-of-neighbors (~5km radius). Over multiple requests, the real cell is the one that appears in every query. A statistical attack across sessions narrows it down quickly.
Root Cause
There are two location submission paths — discover queries (has decoys) and profile updates (no decoys). The decoy system only covers one of them. The profile update is the one the server actually stores, so the privacy guarantee is effectively bypassed.
Proposed Fixes
- Profile update: Store only a truncated geohash (precision 4-5, ~5-40km) instead of the full precision cell + neighbors
- DM invitation: Truncate
senderGeohashCell to precision 4-5 before sending
- Discover sorting: Remove proximity scoring or apply it client-side only
- IP fallback: Use Cloudflare headers (
cf-ipcity, cf-iplatitude, cf-iplongitude) instead of third-party API — already available through the tunnel
- Decoys: Add geographically distant decoy cells (different cities/regions) to defeat statistical correlation
Summary
The geohash k-anonymity system only protects the discover query path. Two other paths bypass it entirely, leaking precise user locations to the server.
🔴 Critical
1. Profile update bypasses the entire decoy system
When
updateProfileis called, it sends[currentLoc.geohash, ...neighbors(currentLoc.geohash)]— the real cell + its direct neighbors, no decoys. The server stores these verbatim ingeohash_cells. The decoy system only protects the query side, not the storage side. The server knows exactly where you are.2. DM invitations leak precision-7 location (~150m)
chat.tsgrabsgeohashCells[0]and sends it assenderGeohashCell— no decoys, no truncation. The server stores it raw, and the recipient sees it raw. That's a 150m×150m cell handed over in plaintext.🟠 High
3. Discover endpoint sorting leaks the real cell
The server scores results by longest shared prefix between query cells and stored profile cells. An attacker querying with an expanding grid can observe which cell gets the highest proximity score — that's the real one.
4. Invitation geohash isn't truncated
The discover endpoint truncates returned geohashes to precision 5 (~5km). The invitation endpoint doesn't. So DMs leak ~40x more precise location than discovery does.
🟡 Medium
5. IP sent to third-party GeoIP service
The
/location/ipfallback sends the user's IP to ip-api.com. That's a third party logging users' IPs — undermines the privacy story.6. Decoys are all geographically clustered
All 11 decoys are neighbors-of-neighbors (~5km radius). Over multiple requests, the real cell is the one that appears in every query. A statistical attack across sessions narrows it down quickly.
Root Cause
There are two location submission paths — discover queries (has decoys) and profile updates (no decoys). The decoy system only covers one of them. The profile update is the one the server actually stores, so the privacy guarantee is effectively bypassed.
Proposed Fixes
senderGeohashCellto precision 4-5 before sendingcf-ipcity,cf-iplatitude,cf-iplongitude) instead of third-party API — already available through the tunnel