- Task 7: Create activityStore (Zustand) with Map<userId, Activity[]>, showActivity toggle,
debounced pushActivities, and reset on logout
- Task 8: Wire WS integration — presence_update propagates activities to store,
ready payload initializes userActivities and showActivity (via setState to avoid side effects)
- Task 9: Create ActivityCard component with compact/full modes, type-colored labels,
elapsed time display, asset images, and fallback to customStatus
- Task 10: Upgrade ActivityPanel to three groups — active friends (full cards, no header),
online friends (compact), offline friends — using getPrimaryActivity for grouping
- Task 11: Upgrade MemberSidebar to show compact ActivityCards instead of raw customStatus
- Task 12: Add showActivity toggle in AccountPanel settings (Activity Status section)
with API persistence and store sync
Add auto-launch settings (start at boot + start minimized) to the
Desktop section in Account settings, using Electron's built-in
setLoginItemSettings API across macOS, Windows, and Linux.
Replace the fallback colored-circle tray icon with proper B logo assets:
template images for macOS (adapts to light/dark menu bar) and colored
icons for Windows/Linux. Fix BGRA channel order bug in fallback generator
and move tray icons to resources/ so they're packaged into the app.
- Custom screen share picker for Electron (ScreenSharePicker.tsx) with
Aether Drift design: glass-bubble footer, adaptive grid, pill tabs,
border-based selection (avoids overflow clipping), hover brightness
- Instance URL picker so Electron connects to any Backspace server
- System audio loopback on Windows/Linux via desktopCapturer callback
- macOS: video-only callback (OS blocks system audio capture)
- IPC bridge for screen source enumeration and selection
- Purge stale service worker caches on Electron launch
- Dev/prod URL auto-detection (Vite 5173 in dev, server 3000 in prod)
- Typed IPC bridge via preload (notifications, badge, window controls, updates, deep links)
- Native OS notifications via NotificationController with window focus suppression
- Auto-update via electron-updater with UpdateToast UI
- Deep linking (backspace:// protocol) for macOS and Windows/Linux
- Window state persistence (position, size, maximize across restarts)
- Tray icon with graceful fallback when icon asset missing
- Suppress PWA service worker polling/reloads inside Electron
- Platform detection layer (isElectron, getElectronAPI)
- Root workspace scripts (dev:desktop, build:desktop)
- Document BACKSPACE_URL and BACKSPACE_UPDATE_URL env vars
- Resize avatars/icons to 256px and banners to 1280px (client crop + server safety net)
- Add server-side resizeProfileImage() for federation/API uploads without crop modal
- Fix unconstrained crop on RegisterPage and CreateSpace (was missing maxOutputDimension)
- PWA: switch to autoUpdate with skipWaiting/clientsClaim for seamless deploys
- Storage janitor: exclude profile images from unlinked cleanup, delete stale thumbnails
- Add deleteAttachmentByFilename() to clean orphaned attachment records for profile images
- Migration: one-time cleanup of stale profile image attachment records
- GeneralPanel: wrap in <form> to prevent implicit submission
isValidAssetUrl() was rejecting bare filenames (e.g. "1234567890.webp") which
is the established convention the frontend sends. Now accepts bare filenames
while still blocking path traversal and unsafe schemes.
Also updates client-side password validation to match server's 8-char minimum.
- SSRF protection: DNS resolution + private IP blocking on metadata fetcher
- Upload security: CSP/X-Frame-Options headers, SVG forced download, nosniff
- Auth hardening: JWT secret min length, password min 8 chars, token revocation via password_changed_at
- Attachment ownership verification before linking to messages
- Message length limit (4000 chars) enforced on client and server
- Asset URL validation on avatar/banner updates
- Federation instance validation (domain regex, origin scheme, length limits)
- DB indexes on all FK columns for query performance
- Migrations: nullable moderator columns, dm_messages reply_to FK constraint
- File cleanup on avatar/banner replacement and space deletion
- Fastify trustProxy, AbortController on fetches, typing map size cap
- Add "Discover People" section to Add Friend tab with user cards, mutual counts, and inline actions
- Add discoverStore for fetching/searching discoverable users across local and federated instances
- Add PrivacyPanel to user settings with discoverability toggle
- Add is_discoverable column to users table with migration
- Fix "Send Friend Request" button vertical alignment using transform centering
Avatars/banners now resize to max 512px/1920px and convert to WebP before
upload (zero server cost). Chat image uploads generate an 800px-wide WebP
thumbnail via Sharp; the feed shows the thumbnail, click opens the full-res
original. Adds lazy loading to avatars. Federation-compatible: remote
instances without this feature fall back gracefully.
Make /join/:code a public route with a standalone JoinPage that shows a
space preview and handles authenticated, unauthenticated, and cross-instance
users. Adds GET /api/spaces/invite/:code/preview (no auth) endpoint,
?redirect= param support on login/register, and cleans up dead invite
handling from AppLayout and JoinSpace modal.
- Rewrite ChannelSettingsModal with full tri-state permission override UI
for roles and members (allow/neutral/deny per permission bit)
- Switch font from Inter to self-hosted DM Sans (woff2 variable fonts)
- Add client-side VIEW_CHANNEL filtering in ChannelSidebar for private channels
- Broadcast isPrivate flag on channel override changes
- Fix voice permission bit migration: gate behind persistent flag to prevent
repeated re-runs that stripped STREAM from @everyone roles
- Add speakingUserIds set to voice store for efficient user-level lookups
- Clear current channel view when a channel is deleted
- Move .glass-strip to @layer utilities for proper CSS specificity
- Simplify avatar initials font size to proportional formula
Three changes to bring output volume closer to native apps:
- Insert masterBoost GainNode (+3dB) before the compressor/limiter
- Raise default system sound volume from 0.5 to 0.8
- Add configurable Sound Effects Volume slider (0–200%) in Voice settings
Remove FK constraint on space_folder_members.space_id so federated
space IDs (which don't exist in the local spaces table) can be added
to folders without silently failing. Add migration to recreate the
table for existing databases and explicit cleanup on space deletion.
Offset drop indicator lines by 3px into the mb-1.5 gap so adjacent
items share one visual position instead of showing two lines.
Extract TransferOwnershipModal (~165 lines) to its own file.
Add stale-channel guard in AppLayout that redirects to the space root
when the URL's channelId no longer exists in the channel list. Remove
the hardcoded navigate('/channels/@me') from ChannelSettingsModal and
the redundant setCurrentChannel fallback from the WS channel_deleted
handler — AppLayout's guard now handles all cases uniformly.
Add Delete Channel button to channel settings modal with ConfirmDialog
confirmation. Fix backend DELETE route to disconnect voice users, clean
up attachment files from disk, and remove orphaned read_states. Make
deleteChannel federation-aware in spaceStore and clean up voiceUsers
on channel_deleted WebSocket event.
Add CreateCategory modal (replaces browser prompt) and right-click
"Delete Category" context menu on category headers with confirmation
dialog explaining channels will be uncategorized, not deleted.
Voice channels already support video/screen share, so the separate video
type was redundant. Adds migration to convert existing video channels.
Also adds border-border-soft to CreateChannel input fields for visibility.
Bring CLAUDE.md into full alignment with the production codebase after months
of active development. Adds 3 new DB tables, 8 user columns, 18+ API endpoints,
10 WS events, and 30+ new source files to the documentation. Updates deployment
section for Caddy/multi-instance setup and adds 8 new feature entries.
Also applies glass material system to UserProfileModal (backdrop + card).
Add .glass-modal CSS class (0.82 opacity) for dialogs and apply
consistent glass tiers across all floating UI: modals, context menus,
tooltips, popovers, and call cards. Replaces ad-hoc bg-surface-elevated
and inline styles with the documented 5-tier glass hierarchy.
Add avatarColor field to spaces, matching the user avatar color system.
Spaces get a random color on creation and owners can change it in space
settings. The color controls the fallback gradient when no icon is uploaded,
replacing the old deterministic hash-based gradient. Includes full
federation support, explore page, mutual spaces, and color picker in both
create and settings modals.
Enforce lowercase usernames at registration/login, migrate existing
usernames, and fix ownership checks for federated spaces by resolving
user identity per-instance with getMyUserIdForOrigin.
- Use custom Tooltip on all space sidebar items instead of browser title
- Add transferOwnership action to spaceStore (federation-aware)
- Add "Transfer Ownership" context menu item for space owners
- Add TransferOwnershipModal with member search and confirmation
- Add transfer ownership option in SpaceSettings > Danger Zone
- Add account deletion with tombstone (isDeleted flag), password/username
confirmation, owned-space guard, and full cleanup transaction
- Free deleted usernames by renaming to !deleted:<id> so they can be reused
- Add migration to retroactively free usernames from already-tombstoned users
- Add GET /api/auth/check-username endpoint with rate limiting for real-time
availability checking during registration
- Add debounced username availability indicator on registration Step 1
- Add DeleteAccountModal with federation-aware remote account cleanup
- Add federation ops utility for remote instance management
- Update sanitizeUser to anonymize deleted user profiles
- Add instance store improvements and connected instances modal updates
Add showAt prop to Username component and use it in both UserProfilePopout
and UserProfileModal to consistently display @username for all users.
Also move avatar ring styling into Avatar component's ring prop.
Replace element-level opacity on banner fallback divs with rgba-based
gradient color stops via new mutedGradient() utility. This prevents
compositing layer artifacts that caused visible color seams at the
banner boundary in AccountPanel, UserProfilePopout, and UserProfileModal.
Move bg-surface-channel from info section to card wrapper in AccountPanel
so the avatar's negative margin overlaps into the banner correctly. Replace
accent color presets with 7×3 banner color palette, remove accent color
tinting from display names in popout/modal, and refactor gradient entries
to expose from/to hex stops.
Replace the hardcoded 16-color ACCENT_PRESETS array in AccountPanel with
a shared export from gradients.ts that extracts both hex stops from each
avatar gradient, producing 14 harmonized colors in a 7×2 grid.
Add avatarColor as a stored, user-selectable field (mint, sky, lavender,
coral, rose, teal, amber). Randomly assigned on registration, changeable
in profile settings. Existing users keep hash-based fallback until they
choose a color. Includes DB migration, API validation, gradient map,
live preview in settings, and banner fallback integration.
Friends/requests were only loaded when FriendsPage or ActivityPanel rendered,
so profile modals in space views always showed "Add Friend" even for existing
friends. Now loadFriends/loadRequests fire on every WS ready event.
Also adds canonicalUserMatch() with cascading ID/homeUserId/username+instance
fallback, replacing fragile homeUserId-only matching in getFriendshipStatus.
Space avatars in the mutual spaces tab now use getSpaceGradient() instead of
a flat grey background, matching the sidebar appearance. Mutual friends and
spaces from remote instances show a globe icon with the instance hostname.
Also wires up federated mutuals loading, correct API client routing for
remote user profiles, and the new mutuals utility.
Add banner image, accent color, and bio fields to user profiles with
full-stack support: schema migration, API validation (hex color format,
190-char bio limit), sanitizeUser propagation, and new GET /users/:id/mutuals
endpoint. Rewrite AccountPanel with live preview card, avatar/banner upload
via ImageCropModal, 16-preset accent color picker, and bio editor. Enhance
UserProfilePopout with banner display, accent-colored names, bio rendering,
and mutual counts. Add new UserProfileModal with About/Mutual Friends/Mutual
Spaces tabs and friend action buttons.
Space icon in Settings → Overview was using the user Avatar component
(wrong palette + hashed on name), now uses getSpaceGradient(id, name)
to match the sidebar.
Remove the unused USE_VOICE_ACTIVITY permission bit (was bit 25) and shift
STREAM to bit 25, DISCONNECT_MEMBERS to bit 26. Add a database migration to
remap stored permission values. Gate camera and screen share buttons in
VoiceControlBar behind canSpeak/canStream, matching VoiceControls behavior.
Split monolithic UserSettings into AccountPanel, VoicePanel, ConnectionsPanel,
and InstancePanel tabs. Remove standalone InstanceSettings modal. Add reusable
Toggle component fixing size deformation, color inconsistency (green→purple),
and flex-shrink issues. Fix custom status clearing by always sending the field
to the server. Wrap Log Out button in glass bubble. Add subtle card depth with
borders.
- Add DISCONNECT_MEMBERS permission (bit 27) to disconnect users from voice
- Implement voice_disconnect WebSocket handler with permission checks
- Add disconnect button to voice user context menu
- Grant instance admins full permissions across all spaces
- Fix voice_disconnected handler to use federation-aware identity resolution
- Update CLAUDE.md with new event types and permission docs
- Add migration to convert legacy JSON array permissions to decimal strings
- Add runtime fallback in stringToPermissions for legacy format
- Show all permission toggles as enabled (dimmed) when Administrator is on
Add ban/unban functionality with BansPanel in space settings, voice
moderation context menu (mute/deafen/disconnect), and fix federated
space settings panels to use origin-aware API client. Show domain
indicators for federated members in MembersPanel.
- Redesign explore cards with banner images, overlapping icons, and frosted fade
- Extract dominant colors from space icons for dynamic banner gradients
- Add space description/banner fields to schema with migration
- Move origin label from banner overlay to content metadata row
- Support space descriptions in settings overview panel
Move instance-level administration out of Space Settings into its own
modal. Adds admin UI for instance name, registration toggle, and
discovery toggle. Streaming limits panel relocated from SpaceSettings.
- Add InstanceAdminSettings type and GET/PATCH /api/settings/instance
- Add registration_open DB column (nullable, env var fallback)
- Auth registration and instance info now check DB override
- New InstanceSettings modal with General and Streaming tabs
- Admin shield button in UserAreaPanel (visible to admins only)
- Remove Streaming tab from SpaceSettings
Fix three federation bugs in Space Settings: icon URL was double-wrapped
through home API, upload targeted home instance instead of remote, and
updateSpace used home API client. Also restyle settings panels (User,
Space, Members, Roles, Connected Instances) with consistent section
headers, grouped cards, and improved layout.