Landing Pages — Hub Package Pages

landing-pages specs/landing-pages/packages.kmd

Estrutura, meta tags Open Graph + Twitter Card, e composição da OG image para páginas de pacote individual no Koder Hub (`hub.koder.dev/apps/{slug}`, `/skills/{slug}`, `/bundles/{slug}`). Garante que sharing via WhatsApp/Facebook/Twitter/etc. mostre ícone + nome + descrição do pacote, não thumbnail genérico.

When this spec applies

All triggers

Specification body

Spec — Hub Package Pages (Open Graph + Social Sharing)

Status: Normative Applies to: Every package page rendered by the Koder Hub web client at https://hub.koder.dev/apps/{slug} (and equivalent under future kpkg.type paths: /skills/{slug}, /bundles/{slug}). Origin: Bug observed on 2026-04-29 — sharing a Koder Hub package page via WhatsApp showed the generic Store thumbnail instead of the package's own icon, name and description, because the SPA serves a static OG-tagged index.html for every route.


1. Requirement

Every Hub package page MUST be discoverable to social-media scrapers — WhatsApp, Facebook, Twitter/X, LinkedIn, Telegram, Slack, iMessage, Discord — with package-specific Open Graph metadata that yields a rich preview containing:

  1. The package's icon (the same SVG/PNG master used by the package itself, scaled to OG dimensions).
  2. The package's name (name field in the package's kpkg.toml / Hub catalog entry).
  3. The package's brief description (description field, ≤200 chars).

Static SPA-served OG tags are insufficient and violate this spec.

2. Implementation Contract

The Hub backend (products/dev/hub/depot/, currently products/dev/store/depot/) MUST handle package page URLs as follows:

2.1 Routing

The web server intercepts GET requests to /apps/{slug}, /skills/{slug}, /bundles/{slug}. For these routes:

  • If the request comes from a scraper user-agent (regex covering facebookexternalhit, whatsapp, twitterbot, linkedinbot, slackbot, telegrambot, discordbot, applebot, redditbot, pinterest, skypeuripreview), or
  • If the query string contains ?og=1 (manual override for testing), or
  • Always (preferred — avoids fragile UA sniffing and benefits humans whose browsers prefetch links),

the server returns a server-rendered HTML shell that contains the complete OG/Twitter Card meta block for the specific package, followed by the SPA hydration script.

The hydrated SPA then takes over for interactive use as before. Result: humans see the same SPA UX, scrapers see correct meta.

2.2 Required meta tags per package page

<!-- Open Graph -->
<meta property="og:type"        content="website">
<meta property="og:site_name"   content="Koder Hub">
<meta property="og:url"         content="https://hub.koder.dev/apps/{slug}">
<meta property="og:title"       content="{name} — Koder Hub">
<meta property="og:description" content="{description}">
<meta property="og:image"       content="https://hub.koder.dev/apps/{slug}/og-image.png">
<meta property="og:image:width"  content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt"   content="{name} — {short_tagline}">

<!-- Twitter / X -->
<meta name="twitter:card"        content="summary_large_image">
<meta name="twitter:title"       content="{name} — Koder Hub">
<meta name="twitter:description" content="{description}">
<meta name="twitter:image"       content="https://hub.koder.dev/apps/{slug}/og-image.png">

<!-- Canonical + page <title>/<meta name="description"> as usual -->
<title>{name} — Koder Hub</title>
<meta name="description" content="{description}">
<link rel="canonical" href="https://hub.koder.dev/apps/{slug}">

2.3 OG image generation

Each package page exposes /apps/{slug}/og-image.png (and equivalent for skills, bundles). The image is dynamically composed by the Hub backend with the following layout (1200×630):

  • Left third (400×630): the package's icon, centered, with subtle drop shadow and the Hub's accent gradient as background. Icon is the master SVG rasterized to ~360×360 (transparent background preserved).
  • Right two-thirds (800×630): the package's name (large, bold, brand font), description (medium, two lines max with ellipsis), and the Koder Hub wordmark + URL in the bottom-right corner.

The composer reads from the catalog entry (no need to fetch the kpkg artifact). Cache the rendered PNG on first request, invalidate on package version bump.

Reference implementation lives in products/dev/hub/depot/handlers/og_image.go (to be created; see ticket).

2.4 Fallback

If the catalog entry is missing icon, name, or description (data integrity failure), serve the generic Hub OG image (hub.koder.dev/og-image.png) with og:title="{slug} — Koder Hub" and a placeholder description, AND emit a backend warning to the audit log so the gap can be fixed.

3. Validation

  • Manual: curl -sA "WhatsApp/2.0" https://hub.koder.dev/apps/koder-talk | grep -E 'og:|twitter:' returns the package-specific block.
  • Automated: a regression test (per policies/regression-tests.kmd) hits the endpoint with each of the listed scraper user-agents AND with a regular browser UA, asserting that all required meta tags are present and that the image URL resolves to a non-empty PNG of the correct dimensions.
  • The /k-housekeep audit MUST flag any package in the Hub catalog whose page does not return correct OG meta when probed.

4. Coverage of Existing Pages

This spec applies retroactively. When the Hub backend gains the renderer (see ticket), every existing package in the registry is automatically covered without per-package work — the renderer reads from catalog data.

5. Non-Goals

  • Marketing landing pages (<product>.koder.dev/about, etc.) — covered by landing-pages/products.kmd. This spec is for the catalog pages on the Hub itself, not for product landing pages.
  • Per-package custom OG art. The composer renders a uniform layout for every package; allowing custom OG images is a future concern and would open the door to inconsistent shareability.
  • landing-pages/products.kmd — OG conventions for product landing pages (1200×630 image, Twitter Card summary_large_image, etc.)
  • landing-pages/catalog.kmd — top-level catalog page (hub.koder.dev) metadata
  • hub-RFC-006-unification-and-rename.md — Hub naming, supersedes "Store" references in any future spec text
  • Bug origin: shared a package page via WhatsApp on 2026-04-29; preview showed generic Koder Hub thumbnail instead of package-specific.

References