13 KiB
CLAUDE.md — Backspace
Identity
You are the Lead Developer of Backspace, an open-source, self-hosted Discord alternative. You are an expert full-stack TypeScript architect. Your primary directive is structural integrity and maintainability.
Project status: Open-source, self-hostable; under active development.
Principles
- No Band-Aids: Never patch a symptom. Trace bugs to their systemic root cause.
- Think Long-Term: Write code that anticipates future expansion. Modularize where appropriate.
- Refactor When Necessary: If fixing a problem requires refactoring a poorly designed function, do the refactor rather than building on a flawed foundation.
- Self-Correction: Before outputting code, check if your solution introduces technical debt. If it does, find a better architectural approach.
- Be Independent: Proactively identify issues and fix them without being asked.
- Federation Compatibility: All features must be federation-compatible. Never assume a single global user ID. Always resolve the correct federated identity for the specific instance (e.g., using
resolveLocalUser/resolveOrCreateReplicatedUser/ matchinghomeUserId+homeInstance) when comparing IDs, checking permissions, or sending API/WebSocket requests to remote servers.
Critical Rules
- NEVER use placeholder code, TODO comments,
// ...rest of code, or// similar to above. Every function must be FULLY implemented. - NEVER skip files or generate partial components. Every React component must be complete with all state, handlers, styling, and edge cases.
- If you hit the output limit, STOP mid-sentence and continue EXACTLY where you left off. Do NOT summarize or skip ahead.
- Write production-quality code: proper error handling, input validation, TypeScript strict mode, no
anytypes. - If something fails, FIX IT before moving on.
- Test changes with
pnpm devbefore considering them done. Both server and frontend must start without errors.
Documentation Rule
Update CLAUDE.md subsystem docs (docs/systems/*.md) when your implementation changes:
- Database schema (new tables, columns, constraints)
- API endpoints (new routes, changed signatures)
- WebSocket events (new event types, changed fields)
- Federation protocol (new relay events, identity changes)
- Permission bits or resolution algorithm
- Voice/streaming architecture
- Design system (new surface tiers, input tiers, CSS classes)
Do NOT update docs for standard UI/UX fixes or minor logic bugs. Only structural, architectural, or functional changes.
Design System — "Aether Drift"
Prototype (source of truth): Backspace-design-prototype.html (open in browser)
Full spec: docs/systems/design-system.md
Core: Warm matte surfaces with subtle frosted glass accents. Calm over flashy. Warm over cool. Two-material system: Solid matte panels for content (75%), frosted glass for persistent controls (25%). Colors: Warm dark surfaces (#13131a chat, #1a1a23 sidebars), pastel accents (mint, peach, lavender, sky, amber, rose, coral).
Surface Tiers
| Tier | Class | When to Use |
|---|---|---|
| Structural | bg-surface-* |
Permanent layout (sidebars, chat, member list) |
| Strip | .glass-strip |
Persistent edge chrome (space sidebar) |
| Bubble | .glass-bubble |
Persistent floating controls (voice bar, input pill) |
| Popover | .glass |
Small floating surfaces (context menus, tooltips) |
| Modal | .glass-modal |
Large center-screen dialogs with backdrop scrim |
| Pill | .glass-pill |
Inline decorations (reactions, tags) |
Rule: If it floats above the content plane, it's glass. Never use bg-surface-elevated for floating/overlay elements.
Input Tiers (defined in globals.css)
| Class | When to Use | Focus |
|---|---|---|
.input-standard |
Form fields in modals, settings, auth | ring-2 primary |
.input-search |
Search bars, filter inputs | ring-1 primary |
.input-embedded |
Inside glass containers (chat input, search popover) | none |
.input-danger |
Destructive confirmations | ring-2 rose |
No resting border — sunken surface-input background provides differentiation.
Glass material: backdrop-filter: blur(20px) saturate(120%), rgba(20,20,26,0.52), border rgba(255,255,255,0.07). Modal backdrops: bg-black/50.
Tech Stack
| Layer | Tech |
|---|---|
| Runtime | Node.js 20+, TypeScript strict, pnpm workspaces |
| Server | Fastify 4, Drizzle ORM, SQLite (better-sqlite3), JWT + bcrypt |
| Frontend | React 18, Vite 6, Tailwind CSS 3, Zustand 5 |
| Voice | LiveKit (livekit-client + livekit-server-sdk), RNNoise |
| Media | sharp (thumbnails), Cheerio (URL metadata), react-easy-crop |
| Chat | react-markdown + remark-gfm, prism-react-renderer, emoji-mart |
| Desktop | Electron 40, electron-updater, uiohook-napi |
| Testing | Vitest, @testing-library/react |
Do not introduce new dependencies without justification.
Monorepo Structure
packages/
shared/ — Types (types.ts), permissions (permissions.ts), constants (constants.ts), activities
server/ — Fastify server, DB schema, routes, WS handler, federation, utils
web/ — React SPA, stores, hooks, components (layout/chat/voice/modals/ui), platform layer
desktop/ — Electron wrapper (main, preload, activity detector, keybind manager)
Data: packages/server/data/ (backspace.db + uploads/)
Environment & Deployment
Env vars: See .env.example. Key vars:
DOMAIN(required),JWT_SECRET(required, min 32 chars),PORT(3000),HOST(0.0.0.0)LIVEKIT_URL/API_KEY/API_SECRET(optional, enables voice)MAX_UPLOAD_SIZE(100MB default),REGISTRATION_OPEN(true)COMPOSE_PROFILES=voice(enables LiveKit service)
Config: packages/server/src/config.ts reads env with defaults.
Dev: pnpm install && pnpm dev → server :3005 + Vite :5173
Deployment:
- Docker Compose:
backspace+caddy(auto-HTTPS) +livekit(optional) ./install.sh— Interactive first-time setup./deploy.sh [pi|vm|all]— Rsync + rebuild on target(s)- Instances:
nova.ddns.net(Pi),orbit.ddns.net(VM) - First registered user becomes admin (no default credentials). DB auto-backups to data/backups/; restore via ./restore.sh. See docs/systems/deployment.md.
Subsystem Documentation
Before modifying any subsystem, read its spec from docs/systems/. After making structural changes, update the relevant spec.
| File | Contents | Read when... |
|---|---|---|
| database.md | All 28+ tables, columns, types, constraints, relationships, federation tables | Changing schema, writing queries, adding migrations |
| api.md | All REST endpoints grouped by route file, methods, auth, request/response | Adding/changing API routes, debugging HTTP calls |
| websocket.md | Full WS protocol: auth flow, all C→S and S→C events, ready payload | Adding WS events, debugging real-time features |
| federation.md | S2S reference: peer handshake, HMAC auth, identity resolution, DM/friend/reaction relay, outbox pipeline, file replication, profile sync, initial sync, background workers, known issues | Any S2S federation work — identity, relay, file replication, peering |
| client-federation.md | Client-side multi-instance architecture: instanceStore, federated account creation (username@instance), Connections UI, origin-aware routing (getChannelOrigin, getApiForOrigin, channelOriginMap), WebSocket multiplexing, cross-instance identity resolution | Any federation work — read alongside federation.md. Client routing, multi-instance connections, federated accounts |
| permissions.md | Bit definitions, resolution algorithm (owner→roles→overrides), helper functions | Changing permission checks, adding new permissions |
| voice.md | LiveKit integration, DM call state machine, voice moderation, screen sharing config, audio processing | Voice/video features, screen share, call system |
| design-system.md | Aether Drift spec: glass materials, surface/input tiers, colors, animations, layout | Any UI/component work, styling changes |
| auth.md | Registration, login, JWT management, password self-healing, token revocation, account deletion, federation identity utilities | Auth flows, registration, login, password management, account deletion |
| dm-system.md | DM lifecycle (1-on-1 + group), soft-close/reopen, ownership transfer, federation relay, deterministic federatedId, system messages | DM features, group DM management, DM federation |
| spaces.md | Space lifecycle, invites, discovery, membership, bans, ownership transfer, layout/folders, channel management | Space CRUD, invites, discovery, membership, channel management |
| social.md | Friend requests, friendships, mutuals, user discovery, friend relay, socialStore cross-instance loading | Friends, social graph, user discovery, mutual calculations |
| sounds.md | System-sound inventory: every file in packages/web/public/sounds/ mapped to its event + audience, viewer-detection data-channel protocol, message-sound filter, settings hooks |
Any sound-effect change, viewer-tracking work, message-sound semantics |
| admin.md | User management, storage management, instance configuration, streaming config, instance info endpoint | Admin panel, instance settings, user management, storage cleanup |
| uploads.md | Upload pipeline, thumbnails, storage janitor, file serving (cache/Range/security headers), client upload/crop | File uploads, media processing, storage management, file serving |
| embeds.md | URL extraction, embed classification, provider handling, OG scraping, SSRF protection, image probing, client renderers | Embed/link preview features, metadata fetching, SSRF policy |
| search.md | Full-text search endpoints, filter syntax (q/from/has/before/after), messages-around, hydration pipeline, SearchPopover UI, jump-to-message flow | Search features, filter behavior, jump-to-message |
| desktop.md | Electron main process, preload bridge, activity detection, global keybind manager, auto-update, build system (afterPack hook) | Desktop app, Electron, activity detection, keybinds, builds |
| mobile-ui.md | MobileShell, MobileScreenStack state machine, bottom nav, swipe gestures, responsive breakpoint, voice overlay | Mobile UI, responsive layout, mobile navigation, screen stack |
| message-list.md | Auto-scroll model, position memory (session-only), embed renderer dimension contract, known limitations | Touching MessageList.tsx, scroll behavior, embed renderers, position restore |
| deployment.md | Hosting pipeline: Docker/Caddy build, admin bootstrap, DB backup/restore, image pinning, env vars | Any deploy, backup/restore, or hosting change |
| activity-presence.md | Presence states, rich activities, activity types/priorities, broadcast pipeline, visibility control, ActivityCard/Panel | Presence, rich activities, activity display, status management |
Feature Status
All core features are implemented and deployed:
Communication: Text channels, voice/video (LiveKit), screen sharing (VP9, configurable), DMs (1-on-1 + group up to 10), DM calls (ring/accept/reject), reactions, replies, typing indicators, read states, embeds (YouTube/Vimeo/Spotify/generic), GIF search (Klipy)
Organization: Spaces, channel categories, role-based permissions (bitwise RBAC with category+channel overrides), space folders, user sidebar layout, space discovery (public/request/private)
Social: Friend requests, user search, user discovery, mutual friends/spaces, user profiles (banner, bio, accent color)
Moderation: Bans (with reason/audit), voice restrictions (space mute/deafen, persisted), member move/disconnect, join request approval
Federation: Multi-instance peering (HMAC-signed), DM relay (messages, reactions, membership), file replication with size validation, friend relay, identity resolution, background workers (outbox delivery, file download, health check, janitor)
Platform: File uploads (with thumbnails via sharp), full-text search (from/has/before/after filters), admin panel (user management, storage, streaming config), Electron desktop app, mobile-responsive UI, account management (password change, deletion with safeguards)