One of my team asked if our rushomon instance could support mailto links. They currently use linkly for that but would prefer our url branding. I'm happy to build this. Proposed plan below is AI, based on how linkly works, but revised and reviewed. Let me know if you think this fits, as I'd rather not maintain a fork of the link creation modal etc if this isn't wanted upstream. Thanks!
Use case
Shorten a mailto: URL (address + subject/body/cc/bcc) so a short link opens the user's mail client with a pre-filled compose window. Useful for print/QR contexts ("scan to email us"), signatures, and campaign CTAs. Hand-writing a correctly percent-encoded mailto: URL is error-prone, so this includes a small compose UI. Linkly and Short.io support mailto shortening; Bitly doesn't.
Current behaviour
validate_url() rejects anything that isn't http/https, so mailto: destinations can't be created, and there's no UI to build one.
Proposed behaviour
Backend
- Validation: accept
mailto: alongside http / https (create/update/import share validate_url, covering every entry point). Validate structurally: non-empty comma-separated address list, query params allowlisted to subject, body, cc, bcc (closes off RFC 6068's arbitrary-header loophole), cc/bcc validated as address lists. javascript:/data:/file: stay rejected.
- Serving:
Location: mailto: redirects are unreliable across browsers/in-app webviews, so when the resolved destination is mailto:, serve a 200 HTML interstitial instead of a 30x: immediate JS redirect + meta refresh fallback + a visible "Compose email to address" link (which also gives visitors transparency about where the link points). noindex, no-store. Analytics/click counting attach exactly as today.
- Skip UTM/query forwarding for mailto destinations — appending params would corrupt the compose window.
- Blacklist: extend domain-match to extract the domain from the email address (
host_str() is None for mailto), so admins can block abusive addresses/domains.
Frontend
- Link-type selector: a compact
Web | Email segmented control in the link modal's destination section, defaulting to Web — the regular-link flow is visually and behaviourally unchanged. Pasting a mailto: string auto-switches to Email mode; editing an existing mailto link opens in Email mode.
- Email mode: To (required, multiple comma-separated), Subject, Body, and Cc/Bcc behind a collapsed toggle. The form builds a canonical, correctly-encoded
mailto: string submitted as destination_url — no API changes; the composer is a pure convenience layer over the same field, and round-trips (parse ↔ build) for editing.
- Display: envelope icon in place of the favicon for mailto links; address shown where the hostname normally appears. Title auto-suggest becomes "Email address".
Security notes
- The mailto URL is embedded in an HTML attribute and a JS string on the interstitial — escaped at both insertion points, with tests covering quote/angle-bracket injection via crafted
subject=/body= params.
- Query-param allowlist prevents smuggling arbitrary RFC 6068 header fields.
- Showing the target address on the interstitial mitigates the phishing-obfuscation concern; existing report + blacklist flows apply.
Out of scope (possible follow-up)
tel: / sms: schemes (the scheme branch will be trivially extensible)
No schema/migration changes — destination_url is already free-text and the KV mapping is unchanged.
One of my team asked if our rushomon instance could support mailto links. They currently use linkly for that but would prefer our url branding. I'm happy to build this. Proposed plan below is AI, based on how linkly works, but revised and reviewed. Let me know if you think this fits, as I'd rather not maintain a fork of the link creation modal etc if this isn't wanted upstream. Thanks!
Use case
Shorten a
mailto:URL (address + subject/body/cc/bcc) so a short link opens the user's mail client with a pre-filled compose window. Useful for print/QR contexts ("scan to email us"), signatures, and campaign CTAs. Hand-writing a correctly percent-encodedmailto:URL is error-prone, so this includes a small compose UI. Linkly and Short.io support mailto shortening; Bitly doesn't.Current behaviour
validate_url()rejects anything that isn't http/https, somailto:destinations can't be created, and there's no UI to build one.Proposed behaviour
Backend
mailto:alongsidehttp/https(create/update/import sharevalidate_url, covering every entry point). Validate structurally: non-empty comma-separated address list, query params allowlisted tosubject,body,cc,bcc(closes off RFC 6068's arbitrary-header loophole),cc/bccvalidated as address lists.javascript:/data:/file:stay rejected.Location: mailto:redirects are unreliable across browsers/in-app webviews, so when the resolved destination ismailto:, serve a 200 HTML interstitial instead of a 30x: immediate JS redirect +meta refreshfallback + a visible "Compose email to address" link (which also gives visitors transparency about where the link points).noindex, no-store. Analytics/click counting attach exactly as today.host_str()isNonefor mailto), so admins can block abusive addresses/domains.Frontend
Web | Emailsegmented control in the link modal's destination section, defaulting to Web — the regular-link flow is visually and behaviourally unchanged. Pasting amailto:string auto-switches to Email mode; editing an existing mailto link opens in Email mode.mailto:string submitted asdestination_url— no API changes; the composer is a pure convenience layer over the same field, and round-trips (parse ↔ build) for editing.Security notes
subject=/body=params.Out of scope (possible follow-up)
tel:/sms:schemes (the scheme branch will be trivially extensible)No schema/migration changes —
destination_urlis already free-text and the KV mapping is unchanged.