What changed
The Open Graph image route now serves one signed card endpoint: /open-graph/card.jpg?token=....
Every page builds the card data it wants to advertise, and HiveWeb.OpenGraph signs that payload into the image address. The image controller verifies the token and renders the card directly, so it no longer reconstructs page-specific data from route names.
The card data field previously named eyebrow is now section_label, and the shared card validation bounds the rendered title, description, section label, path, author, and highlights before generating an image.
Why
The old design made the page metadata and image route calculate the card independently. That allowed them to drift, which caused valid page metadata to advertise image addresses that returned Not found.
Root cause
The specs page generated its Open Graph protocol image address from all visible specs, but the image controller previously validated the same address against draft specs only. The hash did not match once non-draft specs existed.
Approach
The page now signs the exact card payload used for the image address. The image controller only verifies the signature and serves the signed card data.
The token uses a stable signing timestamp so unchanged card data produces the same image address. Object storage still keys images by the normalized card hash.
Impact
Shared page cards no longer depend on per-page controller reconstruction, so all Open Graph images use the same path and verification flow.
Existing page authorization still controls who can see pages that contain private card data. Anyone with a signed image address can render the card contained in that address, which matches the signed-payload model.
Validation
mix test test/hive_web/open_graph_test.exs
mix test test/hive_web/controllers/open_graph_controller_test.exs
mix test test/hive_web/live/spec_live/show_test.exs
mix test test/hive_web/live/spec_live/index_test.exs
mix compile --warnings-as-errors
mix test
git diff --check