atagen

malice

public
Star0
Watch0
c04b1e46303fc98620efa4a4e18798510df98208
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: MACLTOK_URL_PREVIEW.md

b8ab00377c20atagen2026-10-22 05:13 UTC1
# macltok request, URL previews via the homeserver
b8ab00377c20atagen2026-10-22 05:13 UTC2
b8ab00377c20atagen2026-10-22 05:13 UTC3
## Problem
b8ab00377c20atagen2026-10-22 05:13 UTC4
b8ab00377c20atagen2026-10-22 05:13 UTC5
Malice's link cards only show the URL's host and path. It wants the page's title, description and image, the way other Matrix clients do. The client can't scrape pages itself. Browsers block it with CORS, and fetching from the malice server would leak its IP to every linked site.
b8ab00377c20atagen2026-10-22 05:13 UTC6
b8ab00377c20atagen2026-10-22 05:13 UTC7
Matrix already solves this with `GET /_matrix/client/v1/media/preview_url?url=...&ts=...` (ruma `authenticated_media::get_media_preview`). The homeserver fetches the page, parses OpenGraph, re-hosts `og:image` as an mxc, and returns the `og:*` keys as a loose JSON map. Knowing that endpoint, which keys matter, and that `og:image` is an mxc is protocol work every consumer would repeat, so it belongs in macltok. Nothing in `ClientHandle` reaches it today.
b8ab00377c20atagen2026-10-22 05:13 UTC8
b8ab00377c20atagen2026-10-22 05:13 UTC9
## Request
b8ab00377c20atagen2026-10-22 05:13 UTC10
b8ab00377c20atagen2026-10-22 05:13 UTC11
```rust
b8ab00377c20atagen2026-10-22 05:13 UTC12
impl ClientHandle {
b8ab00377c20atagen2026-10-22 05:13 UTC13
    /// None when the server has no preview for the url (404, empty body, or no usable keys)
b8ab00377c20atagen2026-10-22 05:13 UTC14
    pub async fn url_preview(&self, url: &str) -> Result<Option<UrlPreview>, Error>;
b8ab00377c20atagen2026-10-22 05:13 UTC15
}
b8ab00377c20atagen2026-10-22 05:13 UTC16
b8ab00377c20atagen2026-10-22 05:13 UTC17
pub struct UrlPreview {
b8ab00377c20atagen2026-10-22 05:13 UTC18
    pub url: String,
b8ab00377c20atagen2026-10-22 05:13 UTC19
    pub title: Option<String>,
b8ab00377c20atagen2026-10-22 05:13 UTC20
    pub description: Option<String>,
b8ab00377c20atagen2026-10-22 05:13 UTC21
    pub site_name: Option<String>,
b8ab00377c20atagen2026-10-22 05:13 UTC22
    pub image: Option<UrlPreviewImage>,
b8ab00377c20atagen2026-10-22 05:13 UTC23
}
b8ab00377c20atagen2026-10-22 05:13 UTC24
b8ab00377c20atagen2026-10-22 05:13 UTC25
pub struct UrlPreviewImage {
b8ab00377c20atagen2026-10-22 05:13 UTC26
    /// og:image, already re-hosted by the server, usable with `media` / `media_source`
b8ab00377c20atagen2026-10-22 05:13 UTC27
    pub media_id: MediaId,
b8ab00377c20atagen2026-10-22 05:13 UTC28
    pub width: Option<u32>,
b8ab00377c20atagen2026-10-22 05:13 UTC29
    pub height: Option<u32>,
b8ab00377c20atagen2026-10-22 05:13 UTC30
    pub mimetype: Option<String>,
b8ab00377c20atagen2026-10-22 05:13 UTC31
    pub size: Option<u64>,
b8ab00377c20atagen2026-10-22 05:13 UTC32
}
b8ab00377c20atagen2026-10-22 05:13 UTC33
```
b8ab00377c20atagen2026-10-22 05:13 UTC34
b8ab00377c20atagen2026-10-22 05:13 UTC35
Things that would help beyond the bare call.
b8ab00377c20atagen2026-10-22 05:13 UTC36
b8ab00377c20atagen2026-10-22 05:13 UTC37
1. Fall back to the legacy `/_matrix/media/v3/preview_url` when the server lacks authenticated media, the same way `media` does.
b8ab00377c20atagen2026-10-22 05:13 UTC38
2. Cache results per url for a while, including negative ones. Synapse rate-limits this endpoint and a busy timeline repeats links.
b8ab00377c20atagen2026-10-22 05:13 UTC39
3. Clamp string lengths, since the server passes page text through unfiltered. Something like 256 chars for title and site name, 1024 for description.
b8ab00377c20atagen2026-10-22 05:13 UTC40
4. Treat `og:image` that isn't an `mxc://` uri as absent.
b8ab00377c20atagen2026-10-22 05:13 UTC41
5. Map 429 and 5xx to a distinct error, so hosts can tell "try later" from "no preview".
b8ab00377c20atagen2026-10-22 05:13 UTC42
b8ab00377c20atagen2026-10-22 05:13 UTC43
## Optional, room level opt-out
b8ab00377c20atagen2026-10-22 05:13 UTC44
b8ab00377c20atagen2026-10-22 05:13 UTC45
Element honours `org.matrix.room.preview_urls` room state (`{"disable": true}`, set by room admins) and the per-user `org.matrix.preview_urls` room account data. If macltok exposed those on `RoomSummary`, or as `url_previews_allowed(room_id) -> bool`, malice wouldn't need to know the event types.
b8ab00377c20atagen2026-10-22 05:13 UTC46
b8ab00377c20atagen2026-10-22 05:13 UTC47
## Malice side, for context
b8ab00377c20atagen2026-10-22 05:13 UTC48
b8ab00377c20atagen2026-10-22 05:13 UTC49
Malice will call this only in unencrypted rooms by default, with a user setting to also allow it in encrypted ones, because the homeserver sees every previewed url. The image goes through malice's existing bounded media endpoint by `media_id`.