ShowUP logo
Case Review · Design Documentation

Designing a
security-centered app

ShowUP is a social network built entirely around local events — no event, no post. People use it to physically show up at parties, gigs and gatherings, which makes it different from an ordinary social app in one uncomfortable way: a leak here isn't embarrassing, it's a stranger knowing where you will be on Friday night. This review walks through how that single fact shaped the whole design — the threat model, the layered defenses, the app flow, and the mockups-v2 screens that carry each guarantee into the UI.

Compiled 2026-08-13 · From design-documentation 01–19 & mockups-v2 · Accumulative — supersedes nothing
01The threat model

The core adversary is a stalker: a stranger trying to track a specific person across events to find them in the real world. Everything else follows from making that impossible. The answer is not invisibility — a hidden person can't be counted, can't chat, can't be checked at the door. The answer is pseudonymity relative to the viewer:

Friends see the real you. Real name, where you're going, what you host. You are not hiding from your mates.
Strangers see a fresh generated alias per eventDrunkBeaver67 here, SneakyOtter12 there — with no way to link the two. Countable within one event, unlinkable across events.

Two further adversaries round out the model. The person holding your unlocked phone — a friend "just checking something", a snatch-and-scroll at a party — is answered by a local app-lock PIN with a background blur cover. And the gatecrasher at the venue door is answered by per-guest single-use door codes, verified atomically.

Crucially, every network-facing guarantee must hold in the database itself (Row-Level Security), so a modified client changes nothing. The UI is a convenience; Postgres is the boundary.

02Five layers, one story

Security here is not a module — it's a stack of independent layers, each answering a different attacker, each surviving the failure of the others.

L5L4 L3L2L1 THE FRONT DOOR — REGISTRATION THE GUEST WALL — ANON TIER THE ALIAS ENGINE — IDENTITY THE DEVICE — APP-LOCK THE PHYSICAL DOOR — CHECK-IN Email verification · disposable-domain blocklist · ~2 signups/IP/day · never asks a phone number No account → venues, venue events and attendee counts only. Never a user profile, never a name. Fresh alias + generated avatar per event · encrypted mapping, key outside the DB · unfindable by search Local PIN (salted hash, secure enclave, never synced) · biometric unlock · blur cover over app-switcher snapshot Per-guest single-use 6-char code · atomic check-in (FOR UPDATE) · throttled, never hard-locked · alias shown at door STOPS: BOTS ·FAKE ACCOUNTS STOPS: ANONYMOUSBROWSING / SCRAPING STOPS: STALKERS ·CROSS-EVENT TRACKING STOPS: WHOEVER HOLDSYOUR UNLOCKED PHONE STOPS: GATECRASHERS ·PASS-BACK AT THE DOOR

Fig. 1 — The defense stack. L3 (amber) is the load-bearing layer: every read path resolves identity through it, enforced by RLS in the database.

03The eight invariants

The security spec (doc 18) reduces the whole design to eight invariants. They are merge gates, not aspirations — an adversarial test suite runs them in CI against every read path as ten different actors (guest, stranger, friend, blocked, host, door staff…), and a change that breaks one does not merge. Not behind a flag, not "temporarily".

#Invariant
I1A stranger cannot resolve a private user's real identity through any read path, at any event.
I2A private user's aliases cannot be linked to each other across events.
I3A guest sees only venue profiles, venue-hosted approved public events, their posts, and attendee counts. Never a user profile, never a name.
I4Private users are unfindable by search — the "hidden user" response is byte-identical to "no such user", with no timing difference. A distinguishable response is a probe oracle.
I5Every guarantee holds in the database. A modified client changes nothing.
I6The service_role key never reaches a client bundle, a commit, or a log.
I7Every alias reveal is written to an append-only audit log — including passive reads.
I8Media is never reachable by a durable unauthenticated URL — signed, expiring links only; EXIF stripped on upload.
04The app flow

The flow reads left to right: launch, the wall, the core loop, the physical door. Each dashed line is a security boundary — a tier you cannot cross without earning it (registering, joining, being a friend, being the host).

LAUNCH GUEST TIER REGISTERED — THE CORE LOOP THE DOOR A1 SPLASH cold start A8 APP-LOCK PIN / biometric · local only blur cover on background SIGNED IN? route by session A2 GUEST LANDING venue shop-window counts only · read only D3 VENUE PROFILE address · rating · events A3 SIGN UP email or OAuth · never a phone disposable-mail + IP checks → verify → region A7 PRIVACY SETUP the private/public choice, taught before the feed guest sign up CTA THE SIGN-UP WALL · I3 B1 HOME FEED upcoming events, city-scoped nudged by friends C1 EVENT DETAIL public → real names private → per-event alias guests → count only C4 CREATE EVENT public / friends / invite C3 EVENT CHAT joined only · no backfill before your joined_at E5 FRIEND REQUEST to an alias — consent is the only unmask path C7 MY DOOR CODE personal 6-char ticket register host side join met someone joined HOST / DOOR STAFF ONLY C6 DOOR CHECK code → atomic check-in, once throttled, never hard-locked private guest shows as alias even to staff show code EVERY BOUNDARY ABOVE IS ENFORCED IN POSTGRES RLS (I5) — THE CLIENT ONLY DRAWS IT. AMBER NODES = WHERE THE SECURITY MODEL BECOMES VISIBLE UI.

