Skip to content

feat(messages): import linked images as attachments - #1501

Open
hmans wants to merge 6 commits into
mainfrom
hmans/linked-image-previews
Open

feat(messages): import linked images as attachments#1501
hmans wants to merge 6 commits into
mainfrom
hmans/linked-image-previews

Conversation

@hmans

@hmans hmans commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Why

Direct image links should feel and behave exactly like uploaded images, without creating a second media lifecycle to secure and maintain. The import path also needs explicit abuse bounds because a cheap composer request can otherwise amplify into server downloads, durable events, storage, and GIF processing work.

What changed

  • Recognize directly served JPEG, PNG, GIF, and static WebP responses by bytes and import only the first linked image through the normal pending attachment lifecycle.
  • Render linked images through the existing attachment tray, timeline thumbnail, Files panel, deletion flow, and lightbox while preserving the source URL in message text.
  • Require ordinary message.attach authorization and reuse attachment size, decoded-dimension/pixel/frame, storage, pending-expiry, claim, cleanup, and GIF/video-processing behavior.
  • Reserve distributed capacity before fetching: same actor/room/source requests are idempotent, provisional reservations expire after two minutes, and each actor can hold at most 10 outstanding linked-image imports.
  • Keep generic OpenGraph and YouTube previews on the existing cache and preview-token path.
  • Add optional room_id and imported_attachment fields to MessageService.FetchLinkPreview; regenerate Go, TypeScript, and public API references.
  • Update FDR-009, architecture, the media operations guide, and backend agent guidance.

Compatibility and operations

  • The protobuf/API change is additive; existing clients that omit room_id retain generic preview behavior and ignore the new response field.
  • No persisted core protobuf fields, EVT event types, room-owned asset types, or migrations are added. Imported media uses existing AssetCreated and attachment ownership/deletion facts.
  • RUNTIME_STATE gains hashed, TTL-bound staging coordination records. Direct media remains capped at 5 MB before ordinary decoded-image validation.

Test plan

  • go test ./internal/core/linkpreview ./internal/core ./internal/connectapi
  • frontend API/composer unit suites: 114 tests passed
  • pnpm run check:frontend (0 errors, 0 warnings)
  • mise lint-frontend
  • Playwright link-preview suite: 8 tests passed
  • docs website production build
  • mise lint
  • mise license-check
  • adversarial code review loop: no remaining findings

Closes #1498.

hmans added 5 commits July 13, 2026 22:10
…eviews

* origin/main: (405 commits)
  feat(authz): add interaction-scoped message reads (#2114)
  fix(auth): allow configured browser origin aliases (#2123)
  docs(agents): simplify instructions for ASD-STE100 (#2122)
  fix(dev): clean up archived workspace processes (#2121)
  fix(auth): preserve legacy browser login (#2120)
  docs: tighten local development guide (#2118)
  fix(auth)!: keep active users signed in (#2102)
  fix(notifications): avoid authorization fence events (#2115)
  feat(notifications): add scoped policy matrix (#2111)
  fix(api)!: prevent bots from starting DMs (#2113)
  feat(rbac)!: require message.read for channel content (#2100)
  feat(frontend): show inline message state markers (#2110)
  feat(frontend): refine room and thread dialogs (#2106)
  fix(realtime): defer retained timeline hydration (#2109)
  feat(frontend): submit dialog actions with Enter (#2107)
  feat(frontend): open created threads automatically (#2108)
  feat(frontend): show server re-login status (#2105)
  feat(frontend): frame preference forms (#2104)
  feat(frontend): add composer formatting shortcuts (#2103)
  feat(frontend): restructure settings and preferences (#2097)
  ...

# Conflicts:
#	AGENTS.md
#	apps/frontend/src/lib/api-client-tests/linkPreviews.spec.ts
#	apps/frontend/src/lib/api-client/linkPreviews.ts
#	apps/frontend/src/lib/components/composer/MessageComposer.svelte
#	cli/internal/connectapi/api_test.go
#	cli/internal/core/core.go
#	cli/internal/core/linkpreview/fetcher.go
#	cli/internal/core/linkpreview/fetcher_test.go
#	cli/internal/pb/chatto/api/v1/link_previews.pb.go
#	docs/ARCHITECTURE.md
#	docs/fdr/FDR-009-link-previews.md
#	packages/api-types/src/chatto/api/v1/link_previews_pb.ts
@hmans
hmans marked this pull request as ready for review August 27, 2026 07:04
…eviews

* origin/main: (50 commits)
  fix(frontend): separate member sidebar scrolling (#2177)
  fix(frontend): keep permission matrices stable during refresh (#2178)
  refactor(authz)!: flatten interaction read permission (#2175)
  docs(frontend): clarify panel collection treatments (#2176)
  fix(frontend): balance room directory card padding (#2173)
  refactor(frontend): load pending SSO confirmation flows (#2174)
  refactor(authz): make permission identifiers opaque (#2169)
  docs(release): correct 0.5 upgrade guidance (#2172)
  refactor(frontend): route server redirects through loads (#2171)
  feat(email): add JMAP delivery transport (#1443)
  feat(frontend): open profiles in direct messages (#2164)
  fix(frontend): restore neutral server selection (#2170)
  refactor(frontend): redirect root during load (#2167)
  feat(frontend): polish navigation feedback (#2166)
  chore(proto): retire storage relocation waiver (#2168)
  refactor(proto)!: separate internal storage contracts (#2162)
  fix(frontend): stabilise sign-in and chat landing states (#2160)
  fix(frontend): align pane header actions (#2161)
  feat(frontend): refresh semantic colors and buttons (#2159)
  refactor(authz): derive nested permission inclusion from names (#2157)
  ...

# Conflicts:
#	cli/internal/core/link_previews.go
#	cli/internal/core/linkpreview/fetcher.go
#	docs/architecture/nats-resources.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: import directly linked images as attachments

1 participant