Stage 2: UAVs, truces, Bluetooth, and an album of evidence


Stage 1 made the game work. Stage 2 made it worth playing for a month.

The UAV

Spend a credit and every player's position appears on the map for ten minutes. Two decisions made it interesting.

**Everyone can use it, not just the person who is it.** You can spend yours to run away. That turns it from a hunting tool into a resource both sides want.

Everyone is told when one is active. A banner with a countdown, for the whole gang. Free information would have been boring; announced information is a decision — you are trading surprise for sight, and everyone knows you did it.

One credit per season by default, with more earned by tagging: every third confirmed tag gives you another. It makes a UAV feel like something you spend rather than something you have.

Truces

Propose one, list who it covers, everyone listed has to sign before it starts. Miss the deadline and it lapses — a signature afterwards protects nobody.

Two holes turned up while building the app side, both worth the trouble:

A truce only protects its participants from each other. Anyone outside it can still tag both of you. If you want to be safe from the whole gang, you list the whole gang.

And the protection has to start at the exact second it says, not when a scheduled job gets round to flipping a flag. That job runs every five minutes, which would leave a window where you believe you are safe and are not. So the block is decided by the signatures and the timestamps, and the flag is only for display.

Bluetooth, for indoors

GPS is hopeless in a shopping centre. So the phones also watch for each other over Bluetooth, and a mutual sighting within two minutes counts as proximity.

Building this exposed something ugly: the tag's method field was being sent by the app. That is a cheat path — claim bluetooth and skip the distance check entirely. The server picks the method now, based on what it can actually verify. The app does not get an opinion.

The app side cannot be fully tested, because a simulator has no Bluetooth radio. The server side is covered; the app side waits for two real phones.

The album

You tag first and photograph afterwards. A photo or a clip of up to fifteen seconds can be attached to a tag, but only by the person who made it, only if the tag is fresher than a day, and only once.

Two things production taught me here that no local test could:

A database function cannot delete files in storage. Locally the test rig runs as a superuser and it worked fine. In the real project the permission does not exist, and the failure was being swallowed by its own safety net. The app deletes the evidence files itself before asking the server to delete the account.

A CDN will serve a deleted file for up to an hour to someone who already fetched that exact URL and was allowed to see it. Outsiders never get in, cached or not. Acceptable — they could have taken a screenshot anyway — but I would rather know it than discover it during an argument.

Deleting your account keeps the history

An account deletion anonymises rather than erases. Your name, your positions, your photos, your memberships and your login are gone. Your tags, your stretches as it and your losing titles stay, under Deleted player, because they are the gang's shared memory and not yours alone.

The schema was built for that from the first migration: personal data cascades, history does not.

Where it stands

Migrations 1 to 15 are running in production. 44 unit tests, 212 database assertions, and four interface tests that play the real game against the real backend. Nothing is waiting on me.

Next: design polish, the privacy policy, and TestFlight — which needs an Apple Developer account I have decided to put off buying until the app deserves it.

All entries