ADR-0004: Profiles are never public
| Field | Value |
|---|---|
| Status | Proposed |
| Date | 2026-09-24 |
| Deciders | Wes Huber |
| Related | ADR-0005 |
Context
Vegan Grove's members are activists. A public profile page is a directory of activists, and a directory is the single artifact the threat model most wants to prevent. The Trick Book has public profiles, a riders directory, a GET /users?email= lookup, and a GET /user/:id that returns any user's full document to any signed-in caller. Wes also wants a feed that works like a normal social app, which normally implies public profiles.
Decision
- No public profile route exists on the web or in the app. No
GET /users/:id. No search by handle unless the target has setdiscoverableon, and even then the result is "exists, send invite", not a profile. - Friends connect by invite code (a short code or QR shown in person), with a 7-day expiry and a use limit.
- A handle is rendered only in contexts where the viewer is allowed to see the underlying object: a friend's post, a Grove post to a fellow member, an attendee to that event's organizer, a review where the author opted to show it.
- Public posts (opt-in, see Feed) show a handle and avatar. Tapping the handle opens a public-posts view: that handle's public posts and nothing else. No bio, area, friends, counts, or join date. This view is a list of posts, not a profile, and the API route is
GET /api/handles/:handle/posts, which returns 404 when the handle has no public posts, so the route cannot be used to test whether a handle exists. - Admin tooling never lists members beyond the handle and never exports.
Alternatives considered
- Private-by-default profiles with a public toggle. Common in social apps. Rejected because the toggle would be used, and one public activist profile with a location and a friends list is the failure mode.
- Public profiles for organizers only. Organizers are the most exposed people in the movement already. Rejected; organizers are represented by their Grove or organization page, which carries no personal detail.
Consequences
Positive
- The product cannot leak a member list through its own UI.
- The reconciliation with the feed (opt-in public posts, a posts view instead of a profile) keeps the normal social loop without the directory.
Negative
- Finding friends is harder than in other apps. Mitigation: invite codes at events, and Groves as the place you meet people.
- A member who posts publicly under a handle they use elsewhere is linkable. The app warns once when public posting is enabled.