Privacy Controls: "Go It Alone" & Private Mode -- Feature Exploration & PRD
Product: Objectuve (codename: Enkidu) Feature: Privacy Controls (Roadmap #29) Status: Exploration / Design Date: April 2026
1. Problem Statement
Objectuve markets itself as an "anti-social" goal-tracking app: sessions should last roughly 10 minutes, and social features exist strictly for accountability, not entertainment. Yet today the app makes no distinction between users who want community accountability and users who want pure solo tracking. Every user sees the same Community tab, Feed tab, ally activity, and social notifications regardless of preference.
Three problems:
Unwanted social pressure. Users who came for personal streak-tracking see community recommendations, ally activity feeds, and social notifications they never asked for. For users managing sensitive goals (health, finance, mental wellness), the mere visibility of social features creates anxiety about accidental exposure.
No way to reduce discoverability. A user who joins one community or accepts one ally connection becomes searchable, appears in leaderboards, and can receive ally requests. There is no mechanism to participate selectively without appearing on everyone's radar.
Coach misalignment. The AI coach currently gives community-related suggestions to all users. A user with no interest in communities receives irrelevant advice.
Privacy controls are not a niche request -- they are a prerequisite for the app's own brand promise. If Objectuve is genuinely "anti-social by design," it must let users opt out of the social layer entirely, or at least control their visibility within it.
2. Goals & Success Metrics
Goals
- Let users remove all community/social UI so the app feels like a pure personal tracker
- Let users hide themselves from search, discovery, and leaderboards while retaining direct-invite participation
- Provide username-based user search for intentional ally connections
- Ensure the AI coach respects the user's privacy posture
- Preserve all underlying data so toggling off restores everything instantly
Success Metrics
| Metric | Target | Measurement |
|---|---|---|
| Go It Alone adoption | 10-15% of MAU enable within 60 days | users.where(go_it_alone: true).count / MAU |
| Private Mode adoption | 5-10% of MAU enable | users.where(private_mode: true).count / MAU |
| Community engagement preservation | Private Mode does not reduce community DAU by >2% | Compare community DAU pre/post launch |
| Username search ally conversion | 20%+ of new ally connections from search | Track source: 'username_search' on UserAlly creation |
| Retention impact | Go It Alone users retain at >= same rate as general population | Cohort analysis at 30/60/90 days |
| Toggle reversal rate | <30% toggle back within 7 days | Track toggle timestamps |
3. User Stories
- As a user who prefers solo tracking, I want to toggle "Go It Alone" in Settings so that all community UI disappears and I see only my goals, streaks, and personal progress.
- As a Go It Alone user, I want my existing community memberships and ally connections preserved (but hidden) so I can restore them later without re-joining.
- As a privacy-conscious user, I want to enable "Private Mode" so that my profile does not appear in search results, ally request discovery, or public leaderboards.
- As a Private Mode user, I want to still join communities and accept ally requests via direct invite so I retain selective social engagement.
- As a user looking for a specific person, I want to search by username so I can find and connect with people I already know.
- As a Private Mode user, I expect that username search does NOT return my profile, so my discoverability preferences are respected.
- As a Go It Alone user, I want the AI coach to stop making community-related suggestions and focus exclusively on personal goal strategies.
- As a Go It Alone user, I want social notifications paused so I am not reminded of features I have hidden.
- As a user who toggles off Go It Alone, I want a "welcome back" confirmation and all my previous data restored immediately.
- As a new user during onboarding, I want the option to skip community features entirely and start in Go It Alone mode.
4. Feature Design
4a. "Go It Alone" Mode
What gets hidden
Every social/community UI element disappears:
Navigation:
- "Community" tab filtered from
visibleTabsinBottomTabBar.vue - "Feed" tab filtered from
visibleTabs
Routes:
/communitiesand/communities/:idredirect to/dashboard/feedredirects to/dashboard
Dashboard:
allyFeedItemsreturns empty arraysocialNotificationsreturns empty array- Any "Join a community" or social CTA cards hidden
Communities page: Inaccessible via route guard redirect.
Activity Feed: Inaccessible via route guard redirect.
AI Coach:
- Community page context and suggested prompts removed from
useAiCoach.ts - Backend
CoachServicereceivessolo_mode: true, adding "Do not suggest community engagement, ally features, or social activities" to prompts
AI Insights:
communitiesInsightPage excluded- Dashboard insights never reference community engagement
Notifications: Social kinds paused at delivery (see Section 8).
Goal Detail:
- "Share to community" actions hidden
- Encouragement counts on own events still visible
What remains visible
- Dashboard (goals, streaks, habits, momentum bar, XP, level)
- Goals list and individual goal detail
- All goal tracking features (check-ins, milestones, events)
- Achievements/badges page
- Settings (including privacy toggles)
- AI Coach (personal goal advice only)
- Personal notifications (badge, general, reminder, past_due, ai_check_in)
Technical approach: Frontend hiding, NOT backend filtering
Go It Alone is a frontend presentation concern. The backend still returns all data normally. The frontend reads user.goItAlone from the GraphQL user query and conditionally hides UI elements.
Rationale:
- Data integrity: memberships, posts, ally connections remain untouched
- Instant restoration: toggling off restores everything with no re-fetching
- No backend complexity: no conditional includes in social queries
- Single source of truth: the
go_it_aloneboolean drives all frontend gating
Exception: AI Coach -- the backend CoachService must receive Go It Alone status to adjust prompt content.
What happens to existing data
Nothing. Community memberships, ally connections, posts, reactions, and comments all remain in the database. They are simply not rendered. Toggling off restores everything instantly.
4b. Private Mode
What "not discoverable" means
A Private Mode user is excluded from:
- Username search results --
searchUsersquery filters outprivate_mode: true - Recommended allies -- "People you may know" features exclude private users
- Public leaderboards -- Community point leaderboards exclude private users
- Community member lists for non-members -- Private users don't appear when browsing
- Non-ally activity feeds -- Private users' events don't appear for non-allies
- Trending/featured contributor lists -- Any list surfacing users by activity excludes private users
What Private Mode users CAN do
- Join communities (via URL/invite or direct browsing)
- Accept ally requests arriving via direct invite
- Post in communities they belong to
- Appear to existing accepted allies (pre-Private Mode connections unaffected)
- Send ally requests to others
- Use all goal-tracking features normally
Queries needing filters
| Query/Feed | Filter needed |
|---|---|
searchUsers (new) | WHERE private_mode = false |
communityMembers resolver | Exclude private users from non-member views |
| Recommended communities (mutual allies count) | Exclude private users |
userAllies resolver | No change -- already accepted connections |
allyActivityFeed resolver | No change -- scoped to accepted allies |
BuildUnifiedFeed | No change -- scoped to accepted allies |
Existing ally connections
Private Mode does NOT sever existing connections. If User A and User B are accepted allies and User A enables Private Mode, User B still sees User A. Principle: Private Mode controls discovery, not existing relationships.
Interaction with goal visibility tiers (v4.61)
No interaction. Private Mode governs discoverability — whether a user can be found by search, recommendations, or leaderboards. Goal visibility (public/allies/private, added in v4.61 Allies-Only Goal Visibility) governs whether an already-known accepted ally can see a specific goal. A Private Mode user's allies-only goals behave identically to a non-Private-Mode user's: any of their existing accepted allies can see, encourage, comment on, react to, and follow an allies-only goal exactly as they would if Private Mode were off. Enabling Private Mode does not hide, and does not need to hide, a goal from allies the owner has already accepted — those connections aren't "existing ally connections" being newly exposed; they were already visible to each other before Private Mode was ever considered, per the principle above. See Allies § Goal visibility tiers.
4c. User Search by Username
Where it lives
- Communities page -- Toggle within existing search bar area to switch between "Search communities" and "Search users"
- Add Ally flow -- Primary mechanism for future ally requests (Roadmap #19)
Result display
- Profile photo, display name,
@username - Supporter badge (if applicable)
- Mutual ally count (if > 0)
- Action button ("Add Ally" / "Pending" / "Already Allies")
Private Mode interaction
searchUsers scope includes WHERE private_mode = false. Private users are completely invisible. No indication that a private user exists.
Matching strategy
Prefix matching on username, case-insensitive. PostgreSQL ILIKE 'query%' using existing index_users_on_username. Minimum query: 3 characters.
Rate limiting
- Client-side: 400ms debounce
- Server-side: 20 requests/minute/user
- Max results: 20 users
4d. Interaction Between Modes
Both can be enabled simultaneously. The toggles are independent and orthogonal:
- Go It Alone = UI visibility control (hides social from the user's own view)
- Private Mode = discoverability control (hides the user from other people's views)
A user might want:
- Private ON, Go It Alone OFF: participates in communities but can't be searched
- Go It Alone ON, Private OFF: hides social UI but remains discoverable
- Both ON: maximum privacy
- Both OFF: default social experience
When enabling Go It Alone, suggest (not auto-enable) Private Mode via secondary prompt.
5. UX Flow
Settings Page Layout
New "Privacy" tab in Settings (after Supporter, before Account):
Profile | Supporter | Privacy | Account | FeedbackSection 1: "Go It Alone" mode
- Card with
Usericon, title, description: "Hide all community features and focus purely on your goals." - Toggle switch (right-aligned)
- Secondary text on enable: "Your communities, allies, and posts will be hidden but not deleted."
Section 2: "Private Mode"
- Card with
EyeOfficon, title, description: "Hide your profile from search results, recommendations, and public leaderboards." - Toggle switch
- Subtitle: "You can still join communities and accept ally requests via direct invite."
Section 3: informational
- Muted text: "Your data is never deleted by these settings. Toggling off restores everything instantly."
First-Time Toggle Activation
Go It Alone: Confirmation dialog (matching existing Delete Account pattern):
- Header: "Go It Alone?"
- Message: "Community features will be hidden. Your memberships, allies, and posts are kept safe -- toggle off anytime to bring them back."
- Buttons: "Cancel" | "Enable"
Private Mode: No dialog. Toast: "Private Mode enabled. You're hidden from search and discovery."
Immediate Effects on Toggle
Go It Alone ON:
updateUsermutation fires withgoItAlone: true- Apollo cache updates optimistically
- BottomTabBar re-renders without Community and Feed tabs
- If on
/communitiesor/feed, navigate to/dashboard - Social notification badges disappear
- Toast: "You're now in solo mode"
Go It Alone OFF:
updateUsermutation fires withgoItAlone: false- Community and Feed tabs reappear
- Apollo refetches community and feed queries
- Previously paused notifications appear
- Toast: "Welcome back! Your communities and allies are restored."
"Welcome Back" Experience
Dismissible dashboard card after toggling off: "Welcome back to the community! You have [X] unread notifications and [Y] new posts." CTA: "View Activity" (navigates to /feed). Auto-dismiss after 10 seconds.
6. Data Model Changes
New Columns on users Table
add_column :users, :go_it_alone, :boolean, default: false, null: false
add_column :users, :private_mode, :boolean, default: false, null: false
add_column :users, :go_it_alone_enabled_at, :datetime, null: true
add_column :users, :private_mode_enabled_at, :datetime, null: true
add_index :users, :go_it_alone
add_index :users, :private_modeThe *_enabled_at timestamps support analytics and "welcome back" notification counting.
Model Scopes
scope :discoverable, -> { where(private_mode: false) }
scope :solo_mode, -> { where(go_it_alone: true) }GraphQL Changes
UserType: Add go_it_alone (Boolean) and private_mode (Boolean) fields.
UpdateUser mutation: Add go_it_alone and private_mode optional arguments.
New query: searchUsers
- Arguments:
query(String, required),limit(Integer, default: 20) - Returns:
[UserSearchResultType]
New type: UserSearchResultType
public_id,username,first_name,last_name,photo,is_supporter,mutual_ally_count,ally_status
Frontend GraphQL
USER_QUERY: AddgoItAloneandprivateModefieldsUPDATE_USER_MUTATION: Add$goItAlone: Booleanand$privateMode: Booleanarguments- New
SEARCH_USERS_QUERYconstant
7. How Privacy Affects Every Feature
Dashboard
| Element | Go It Alone | Private Mode |
|---|---|---|
| Greeting, streaks, momentum bar | No change | No change |
| Social notifications section | Returns empty | No change |
| Ally feed items | Returns empty | No change |
| Notification bell count | Excludes social kinds | No change |
| AI insight cards | Never reference communities | No change |
| Getting Started cards | No "join a community" CTA | No change |
Goals List and Detail
| Element | Go It Alone | Private Mode |
|---|---|---|
| Goal list/grid | No change | No change |
| Check-ins, milestones | No change | No change |
| "Share to community" button | Hidden | No change |
| Encouragement display on own events | Still visible | No change |
Communities Tab/View
| Element | Go It Alone | Private Mode |
|---|---|---|
| Tab in BottomTabBar | Hidden | No change |
Route /communities | Redirects to /dashboard | No change |
| Member lists | N/A | Private user excluded from non-member browse |
Unified Feed / Activity Feed
| Element | Go It Alone | Private Mode |
|---|---|---|
| Feed tab in BottomTabBar | Hidden | No change |
Route /feed | Redirects to /dashboard | No change |
| User's events in others' feeds | Still appear | Still appear for accepted allies |
Ally Activity
| Element | Go It Alone | Private Mode |
|---|---|---|
| AllyActivityFeed on Communities page | N/A (page hidden) | No change |
| Ally feed items on Dashboard | Returns empty | No change |
Encouragements/Reactions
| Element | Go It Alone | Private Mode |
|---|---|---|
| Incoming notifications | Paused | No change |
| Existing encouragements on goals | Stored; visible on toggle-off | No change |
Notifications
| Element | Go It Alone | Private Mode |
|---|---|---|
| Paused kinds | encouragement, comment, follow, reaction, feedback | None |
| Unaffected kinds | badge, general, reminder, past_due, ai_check_in | All |
| Push notifications | Social pushes suppressed | No change |
| Notification history | Stored; visible on toggle-off | No change |
AI Coach
| Element | Go It Alone | Private Mode |
|---|---|---|
| Community suggested prompts | Removed | No change |
| Backend advice/insight prompts | "Do not suggest community features" | No change |
| Check-in prompt job | Context excludes community data | No change |
Achievements/Badges
| Element | Go It Alone | Private Mode |
|---|---|---|
| Badge list and progress | No change | No change |
| Community-related badges | Displayed but not progressable | No change |
Onboarding
| Element | Go It Alone | Private Mode |
|---|---|---|
| Community wizard slide | Add deferred prompt (not during) | No change |
Accountability Partners (Roadmap #19)
| Element | Go It Alone | Private Mode |
|---|---|---|
| Partner UI | Hidden | No change |
| Existing partnerships | Preserved but hidden | No change |
| Partner discovery/search | Hidden | Private users excluded |
Per-goal accountability-partner sharing (v4.10)
Shipped alongside — but architecturally distinct from — the Go It Alone / Private Mode toggles above. Where the table just above describes how partner UI responds to those two modes, this describes the underlying goal visibility model a confirmed accountability partner sees, and it coexists with this PRD's explicit-consent posture rather than contradicting it:
- Public goals (
private: false) are partner-visible by default, no separate opt-in required. A per-goal opt-out (excluding one public goal from partner visibility) is planned but not yet shipped — there is noAccountabilityPartnerGoalExclusionmodel or table today, so every public goal is currently visible to a confirmed partner. See Accountability partners § Goal exclusions (planned, not yet shipped). - Private goals (
private: true) stay hidden from the partner by default — the owner must explicitly opt in per goal via theshared_with_partnerflag (GoalPartnerSharingCard.vueon Goal Detail). No retroactive exposure: a private goal that predates this feature does not become visible on its own. Reversible at any time, and only the goal's owner can change the setting. - Both rules are unified at read time by the single
Goal.partner_visiblescope (private = false OR shared_with_partner = true), consumed identically by the profile'spartnerGoalsfield and the missed-day alert job — sharing a private habit also makes it eligible for a missed-day alert to the partner. - Why this doesn't weaken the "nothing exposed without explicit action" posture this PRD is built on: every partner-visibility rule still traces back to an explicit user action — making a goal public, or flipping
shared_with_partner— never to a default-on state or an inferred consent.
Full mechanics, source files, and the user-facing sharing flow: Accountability partners § Per-goal partner sharing.
8. Notification Pause System
Paused Kinds in Go It Alone
Paused: encouragement (0), comment (3), follow (6), reaction (8), feedback (9)
Not paused: badge (1), general (2), reminder (4), past_due (5), ai_check_in (7)
Private Mode does not pause any notifications.
Implementation: Filter at Delivery, Not Generation
Notifications are still generated normally. The pause happens at push delivery and frontend display.
Rationale: Notifications generated during Go It Alone are preserved. When toggling off, the full history appears. No "replay" logic needed. The "welcome back" card can count accumulated social notifications.
Push suppression: SendPushNotification adds early return when user.go_it_alone? and notification kind is social.
WebSocket suppression: UserNotification.trigger_subscription checks Go It Alone and skips trigger for social kinds.
Frontend filtering: Notification list and bell count filter out social kinds when user.goItAlone is true.
Accumulated Notifications on Toggle-Off
They remain as acknowledged: false records. Removing the frontend filter on toggle-off surfaces them all.
9. Edge Cases & Open Questions
Edge Cases
| # | Scenario | Resolution |
|---|---|---|
| EC-1 | User enables Go It Alone while on /communities | Router guard detects change, redirects to /dashboard |
| EC-2 | Direct ally invite link while in Go It Alone | Link works. Prompt: "You have Go It Alone enabled. Accept anyway?" Connection hidden until mode off |
| EC-3 | Sole community admin enables Go It Alone | Warning: "You're the only admin of [community]. Consider assigning another before going solo." |
| EC-4 | Private user already on a leaderboard | Entry removed on next query. Other positions shift |
| EC-5 | @mention in community post while Go It Alone | Notification stored, delivery paused. Visible on toggle-off |
| EC-6 | Pending ally request + Private Mode enabled | Request remains pending. Requester can't find user in search. If user accepts via notification, connection forms normally |
| EC-7 | Admin queries | All users visible regardless of privacy settings |
Open Questions
- Should Go It Alone hide Feed or repurpose as personal-only feed? Recommend hide for v1.
- Should Private Mode hide user from fellow community members? No -- membership implies consent. Exclude from public-facing counts only.
- Onboarding privacy prompt? Defer. After 7 days of no community engagement, show one-time prompt.
- Naming: "Go It Alone" matches the brand's direct tone. "Solo Mode" or "Focus Mode" are weaker (Focus Mode conflicts with existing dashboard mode).
- Rate limiting calibration: 20/min for search -- monitor post-launch.
10. Implementation Phases
Phase 1: Data Model & Backend Foundation (1 sprint)
- Migration: add
go_it_alone,private_mode,*_enabled_atcolumns with indexes - Model scopes (
discoverable,solo_mode) UpdateUserinteraction: accept new fields with timestamp tracking- GraphQL: fields on
UserType, arguments onUpdateUsermutation - Specs: model specs, interaction specs, factory updates
Phase 2: Go It Alone Frontend (1 sprint)
- Privacy tab in Settings with toggle cards
- BottomTabBar: filter
visibleTabsbased onuser.goItAlone - Router guards: redirect social routes
- Dashboard: conditional rendering for ally feed, social notifications, community CTAs
- Confirmation dialog on first enable
- GraphQL query/mutation updates
- Storybook stories for privacy toggles
- Vitest specs for conditional rendering and guards
Phase 3: Notification Pause (1 sprint)
SendPushNotification: early return for social kindsUserNotification.trigger_subscription: skip WebSocket for paused kinds- Frontend notification filtering
- "Welcome back" dashboard card
- Specs for push suppression and filtering
Phase 4: Private Mode Backend (1 sprint)
searchUsersquery resolver withWHERE private_mode = falseUserSearchResultTypeGraphQL type- Community member list filtering for non-members
- Leaderboard exclusion
- Resolver and search filtering specs
Phase 5: Username Search Frontend (1 sprint)
UserSearchInput.vuewith debounced input and resultsUserSearchResult.vuedisplaying profile and ally status- Communities page integration (community/user search toggle)
- Ally request button (dependent on Roadmap #19 prerequisite)
- Client-side rate limiting, loading/error states
- Storybook stories and unit tests
Phase 6: AI Coach Privacy Awareness (0.5 sprint)
CoachServicecontext injection withsolo_modeflaguseAiCoachcomposable: strip community prompts when Go It Alone activeuseAiInsights: skip community insightsGenerateAiCheckInPromptsJob: include solo mode in context- Coach service specs
Phase 7: Polish & Analytics (0.5 sprint)
- Admin panel: privacy flags per user, aggregate metrics
- Analytics events: toggle-on/off, time-in-mode, search usage
- Onboarding integration: deferred Go It Alone prompt after 7 days
- Edge case handling: sole admin warning, direct invite behavior
- QA and regression testing
11. Design Suggestions
Privacy Tab
Calm, neutral palette. Toggles use standard primary color when enabled -- not red or warning colors. Card layout mirrors existing Settings cards.
Tab Removal Animation
When Community/Feed tabs disappear from BottomTabBar: 300ms opacity transition. Remaining tabs redistribute with transition-all duration-300.
"Welcome Back" Card
Full-width dashboard card with warm gradient border (matching existing SupporterStreakNudge pattern). Users icon, accumulated notification count, "View Activity" CTA. Auto-dismiss after 10 seconds.
Username Search Results
Compact horizontal list rows: 40px avatar, name/@username, mutual ally badge, action button. Standard UiInput with Search icon prefix. Empty state: "No users found matching '@{query}'." -- never reveals private users exist.
Privacy Toggle Cards
┌─────────────────────────────────────────┐
│ 👤 Go It Alone [ON] │
│ Hide all community features and │
│ focus purely on your goals. │
│ │
│ Your data is preserved. Toggle off │
│ anytime to bring everything back. │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 👁 Private Mode [OFF] │
│ Hide your profile from search, │
│ recommendations, and leaderboards. │
│ │
│ You can still join communities and │
│ accept ally requests via direct invite. │
└─────────────────────────────────────────┘Critical Files
| File | Change |
|---|---|
rails_api/app/models/user.rb | Add go_it_alone, private_mode fields, scopes |
rails_api/app/interactions/user_identity/update_user.rb | Accept and persist privacy fields |
rails_api/app/graphql/types/user_type.rb | Expose privacy fields |
rails_api/app/graphql/mutations/update_user.rb | Add privacy arguments |
rails_api/app/graphql/types/query_type.rb | Add searchUsers query |
rails_api/app/interactions/social/send_push_notification.rb | Go It Alone push suppression |
rails_api/app/models/user_notification.rb | WebSocket suppression for social kinds |
rails_api/app/services/ai/coach_service.rb | Solo mode prompt injection |
ionic_frontend/src/components/BottomTabBar.vue | Filter tabs by goItAlone |
ionic_frontend/src/router/index.ts | Route guards for social routes |
ionic_frontend/src/views/Settings.vue | New Privacy tab |
ionic_frontend/src/views/Dashboard.vue | Conditional social rendering |
ionic_frontend/src/composables/useAiCoach.ts | Strip community prompts |
ionic_frontend/src/composables/useAiInsights.ts | Skip community insights |
ionic_frontend/src/constants/graphql/user.js | Add privacy fields to queries/mutations |
Last updated: 2026-09-15 — added §4b "Interaction with goal visibility tiers (v4.61)": Private Mode and the v4.61 allies-only visibility tier don't interact — the former is discoverability, the latter is per-goal ally access. (Previously updated 2026-07-21 for §7, per-goal partner sharing v4.10.)