Skip to content

fix: social-preview images + absolute URLs + OCI-safe healthcheck - #1

Merged
kallioli merged 3 commits into
mainfrom
fix/link-preview-og-image
May 22, 2026
Merged

fix: social-preview images + absolute URLs + OCI-safe healthcheck#1
kallioli merged 3 commits into
mainfrom
fix/link-preview-og-image

Conversation

@kallioli

@kallioli kallioli commented May 22, 2026

Copy link
Copy Markdown
Contributor

Context

Sharing a Ghostbit link (iMessage, Slack, …) rendered a huge blown-up logo:
og:image pointed at the portrait logo (421Γ—548) via a relative URL, so
unfurl bots stretched it into the hero-image slot.

Rebuilding the image to ship that fix surfaced a second issue: podman build
produces OCI-format images, which silently drop the Dockerfile HEALTHCHECK
("not supported for OCI image format") β€” the liveness probe was effectively
absent from every podman-built image.

Changes

Social previews

  • New static/og-banner.png (1200Γ—630) β€” landscape social card
  • New static/icon.png (512Γ—512) β€” square favicon + apple-touch-icon
  • Homepage: og:image β†’ banner, absolute URL, width/height/type/alt, twitter:card=summary_large_image
  • Paste pages: drop og:image so unfurls fall back to the compact card (an encrypted paste has nothing to preview)
  • Favicon / apple-touch-icon redirects β†’ the square icon

Absolute URLs

  • New BASE_URL setting + abs_url() template helper: og:image / og:url use BASE_URL when configured, else the request origin
  • Hardens absolute URLs behind a TLS-terminating proxy that would otherwise advertise an internal http:// origin
  • Documented in .env.example, README, docs/configuration.md; a malformed value fails fast at startup

Healthcheck

  • Remove HEALTHCHECK from the Dockerfile (dropped by OCI builds anyway)
  • Add a healthcheck: block to the ghostbit service in docker-compose.yml
  • Add Health* keys to the Podman Quadlet example in the README
  • Probe targets 127.0.0.1, not localhost: busybox wget resolves localhost to [::1], but uvicorn binds IPv4 only β€” the original localhost probe never actually worked

Tests

  • ruff check + ruff format --check clean, 84/84 pytest pass (+3 new: og:image is absolute, paste page omits og:image, abs_url BASE_URL override)
  • Rendered meta verified live (homepage = banner, paste = compact, absolute URLs)
  • Image rebuilt with podman β€” no OCI warning, podman healthcheck run β†’ healthy
  • og-banner.png / icon.png served 200 from inside the container

Risks

  • Low β€” no API / storage / crypto changes; the server-never-sees-plaintext invariant is untouched.
  • Link-unfurl caches (iMessage/Slack/Facebook) are per-URL and sticky: previously shared links may show the old preview for a while; new links pick up the new card immediately.

kallioli added 3 commits May 22, 2026 22:49
iMessage and other link-unfurl services blew up the portrait logo into a
huge hero image: og:image pointed at logo.png (421x548) via a relative URL.

- add static/og-banner.png (1200x630) β€” landscape social card
- add static/icon.png (512x512) β€” square favicon + apple-touch-icon
- homepage: og:image -> banner, absolute URL, width/height/type/alt,
  twitter:card=summary_large_image
- paste pages: drop og:image so unfurls fall back to the compact card
  (an encrypted paste has nothing to preview)
- point favicon / apple-touch-icon at the square icon
podman build produces OCI-format images, which silently drop the Dockerfile
HEALTHCHECK instruction ("HEALTHCHECK is not supported for OCI image format").
The probe was effectively absent from every podman-built image.

- remove HEALTHCHECK from the Dockerfile
- add a healthcheck: block to the ghostbit service in docker-compose.yml
- add Health* keys to the Podman Quadlet example in the README

The liveness probe now behaves identically whether the image is built by
podman or docker.
Social-preview meta tags (og:image, og:url) need absolute URLs that
link-unfurl bots can reach. They were derived from request.base_url, which
behind a TLS-terminating proxy with TRUST_PROXY_HEADERS=false would emit an
internal http:// origin a scraper cannot fetch.

- new BASE_URL setting (pydantic-settings), fail-fast on a malformed value
- abs_url() template helper: BASE_URL when set, else the request origin
- base.html / paste.html build og:image and og:url through abs_url()
- document BASE_URL in .env.example, README and docs/configuration.md
@kallioli kallioli changed the title fix: social-preview images for link unfurls + OCI-safe healthcheck fix: social-preview images + absolute URLs + OCI-safe healthcheck May 22, 2026
@kallioli
kallioli merged commit 5dad202 into main May 22, 2026
1 check passed
@kallioli
kallioli deleted the fix/link-preview-og-image branch May 22, 2026 21:13
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.

1 participant