Skip to content

Fork Changes — Features & Fixes Not in Upstream

This page documents all features, bug fixes, and improvements added in the tribixbite/stoat-android fork that are not present in the upstream stoatchat/for-android repository.

Channel and server-wide message search with comprehensive filtering, not available in upstream.

  • Search API route (POST /channels/{channelId}/search) with full parameter support: query, limit, before, after, sort, include_users, pinned
  • Server-wide search — iterates all text channels in a server, aggregates results with progress indicator showing current channel (X/Y), error count for inaccessible channels. Accessible from server context sheet (⋮ menu → “Search Server”)
  • Search UI screen with text input, debounced queries (400ms), and paginated results
  • Search filters: sort order (Relevance/Latest/Oldest), pinned-only mode, date range (before/after), mention filter, attachment filter (client-side)
  • Collapsible filter panel with “Clear All” button and result count display
  • Infinite scroll pagination using cursor-based before parameter
  • Search result navigation: tap a result to jump to the message in channel context
  • Channel labels on server search results showing which channel each message is from
  • MongoDB $text search syntax support: OR matching, "exact phrases", -negation, stemming

Server moderation features with full UI, only API stubs existed upstream.

  • Kick member with confirmation dialog — DELETE /servers/{id}/members/{userId}
  • Ban member with optional reason field — PUT /servers/{id}/bans/{userId}
  • Unban memberDELETE /servers/{id}/bans/{userId}
  • Fetch ban listGET /servers/{id}/bans
  • Pin/unpin messagesPOST /channels/{id}/messages/{msg}/pin and DELETE .../pin
  • Bulk delete messages API route — DELETE /channels/{id}/messages/bulk

Complete server administration screens with permission-gated access.

  • Server Settings screen — edit name, description, icon upload, banner upload/remove via InlineMediaPicker with progress bar and Autumn upload
  • Role Management screen — create, edit (name, colour with hex preview, hoist toggle, rank editing), delete roles
  • Ban Management screen — view ban list with reasons, unban with confirmation
  • Create Channel screen — Text/Voice type selection, name, description, NSFW toggle (passes nsfw param to API)
  • Channel Permissions screen — full per-role permission overrides with tri-state toggles (Allow/Neutral/Deny), add role dialog, save to API
  • Member nickname edit — edit own or others’ nicknames (permission-gated)
  • Role assignment dialog — toggle roles per member with visual checkmarks
  • Server Settings entry point — accessible from server context sheet (long-press server)
  • 14 API routes: PATCH /servers/{id}, POST/PATCH/DELETE roles, PATCH /servers/{id}/roles/ranks, GET/DELETE bans, PATCH members, POST channels, PUT /channels/{id}/permissions/{roleId}, PUT /channels/{id}/permissions/default, plus uploadToAutumn() for icons and banners

Full default and per-role permission editor matching the web client UI, for both server-level and channel-level permissions.

  • Default Permissions screen — checkbox toggles for all 32 permission bits on the default role
  • Role Permissions screen — tri-state segmented buttons (Allow / Neutral / Deny) per permission
  • Channel Permissions screen — per-role permission overrides for individual channels, with add-role dialog and save to API
  • 5 categories: Admin (5), Members (8), Channels (6), Messaging (6), Voice (7)
  • API routes: PUT /servers/{id}/permissions/default, PUT /servers/{id}/permissions/{roleId}, PUT /channels/{id}/permissions/{roleId}, PUT /channels/{id}/permissions/default

Granular notification management beyond upstream.

  • Mute/Unmute server — toggle from server context sheet, synced to backend
  • Mute/Unmute channel — toggle from channel context sheet, synced to backend
  • Notification filtering — HandlerService checks mute state before displaying
  • Notification Settings screen — permission status, FCM registration status with retry, muted server/channel lists (showing names from cache), unmute buttons, reset
  • Placeholder detection — detects placeholder google-services.json at runtime and shows “Not available” instead of misleading retry loop

Complete native Android build toolchain for ARM64 devices — enables building the app directly on Android phones.

  • Custom build-and-install.sh script handling ARM64 AAPT2, JVM tuning, and ADB install
  • x86_64 AAPT2 wrapper using proot + qemu-x86_64 for SDK 36 resource compilation
  • Automatic ADB wireless device discovery and APK installation
  • JVM memory limits tuned for mobile device constraints

