How the vutuv codebase is put together, one document per subsystem, plus the stack conventions and the context-module map below.
Setup, tests and deployment live in Developing vutuv; installing and operating vutuv in Running your own vutuv.
| Document | Covers |
|---|---|
| realtime.md | the LiveView app shell, live pages, reload-free updates over PubSub, presence & online dots, notifications, Berlin-time post stamps, the live people counter |
| social-graph.md | follows, vernetzt (mutual follows), per-follow mute, blocking, personal notes |
| fediverse.md | follow-only ActivityPub federation: WebFinger, actors, the inbox, signed deliveries |
| posts-and-feed.md | posts, deny-based audiences, the /feed timeline, likes/bookmarks/reposts, reply threads, post images |
| translations.md | declared post languages, on-demand Ollama translation, the cache + job queue, what never translates |
| i18n.md | the interface languages: how a request gets its locale, the Gettext catalogs and their extract/merge traps, per-locale email bodies, locale-sensitive formatting, and how to add a language |
| search.md | the search page, query operators, post full-text search |
| messages.md | 1:1 direct messages, message requests, unread-email nudges |
| profiles.md | what a profile shows: owner vs. public view, the job-title line, education, section ordering, contact details & maps, the Mastodon/Bluesky card, BookWyrm reviews |
| job-references.md | Arbeitszeugnisse: private-by-default upload, CV linkage, PDF/OCR text extraction, and the AI review (daily-fetched prompt, the context-window trap, the queue) |
| organizations.md | verified organization pages at /organizations: the domain-proof trust model (DNS TXT / well-known file), lifecycle states, engagement, moderation, agent formats |
| jobs.md | job postings at /jobs/:slug: the 90-day lifecycle & sweeper, structured location (offline GeoNames), the salary model, visibility gates, the AGG hint, easy apply, JSON-LD & agent formats |
| settings-and-account.md | the settings hub, onboarding, username changes, LinkedIn import, GDPR export, the CV (Lebenslauf) download |
| mentions.md | @handle mentions: the shared grammar, existence validation (anti-reservation), handle availability (anti-hijack), and rewriting mentions + notifying authors when a member renames |
| authentication.md | passwordless PIN login, passkeys, server-side sessions |
| account-activity.md | the append-only account-activity log: what changed on an account, when, from where and how it was confirmed; the member's page, the admin's page, retention |
| moderation.md | reports, freezes, the strike ladder, reporter trust, evidence screenshots |
| agents-and-seo.md | agent formats (.md/.txt/.json/.xml/.vcf), the member directory, sitemap/RSS/JSON-LD, Open Graph, how to read the Search Console reports |
| email.md | the Emailer chokepoint, multipart bodies, opt-outs, bounces & deliverability |
| images.md | the AVIF pipeline, kept originals, fingerprinted filenames, the shared images table and its backfill, URL screenshots, AI image moderation (Ollama) |
| video.md | video on posts: the ffmpeg pipeline and its resumable job, the AI check over the stills, the post that waits for its clip, the player and the range-answering proxy, what federates and what the Mastodon API says |
| attachments.md | files on posts and messages: the upload chokepoint, the format read from the bytes, the PDF gate and why a raw-byte scan is not enough, the two on-disk copies, the per-member budget |
| admin.md | the admin panel: live dashboard, member browser, account deletion, newsletter & audiences, daily report |
| ads.md | the daily text ad: booking, review, serving |
| company-pages.md | the site footer, the English /system/investors and /system/media-kit pages, brand assets, and the daily head-count history behind the growth curve |
| api.md | /api/2.0: personal access tokens, OAuth 2, webhooks, developer docs |
| mastodon-api.md | the Mastodon-compatible client API on mastodon.<PHX_HOST>: what phone clients can do, and what they cannot yet |
- Views: mostly Phoenix 1.8 HTML modules with
embed_templates(nophoenix_viewdependency); LiveView is being adopted incrementally for the real-time parts (see realtime.md) - Routes: Verified routes (
~p"..."sigils). Profiles live at the URL root, GitHub-style:/:slugis the profile and all per-user sub-pages hang off it (/:slug/links,/:slug/followers,/:slug/following,/:slug/connections, ...). The legacy/users/:slug/...URLs,/sessions/newand/search_queries/...301 to their new homes (/login,/logout,/search). The user scope is the last in the router, so static routes always win;Vutuv.Accounts.ReservedSlugskeeps users from registering a slug that equals a route prefix. The URL-root namespace is shared with organizations: an organization can claim a member-style@handleand serve at/:handletoo, with global uniqueness guaranteed by thehandlesregistry table (see organizations.md, #941). The old read-only/api/1.0JSON API was removed in favor of/api/2.0; only the session-aware vCard survived, at/:slug/vcard - Forms:
<.form>component with<.inputs_for>for nested forms - Assets: esbuild + Tailwind CSS v4; dark mode follows the system
(
prefers-color-scheme, no toggle) — legacy pages get their dark styles centrally fromassets/css/components.css - HTTP server: Bandit
- Pagination: browse pages (followers, tags, the admin member browser) use
offset pagination —
Vutuv.Pages.paginate/3on the query plus the<.pager>component for the numbered links; feed LiveViews (notifications) use cursor pagination instead —Vutuv.Activity.notifications_page/2behind a numbered "Load 50 of 80 more" button that appends to the stream. Displayed counts (badges, follower numbers) are compacted site-wide viaVutuvWeb.UI.compact_count/1: exact up to 999, then 1K/80K/5M. The one exception is the people counter — the top bar's total of members here plus the Fediverse accounts following them — which shows the exact figure viadelimited_count/1so it visibly ticks (see realtime.md) - Ids: all database ids are UUID v7 (
Vutuv.UUIDv7): time-ordered, minted in the app, never integers or UUID v4.
Business logic is organized into Phoenix context modules under lib/vutuv/:
| Context | Schemas | Purpose |
|---|---|---|
Vutuv.Accounts |
User, Email, UsernameChange, SearchTerm, LoginPin | Registration, PIN-based authentication, user management |
Vutuv.Sessions |
UserSession | Server-side per-device sessions: signed-in-devices list, remote logout, new-device security email |
Vutuv.AccountEvents |
AccountEvent | The append-only account-activity log: what changed on an account, when, from where and how it was confirmed (member + admin readers, key-whitelisted details, retention sweep) |
Vutuv.Credentials |
UserCredential | Passkeys (WebAuthn/FIDO2): enrolment + assertion verification for passkey login |
Vutuv.ApiAuth |
Token, App, Grant, AuthCode | API credentials: personal access tokens, OAuth 2 apps/grants/codes, scopes |
Vutuv.Webhooks |
Subscription, Delivery | Signed webhook deliveries to registered apps (queue, backoff, kill switch) |
Vutuv.Profiles |
Address, PhoneNumber, SocialMediaAccount, Url, WorkExperience | User profile data |
Vutuv.Social |
Follow, Block, Group, Membership, UserLike, UserBookmark | Follows (a mutual follow = vernetzt), per-follow mute, blocking, groups, liking/bookmarking people |
Vutuv.Posts |
Post, PostDenial, PostImage, PostTag, PostLike, PostBookmark, PostRepost, PostReply | Posts, deny-model audiences, the feed, likes/bookmarks/reposts, replies/threads |
Vutuv.Tags |
Tag, UserTag, UserTagEndorsement | Tagging and endorsements |
Vutuv.PersonalNotes |
PersonalNote | Private notes a member keeps about other accounts (members, pages, remote accounts) |
Vutuv.Search |
(none — reads Accounts.SearchTerm) |
Search functionality (people by name and CV, organizations, tags; post full-text via Vutuv.Posts.search_public/2). Stores nothing about a query |
Vutuv.Chat |
Conversation, Participant, Message | 1:1 direct messages, message requests, unread email notifier |
Vutuv.Moderation |
Case, Report, Strike | Reports, the content freezer, the strike ladder, reporter trust |
Vutuv.Notifications |
Emailer | Email notifications |
Vutuv.Newsletters |
Newsletter, NewsletterDelivery, NewsletterClick, NewsletterGroup, NewsletterGroupMember, Markdown | Admin email newsletter ("Rundbrief"): draft, test send, broadcast, delivery log, link click tracking + success overview, and filter-built audience groups |
Vutuv.Deliverability |
Event, MailLog, Watcher, Sweeper | Bounce detection: deactivate dead addresses, freeze unreachable accounts, admin dashboard |
Vutuv.Ads |
Ad | The daily text ad: booking, billing record, serving |