~/projects / Klar-Stack
Klar
Rust Typescript Bash Docker Infrastructure Networking CDN Domains + DNSKlar — Feature List (as of 25 August 2026)
Accounts & Auth
- Registration (username, email, password) with email verification link
- Login / logout, JWT access tokens (15 min) + refresh tokens (30 days)
- Cross-site auth support: tokens in
localStorage+Authorization: Bearer, with httpOnly cookies as a same-site fallback (handlesklarsocial.euvsklarsocial.debeing different top-level domains) - Password reset (forgot password → email link → reset)
- Resend verification email
- Argon2 password hashing
- Change password (requires current password, invalidates all other sessions)
- Account deletion (cascades to posts/comments/messages/media; cleans up stored files)
- Self-service data export (GDPR Art. 15/20) — full JSON download of everything Klar holds about you
Profile
- Username (case preserved as entered, case-insensitive uniqueness, 14-day change cooldown)
- Display name, bio, avatar (upload, resized/processed server-side)
- Public profile page: avatar, stats (posts/followers/following), bio
- Private accounts: toggle in Settings; when on, posts are hidden from non-followers
- Follow requests for private accounts: request → pending → accept/reject (not instant-follow)
- Accept/decline available in the notification dropdown and directly on the requester's profile page
- Dedicated
/follow-requestsmanagement page
Posts & Media
- Create post: caption + optional photo
- Automatic EXIF stripping on upload (location, device info, timestamp removed)
- Three generated sizes per image (thumbnail/medium/full), real width/height captured for correct aspect-ratio rendering
- Edit caption, delete post (with real cascade cleanup of media files)
- Post permalink pages (
/posts/[id]) — shareable, deep-linkable - Post detail modal: comments, likes, delete (from feed, profile, or permalink)
Feed
- Personal feed: strictly chronological, no algorithm, no ranking — deliberate product stance
- Fan-out-on-write (
feed_items) for fast reads; backfilled on follow, cleaned up on unfollow - Discovery feed: global, cross-user, cursor-paginated
- Private-account posts are excluded from the discovery feed and profile grids unless you're the owner or an accepted follower (enforced backend-side in three places: single post view, profile posts list, and the discovery feed itself)
Social Graph
- Follow / unfollow (instant for public accounts, request-based for private ones)
- Followers / following lists
- Block / unblock (blocked users can't follow, like, or comment on your posts)
Comments & Likes
- Post likes, comment likes
- Comments with threaded replies
- Edit/delete own comments; post owners can also delete comments on their posts
Notifications
- Types: post like, comment, comment like, follow, follow request, follow accepted
- Persisted history (
GET /notifications) + real-time delivery via SSE - Cross-replica delivery via Redis (Upstash) pub/sub — works correctly however many backend instances are running, not just replica-count-1
- Notification previews: actor avatar (follows/requests) or post thumbnail (likes/comments)
- Clicking a notification navigates to the relevant post or profile
- Follow-request notifications have inline Accept/Decline buttons, not just a link
Direct Messages (Chat)
- 1:1 conversations, gated to mutual followers only
- Send, edit, delete messages; reply-to-message threading
- Emoji reactions (8-emoji picker; can react to your own or the other person's messages)
- Read receipts
- Real-time message + reaction delivery (same Redis/SSE pipeline as notifications)
- Unread-message badge on the Chat icon, correctly clears when actively viewing a conversation (no false positives from your own open chat)
- Conversation list shows the true most-recent activity — a plain message, a reply, or a reaction — with proper phrasing ("Me: ...", "X replied: ...", "X reacted 👍 to your message: ...")
- Avatar in conversation list/chat header links to the other person's profile; rest of the row opens the chat
Search
- User search by username or display name
Moderation & Reporting
- Report posts, comments, or user accounts (
POST /reports) with a fixed reason taxonomy: spam, harassment, hate speech, violence/graphic content, self-harm, sexual content, CSAM, impersonation, other - Auto-moderation on submission: a CSAM report hides the content immediately, zero tolerance, no threshold; violence/self-harm/sexual-content reports flag the content (shown behind an interstitial rather than removed outright — a single report shouldn't have unilateral takedown power)
- Admin review queue (
/admin/reports), critical reports sorted first: dismiss (restores visibility) or remove content outright, each with an optional internal review note - Admin access gated via an
ADMIN_EMAILSallow-list, requiring the matching account's email to also be verified (prevents someone from claiming an admin address by registering it first) - Not yet built: DSA-mandated automated "Statement of Reasons" notification to affected users, appeal/counter-notice flow, UrhDaG rights-holder copyright takedown portal
Privacy & Compliance
- Impressum, Datenschutzerklärung (privacy policy), Nutzungsbedingungen (ToS), and a plain-language Transparenz page explaining data handling in everyday terms
- Legal pages carry a version-controlled "Stand:" date, auto-updated by a GitHub Actions bot commit whenever the underlying legal page file changes
- ToS + privacy consent checkbox at registration, enforced server-side (
POST /auth/registerrejects the request with 400 ifaccept_termsisn'ttrue, and 422 if it's missing entirely) — not just a client-side gate anymore. Acceptance is timestamped (users.terms_accepted_at) and included in the self-service data export - No tracking/advertising cookies — only functional auth storage
- Documented sub-processors: Bunny.net (hosting/CDN/storage and self-hosted Postgres, German DC), Scaleway (transactional email, EU), Upstash (real-time notification relay, US — SCCs apply)
Pre-Launch
- Site-wide passcode gate (
/welcome) via Next.jsproxy.ts— blocks the whole site except the gate page and legal pages until a shared passcode is entered; disabled automatically if no passcode is configured (e.g. local dev)
Frontend/UX Details
- Consistent top navigation bar across all primary pages (Feed, Discovery, Search, Chats, Profile) with active-state icon highlighting
- Mobile-correct viewport handling (
dvhinstead ofvh) so nav bars don't get clipped by mobile browser chrome - Root URL smart-redirects to Feed (logged in) or Login (logged out)
- Global cursor-pointer fix for all interactive elements
Backend/Infra
- Rust (Axum) backend, self-hosted PostgreSQL 18 in a Bunny Magic Container (Frankfurt) — TLS enforced even on loopback, connection pool retries through pod-startup races
- Nightly automated Postgres backups (
db-backupsidecar → private Bunny Storage Zone, no CDN pull zone); durability handled by off-pod storage, so the sidecar itself needs no persistent volume - UUIDv7 primary keys, denormalized counters (follower/following/post/like/comment counts), hash-partitioned
likes/notifications, monthly-partitioned interaction event log (post_events— logging foundation for possible future recommendations, not used for ranking today) - Bunny.net: application hosting (Magic Containers), CDN, S3-compatible object storage
- Redis (Upstash, TLS) for cross-replica pub/sub
- Multi-provider transactional email abstraction (currently live on Scaleway TEM)
- Per-route rate limiting (stricter on auth endpoints)
- GitHub Actions CI/CD: separate backend/frontend pipelines, Docker builds, auto-deploy to Bunny on push to
main - Health check endpoint
Not yet built / explicitly deferred: DSA Statement-of-Reasons user notifications & appeal flow, UrhDaG rights-holder copyright portal, birth-date/16+ age verification enforcement, uptime monitoring/alerting, notifications for follows-of-a-reply/DM-specific push beyond what's listed above, ClickHouse-based ranking (data collection foundation exists, ranking layer doesn't).