Automated build and release system.

  • GitHub Actions workflow builds debug APK on every push to dev
  • Automatic GitHub Release creation with downloadable APK artifacts
  • Placeholder google-services.json generation for CI builds
  • SDK 36 + build-tools 35.0.0 setup
  • Fixed background disconnect: app now properly reconnects WebSocket when resuming from background after Android kills the connection
  • Reconnection uses exponential backoff to avoid hammering the server
  • Fixed socket timeout: increased from 30s to 60s for large channel searches that take 3-30s+
  • Request timeout cap at 75s prevents Ktor’s 5x retry from hanging for 5+ minutes
  • Fixed wildcard/empty query bug that sent malformed requests
  • Fixed serialization errors when API returns bare Message[] vs {messages, users, members} response format
  • Added HttpTimeout per-request overrides since search is much slower than other API calls
  • FCM onNewToken ANR fix: replaced runBlocking with CoroutineScope(SupervisorJob() + Dispatchers.IO) to prevent app-not-responding on token refresh
  • FCM error handling: subscribePush() no longer silently swallows registration failures
  • Crash fix: bitmap loading in HandlerService wrapped with timeout and fallback to default icon
  • Error message display: push notification errors now surface to the user instead of silent failure
  • Manage Notifications button added to settings for direct access to system notification settings

Full account settings screen with API integration.

  • Account info displayGET /auth/account/ with email, MFA status
  • Change emailPATCH /auth/account/change/email with password confirmation
  • Change passwordPATCH /auth/account/change/password with confirm + mismatch check
  • Disable accountPOST /auth/account/disable with confirmation dialog
  • Delete accountPOST /auth/account/delete with danger confirmation
  • Resend verificationPOST /auth/account/reverify
  • Session management — list, rename, revoke sessions via GET/PATCH/DELETE /auth/session

Server emoji administration with upload and delete support.

  • Emoji list — shows all custom emoji for a server from cache, with names and creator info
  • Upload emoji — pick image, upload to autumn/emojis, create via PUT /custom/emoji/{id}
  • Delete emojiDELETE /custom/emoji/{id} with confirmation dialog
  • Permission-gated — requires ManageCustomisation or ManageServer

Server invite administration with list, copy, and delete.

  • List invitesGET /servers/{id}/invites showing invite links, creator, channel
  • Copy invite link — tap to copy stt.gg/{code} to clipboard
  • Delete inviteDELETE /invites/{code} with confirmation dialog
  • Create invitePOST /channels/{id}/invites

Shows shared connections in member context sheets.

  • Mutual friendsGET /users/{id}/mutual with resolved friend names from cache
  • Mutual servers — shows shared server names
  • Non-intrusive — displays between moderation actions and copy ID, only for other users

Full MFA setup and recovery code management, not available in upstream.

  • MFA Setup screen — accessible from Account Settings, shows TOTP + recovery status
  • Enable TOTP — multi-step dialog: password → secret display (copyable) → 6-digit verification → done
  • Disable TOTP — password confirmation → MFA ticket → disable
  • Recovery codes — view existing codes or regenerate new ones, copy-all button
  • 7 API endpoints: PUT /auth/mfa/ticket, POST/PUT/DELETE /auth/mfa/totp, POST/PATCH /auth/mfa/recovery

Active session management with full API integration.

  • List sessionsGET /auth/session/all
  • Rename sessionPATCH /auth/session/{id} with friendly name
  • Revoke sessionDELETE /auth/session/{id} with confirmation
  • Revoke all othersDELETE /auth/session/all danger zone action

Client-side member search in member list sheet.

  • Search field at top of member list sheet
  • Filters by username, display name, and server nickname
  • Clear button to reset search
  • Works for both server members and group DM participants

Advanced message management for moderators.

  • Remove all reactionsDELETE /channels/{id}/messages/{msg}/reactions
  • Bulk delete messages — dialog with preset counts (5/10/25/50/100), operates on cached messages
  • Both gated by ManageMessages permission

