# Media & Services ## File Upload Files are uploaded via REST, validated by magic bytes, stored with GUID filenames, and broadcast as a message with a download link. ```mermaid sequenceDiagram participant Client as Client participant CC as ChannelsController participant FV as FileValidationHelper participant FS as FileStorageService participant CS as ChatService participant DB as SQLite Client->>CC: POST /api/channels/{channel}/upload (multipart) CC->>CC: Check file size limits CC->>FV: IsValidImage(stream) — magic byte check FV->>FV: Read header: JPEG(FFD8FF) / PNG(89504E47) / GIF / WebP(RIFF+WEBP) FV-->>CC: true/false CC->>FS: SaveFileAsync(stream, extension) FS->>FS: Generate GUID filename, write to uploads/ FS-->>CC: fileId (GUID) CC->>CC: Determine MessageType (Image/Audio/File) CC->>CS: SendMessageAsync (with file URL + optional ASCII art) CS->>DB: INSERT Message CS->>CS: BroadcastToAllAsync → fan out to clients ``` --- ## Link Embed Resolution When a message contains URLs, the server fetches OpenGraph metadata for preview embeds. ```mermaid sequenceDiagram participant CS as ChatService participant LE as LinkEmbedService participant Web as External Website CS->>LE: TryGetEmbedsAsync(messageContent) LE->>LE: Extract URLs via regex LE->>LE: Filter: max URLs per message, skip duplicates loop For each URL LE->>LE: Validate: http(s) only, block private IPs LE->>Web: GET URL (timeout + size limit) Web-->>LE: HTML response LE->>LE: Parse og:title, og:description, og:site_name LE->>LE: Parse theme-color meta tag LE->>LE: Fallback to