Fig. 2 — App flow with security boundaries. Dashed verticals are tier walls; deep links can never cross one on their own — every state-changing route stops at a confirmation screen.

05Screens — where the model becomes UI

Live mockups-v2 embeds, grouped by the security story each one carries. Every frame is the real HTML mockup — open any of them full-size.

The wall — guest tier & the front door
A2Guest landingOPEN ›

The deliberately narrow shop-window (I3): venues, venue events, photos and a bare attendee count. No user is ever rendered — not by name, not by alias. The wall doubles as the conversion funnel: "42 going" without who is the sign-up pitch.

A3Sign upOPEN ›

Data minimization at the door: email or OAuth, never a phone number. Behind it, layered anti-abuse — disposable-domain blocklist (synced, not hand-curated), ~2 signups per IP per day stored as salted hashes and purged in 48h.

A7Privacy setupOPEN ›

The private/public choice is taught during onboarding, not buried in settings — the alias model is a selling point, and an uninformed default would betray it. Private means unlinkable to strangers, not hidden from friends.

Identity — the alias engine
C1Event detailOPEN ›

The atomic unit. One attendee list, three renderings: friends resolve real people, strangers see per-event aliases with generated avatars, guests see a number. The resolver lives in the database (I1, I5) — the screen just draws what it's given.

C3Event chatOPEN ›

Join = enter the chat. Aliases are stable within the event, so mute/ban still works; realtime subscriptions are authorized separately from table RLS, and a late joiner gets no backfill older than their join watermark.

E5Alias friend requestOPEN ›

The one sanctioned path through the alias: a friend request to the alias itself, unmasking only on mutual acceptance. Two pending requests to the same hidden person must be indistinguishable from requests to two different people — even a "duplicate" error would be a correlation oracle.

D2User profile (other)OPEN ›

A private profile is unfindable by search (I4) and shows a stranger nothing — with a response identical to "no such user". Public profiles are findable by design: protection is opt-in.

The device — someone is holding your phone
A8Lock screenOPEN ›

The network can't help when the attacker holds your unlocked phone at the party. PIN or biometric on launch and on foregrounding; a blur cover hides the app-switcher snapshot. Progressive backoff on failures, no wipe.

G3App-lock setupOPEN ›

The PIN is stored as a salted hash in the OS secure enclave and never leaves the device — so there is deliberately no "reset via email" backdoor. Forgot it? Sign out and re-authenticate; the account credential is the real key.

The physical door — bridging RSVP to reality
C7My door codeOPEN ›

Every guest gets their own 6-char alphanumeric code — a personal ticket, not a shared password. Single-use by design, which kills pass-back at the door.

C6Door checkOPEN ›

The host — or delegated door staff with strictly door-scoped rights — verifies the code. Check-in is atomic (FOR UPDATE): a code marks used exactly once even under simultaneous scans. ~10 wrong attempts throttles but never hard-locks — a queue at a club door can't be bricked. A private guest shows as their alias, even to staff.

B1Home feedOPEN ›

The everyday screen, quietly shaped by the model: city-scoped (a region setting, not live GPS), forward-looking, friend-nudged. Location privacy by never collecting location.

06What it doesn't promise

A security-centered design is defined as much by its stated limits as by its guarantees — each one is written down so it's never mistaken for a gap, and so the Privacy Policy can describe it honestly.

Content identifies people. A face in a photo, a distinctive writing styleCannot be solved technically — the alias protects metadata, not content.
Small events shrink the crowd an alias hides inAccepted — the alias still doesn't reveal identity.
A stolen backup reveals who exists and who attended whatOnly the alias mapping is encrypted; said plainly in the policy. The key lives outside the database precisely so backups don't carry it.
The app PIN is not forensics-gradeIt stops the person at the party, not a lab. Full-disk encryption is the OS's job.
No root detection, cert pinning, screenshot blockingDefeatable theater — cost exceeds benefit when the database is the boundary.
Offline check-in can double-enterDetected on sync and reported, never silently prevented.
§Behind this review

Distilled from nineteen internal design documents — concept, guest tier & RLS amendments, anti-abuse registration, app-lock PIN, door-staff delegation, the security requirements & adversarial test spec, and alias friend requests — plus the full mockups-v2 screen set. Every frame above embeds the real HTML mockup; diagrams are hand-drawn inline SVG.

A work sample by David Mkrtchian — product design, threat modelling, and system architecture for ShowUP, 2026.