Collapsible Channel Categories (Upstream #51)

Section titled “Collapsible Channel Categories (Upstream #51)”

Tap category headers in the channel side drawer to collapse/expand.

  • Animated chevron indicator — rotates 90° between collapsed (→) and expanded (↓) states
  • Chevron direction fix — corrected rotation values so → means collapsed and ↓ means expanded (was inverted)
  • Client-side state — collapse state tracked per-category in Compose state map
  • Filtered channel list — collapsed categories hide their channels from the flat list

Tap a reply quote to scroll to the original message in the chat.

  • ActionChannel.ScrollToMessage action for cross-component communication
  • Animated scroll to target message in LazyColumn
  • Works for all loaded messages; messages not in view are scrolled to if in cache

Prevents duplicate message sends from rapid tapping.

  • isSendingMessage flag in ChannelScreenViewModel
  • Guard at top of sendPendingMessage() rejects rapid taps
  • Flag cleared in finally block after send completes or fails
  • Fixed DM/Saved Notes stuck loading — when app restarts on a DM or saved notes channel, the channel cache is empty until WebSocket Ready frame arrives
  • switchChannel() now watches the cache with snapshotFlow and retries loading once the channel appears
  • Previously left the screen permanently in Loading state

Message Send Race Condition (Upstream #17)

Section titled “Message Send Race Condition (Upstream #17)”
  • Fixed channel ID capturesendPendingMessage() captured channel?.id inside async viewModelScope.launch{} block, but channel could change if user switched channels during slow attachment upload
  • Channel ID now captured synchronously before the launch, preventing messages from being sent to the wrong channel
  • Fixed DM/Group DM mentions — autocomplete was blocked in DMs by an unnecessary serverId != null check; DM channels always have null serverId
  • Autocomplete.userOrRole() already handled null serverId correctly, so the guard was simply removed
  • Fixed friends list not updating — friends screen called FriendRequests.getXxx() multiple times per section, creating different list snapshots between header count and item access
  • Lists now computed once per recomposition above the LazyColumn, ensuring consistent data
  • Added stable key lambdas to LazyColumn items for proper item diffing and animation
  • Reads from mutableStateMapOf are automatically tracked by Compose — list updates reactively when userCache changes
  • Blocked swipe-to-replycanReply was hardcoded to true in RegularMessage
  • Now checks StoatAPI.userCache[author]?.relationship != "Blocked" before enabling swipe reply gesture

Swipe-to-Reply vs Code Block Scroll (Upstream #14)

Section titled “Swipe-to-Reply vs Code Block Scroll (Upstream #14)”
  • Fixed gesture conflict — swiping horizontally on a code block triggered swipe-to-reply instead of scrolling the code content
  • Changed supportSwipeReply from PointerEventPass.Main to PointerEventPass.Final so child scrollable elements process events first
  • Filters out consumed pointer changes before passing to swipe handler
  • Fixed duplicate reply banners — selecting “Reply” on the same message multiple times stacked multiple reply banners
  • Context menu reply path called draftReplyTo.add() directly, bypassing the duplicate check in addReplyTo()
  • Now uses addReplyTo() which enforces both deduplication and max-5 limit
  • Implemented ||spoiler|| syntax — double-pipe delimiters render as hidden text
  • New SpoilerParser sequential parser recognizes ||..|| in the markdown pipeline
  • Hidden state: text foreground matches dark background (invisible)
  • Tap to reveal: toggles spoiler visibility, re-tapping hides again
  • Per-spoiler state tracked independently in each text block
  • Added “Copy” and “Copy ID” buttons to the top level of the message context sheet
  • Previously buried inside the “Share” sub-menu requiring two taps
  • “Copy” copies message text content, only shown when message has text
  • “Copy ID” copies message ULID, always available

Long-Press Server Icon Context Menu (Upstream #20)

Section titled “Long-Press Server Icon Context Menu (Upstream #20)”
  • Added long-press to server icons in the sidebar drawer
  • Opens the existing ServerContextSheet (Mark Read, Mute, Settings, Leave)
  • Changed from .clickable to .combinedClickable, matching the pattern used for channel items
  • Fixed tap-to-copy on user profile cards — previously failed silently
  • Added try-catch around copyCard() so errors are displayed instead of swallowed
  • Always show “Copied” toast (system clipboard notification doesn’t work well for image URIs on Android 13+)
  • Always show Share/Copy buttons (previously hidden on Android 13+, leaving only invisible tap)
  • Fixed server clicks — discover page links to app.revolt.chat/invite/CODE which was silently blocked; now handles all known Revolt/Stoat domains (stoat.chat, stt.gg, rvlt.gg, app.revolt.chat)
  • Implemented mark-as-unread — replaced “coming soon” toast with working implementation
  • Generates synthetic ULID just before the selected message, acks channel to that ID
  • Channel appears unread from the selected message onwards in the channel list
  • Fixed “user not found” for users not yet in local cache when opening their profile
  • Now fetches from GET /users/{id} on cache miss, populating the cache for subsequent access
  • Replaced broken revoltchat.github.io links with fork documentation
  • Updated development setup instructions with Termux ARM64 build info
  • HTTP status code checks on all API responses (previously some routes ignored error status)
  • Pin endpoint method fix: corrected from PUT to POST per OpenAPI spec
  • WebP upload support: fixed content type detection for WebP image uploads
  • Search submit button properly triggers search on keyboard action
  • Reduced HTTP retry from 5 to 2 for server errors: 502 Bad Gateway caused ~62s exponential backoff hangs

Comprehensive technical documentation added (not present in upstream):

Cross-referenced against stoatchat/stoatchat backend (96 delta routes) and stoatchat/javascript-client-api (205 endpoints in routes.ts).

CategoryUpstreamThis Fork
Backend delta routes~5596 total, 96 implemented (100%)
Auth routes (authifier)~1027 implemented (login, MFA, sessions, account, logout)
Bots07 endpoints — create, list, fetch, edit, delete, invite, public info
Webhooks010 endpoints — create, list, fetch, edit, delete (auth + token), execute
SearchNoneFull (API + UI + filters + server-wide, MongoDB $text syntax)
Moderation UIPartialKick, ban, pin/unpin, reporting (full UI)
Server admin UINoneSettings, roles (hoist/rank), bans, channels (NSFW), permissions (channel-level), emoji, invites, banner, system messages
Account managementNoneView, edit email/password, delete/disable, MFA/TOTP, session CRUD
Social featuresBasicMutual friends/servers, user profiles, mark-as-unread, block/friend, group DMs
Notification controlsBasicMute/unmute, FCM management, push unsub on logout, notification-only FCM handling
Discord bridgeNoneImport wizard, bridge settings, stoatcord-bot integration
SyncNoneSettings sync (fetch/set), unread sync
DocumentationMinimal11 spec documents, API reference

Target: 96/96 backend delta routes + full auth coverage. Currently 96/96 delta (100%) + 27 auth routes implemented. All 6 phases complete including Discord bridge integration. Phase 6 polish done: user flags display, password reset/email verify endpoints, policy acknowledge, mass mention parsing, webhook sheet, feedback link. 39 Discord features require backend changes (documented in backend-required-features.md).

Phase 1: Account & Security (High Priority) — Complete

Section titled “Phase 1: Account & Security (High Priority) — Complete”
FeatureEndpointsStatus
Account info displayGET /auth/accountDone
Change emailPATCH /auth/account/change/emailDone
Change passwordPATCH /auth/account/change/passwordDone
Delete/disable accountPOST /auth/account/delete, disableDone
Email verificationPOST /auth/account/reverifyDone
MFA setup (TOTP)7 endpointsDone
Push unsubscribe on logoutPOST /push/unsubscribeDone
Server-side session logoutPOST /auth/session/logoutDone

Phase 2: Server Admin Polish (High Priority) — Complete

Section titled “Phase 2: Server Admin Polish (High Priority) — Complete”
FeatureEndpointsStatus
Default permissions editorPUT /servers/{id}/permissions/defaultDone
Role permissions editorPUT /servers/{id}/permissions/{roleId}Done
Role hoist toggle + rank editingPATCH /servers/{id}/roles/{roleId}, PATCH /servers/{id}/roles/ranksDone
Channel permissions (per-role overrides)PUT /channels/{id}/permissions/{roleId}, defaultDone (tri-state Allow/Neutral/Deny)
Server banner upload/removePATCH /servers/{id} + Autumn uploadDone (InlineMediaPicker)
Create channel NSFW togglePOST /servers/{id}/channelsDone
Server invite managementGET /servers/{id}/invites, DELETE /invites/{id}Done
Custom emoji managementPUT/DELETE /custom/emoji/{id}Done
Member searchGET /servers/{id}/members with queryDone (client-side)
System messages settingsPATCH /servers/{id} (system_messages)Done
Fetch single roleGET /servers/{id}/roles/{role_id}Done

Phase 3: Social Features (Medium Priority) — Complete

Section titled “Phase 3: Social Features (Medium Priority) — Complete”
FeatureEndpointsStatus
Mutual friends/serversGET /users/{id}/mutualDone
DM channel listingGET /users/dmsExisting
User profile display + editingGET /users/{id}/profile, PATCH /users/@meDone (avatar, background, bio with Autumn upload)
Block/unblock usersPUT/DELETE /users/{id}/blockDone
Friend requestsPOST /users/friend, PUT/DELETE /users/{id}/friendDone (send, accept, unfriend)
Open DM channelGET /users/{id}/dmDone
Group DM managementPOST /channels/create, PUT/DELETE /channels/{id}/recipients/{userId}Done
Content reportingPOST /safety/reportDone (messages, servers, users)

Phase 4: Content Management (Medium Priority) — Complete

Section titled “Phase 4: Content Management (Medium Priority) — Complete”
FeatureEndpointsStatus
Remove all reactionsDELETE /channels/{id}/messages/{msg}/reactionsDone
Bulk delete UIDELETE /channels/{id}/messages/bulkDone
Server emoji listingGET /servers/{id}/emojisDone (via cache)
Pin/unpin messagesPOST/DELETE /channels/{id}/messages/{msg}/pinDone
Message reactionsPUT/DELETE /channels/{id}/messages/{msg}/reactions/{emoji}Done
Message searchPOST /channels/{id}/searchDone (text, pinned, sort, pagination)
Channel invite creationPOST /channels/{id}/invitesDone
Settings syncPOST /sync/settings/fetch, POST /sync/settings/setDone
Unread syncGET /sync/unreadsDone
Voice call joinPOST /channels/{id}/join_callDone (LiveKit integration)

Discord Bridge Integration (Fork-Exclusive)

Section titled “Discord Bridge Integration (Fork-Exclusive)”
FeatureComponentStatus
Discord import wizardDiscordImportScreen + stoatcord-bot APIDone
Bridge settings (channel linking)BridgeSettingsScreen + bot APIDone
Discord guild/channel fetchGET /api/guilds, GET /api/guilds/{id}/channelsDone
Bridge link CRUDPOST /api/links, DELETE /api/links/{id}, GET /api/links/guild/{id}Done
Migration wizard (roles, channels, emoji)stoatcord-bot /migrate commandDone
Stoat↔Discord message relayWebSocket listener + Discord webhooksDone
FeatureEndpointsStatus
Bot createPOST /bots/createDone
Bot fetch/edit/deleteGET/PATCH/DELETE /bots/{id}Done (3)
Bot owned listGET /bots/@meDone
Bot inviteGET/POST /bots/{id}/inviteDone (2)
Webhook createPOST /channels/{id}/webhooksDone
Webhook listGET /channels/{id}/webhooksDone
Webhook CRUD (auth)GET/PATCH/DELETE /webhooks/{id}Done (3)
Webhook CRUD (token)GET/PATCH/DELETE /webhooks/{id}/{token}Done (3)
Webhook executePOST /webhooks/{id}/{token}Done
Bot management UIBotManagementScreenDone (create, edit, delete, token copy)
Webhook management UIWebhookManagementScreenDone (create, edit, delete, URL copy)

Phase 6: Polish & Edge Cases (Low Priority) — Complete

Section titled “Phase 6: Polish & Edge Cases (Low Priority) — Complete”
FeatureEndpointsStatus
Session management (rename, revoke, revoke all)PATCH/DELETE /auth/session/{id}, DELETE /auth/session/allDone
User flags displayBitmask (Suspended/Deleted/Banned/Spam)Done (UserFlagList + UserInfoSheet)
Default avatarGET /users/{id}/default_avatarExisting (Glide handles)
Policy acknowledgePOST /policy/acknowledgeDone
Voice end ringPUT /channels/{id}/end_ring/{userId}Planned
Push unsubscribePOST /push/unsubscribeDone
FCM notification-only handlingHandlerService fallbackDone
Robust push registrationChatRouterScreen retry logicDone
Password resetPOST/PATCH /auth/account/reset_passwordDone (2 endpoints)
Email verify (code)POST /auth/account/verify/{code}Done
Mass mention parsing@everyone/@here in markdownDone (MassMentionParser + renderer)
Webhook info sheetWebHookUserSheetDone (replaces stub)
Feedback linkOverviewScreen → GitHub IssuesDone
Members experimental queryGET /servers/{id}/members_experimental_queryPlanned

39 features require backend API changes: threads/forums, scheduled events, stage channels, AutoMod, audit log, slash commands, interactive components, polls, stickers, screen sharing, rich presence, server templates, vanity URLs, per-user permission overrides, slow mode. Full list in backend-required-features.md.

All changes from upstream divergence point:

CommitTypeDescription
321f8eefeatx86_64 AAPT2 wrapper using proot + qemu
3174bedfeatTermux ARM64 build system
19cddc8fixNative ARM64 AAPT2, graceful native lib loading
7c78ec5ciAPK build and dev release on every push
40214dcdocsAntifeature and UX audit
dd06b7efeatNotification fixes + message search
beb286efixSearch serialization, notification state, webp upload, filters
a756278fixSearch submit button, HTTP status checks, push error display
fecea65fixWebSocket reconnection on resume after background disconnect
469af7cfixSearch timeout, crash, FCM error messages, manage notifications
9f46e35fixSearch wildcard bug, HttpTimeout, mention filter
9101558featCollapsible search filters, clear all, result count
40e859efeatModeration (kick/ban), pin messages, server management routes
465b458fixSearch socket timeout increased to 60s with 75s total cap
f48c580docsEnable GitHub Pages, update URLs to fork, add fork changes reference
4ec7457featServer management UI, member moderation, FCM fix
7b23120docsServer management spec, fork changes reference
32f89d9featPermissions editor (default + per-role allow/deny/neutral)
01c82b9fixShow server/channel names in notification settings
ca3025adocsPermissions editor and notification improvement specs
dc66ccffixDiscover tab clicks, FCM placeholder detection, project memory
6dd4cc0featAccount management, invite management screens
44699c7featEmoji management, mutual friends/servers display
e54433fdocsUpdate specs for emoji, invites, mutual, account
c0bfb6bfeatSession management, remove all reactions
9216ff4featMember search, session management, remove all reactions
ee92622featBulk delete messages UI with count selector
bb8740ddocsUpdate fork-changes with session, member search, bulk delete
856b45edocsUpdate CLAUDE.md with real Firebase config
5374c5afeatMFA TOTP setup, recovery codes management
f2ad4d0fixMessage send race condition (#17), DM mention autocomplete (#52)
a3d8969docsUpdate fork-changes with MFA, upstream bug fixes
e7e8101featCollapsible categories, reply jump, send debounce, retry fix
c51e360fixNavigation restoration on app restart for DM/saved notes (#41/#21)
8768da8fixFriends list reactivity (#39), blocked user swipe reply (#11)
a444479fixSwipe-to-reply no longer hijacks code block scroll (#14)
4a29c1dfixREADME dead links, add fork docs URLs (#42)
1f7a26ffeatMark-as-unread in message context menu
65d18d3fixFetch user from API when not in cache for info sheet
43d10dbdocsAPI coverage cross-reference with backend and JS client
f1919b0fixDuplicate reply banners (#57), spoiler text (#54), copy buttons (#40)
df30163docsAdd duplicate reply, spoiler, copy fixes to fork-changes
16cb2d6featLong-press server icon opens context menu (#20)
c72565dfixProfile card tap-to-copy error handling and buttons (#19)
07b052cdocsAdd server icon long-press and profile card fixes to fork-changes
104bed3fixCategory chevron direction (expanded=down, collapsed=right)
5d0a7e4docsRewrite README with clear UNOFFICIAL fork disclaimer
94b62cefeatServer-wide message search via context sheet
9f7d001docsServer-wide search, chevron fix, README update to fork-changes
e1fc408featDiscord import wizard in server settings
c28356ddocsPrivacy policy and terms of service pages
59dc49efeatBridge settings screen for Discord-Stoat channel linking
8c02958docsDiscord bridge architecture spec
604fe31fixAllow cleartext HTTP in debug builds, fix bot OAuth permissions
76bb98bfeatRole hoist/rank editing, NSFW toggle, channel perms, server banner
dec3d10featNotification debug logging, system messages settings, server banner
38170e8fixRobust FCM push registration and notification-only message handling
beeaf35featPush unsubscribe and proper server-side session logout
a66e966docsUpdate roadmap with push, notifications, system messages progress
f1f15a4docsComprehensive roadmap audit — add 20+ untracked features
22d22badocsF-Droid anti-features audit and degoogling roadmap
99730c3featBot management and webhook CRUD (Phase 5)
4a8d96adocsUpdate roadmap — Phase 5 complete, 96/96 delta routes (100%)
9abb26afeatFinish remaining stubs and add missing endpoints (Phase 6)