Repository navigation
Pantry Host 0.5.0 — Share to Bluesky
Until now, Pantry Host's AT Protocol story was one-directional: you could browse, deep-link, and import any exchange.recipe.* record on the network, but your own recipes stayed home. 0.5.0 closes the round trip. Sign in with your Bluesky account and publish any recipe — or a whole menu — to your own PDS in one tap. Import a recipe, edit it, publish it back, import it again; nothing is lost that wasn't lossy on the way in. Your records live on your Personal Data Server and travel with your identity — Pantry Host infrastructure never stores them.
The write path is already proven in the wild: the first real records published from inside Pantry Host — a Wine Night menu with four recipes, photo blobs and all — are live on the network and browsable in the community feed.
✨ Highlights
🦋 One-tap publish (#33)
A Share to Bluesky button on every recipe and menu detail page, styled into the export row, on both tiers.
- Recipes →
exchange.recipe.recipe: ingredients, instructions, ISO-8601 durations, yield, keywords — with diet tags (vegan,gluten-free, …) promoted to the lexicon'ssuitableForDietdefinitions. - Menus →
exchange.recipe.collection: children are resolved cheapest-first — Reuse (already on your PDS), Cite (imported from Bluesky — your collection strongRefs the original author's record, never re-publishing their work as yours), or Publish inline. - Re-publish updates the same record in place (
putRecord, stable AT URI); Unpublish deletes it. Publish receipts are remembered per browser. - Real
createdAt— records carry your recipe's actual creation date, not publish time.
📷 Photos ride along
photoUrl is fetched, downscaled to the PDS blob cap (1600 px longest edge / ~950 KB, JPEG quality fallback), uploaded via uploadBlob, and attached as embed.images — the same field the import path reads, so photos survive the round trip. Small images pass through untouched (animated GIFs keep their animation). A photo failure never blocks a publish. The browser PWA's OPFS-stored photos resolve through a pluggable fetcher.
🪪 Attribution, preserved
Publishing an imported or adapted recipe emits the lexicon's adaptedFrom attribution — originalUri for Bluesky-imported recipes, sourceUrl for web imports — so provenance chains stay intact across the network. Auto-tags added on import (bluesky, mealdb, …) are stripped on publish so round trips don't re-ingest their own markers.
🔍 Preview before it leaves your kitchen
Every publish routes through a preview modal showing the exact JSON record that will be written, whose PDS it lands on, the per-child plan for collections (Reuse / Cite / Publish badges with a record-count summary), and any attribution. Confirming flips the modal to a confirmation step: the minted AT URI with click-to-copy, and — on real publishes — a QR code + share link to the record's own detail page.
🔐 OAuth with granular scopes
Sign-in via atproto OAuth + DPoP (@atproto/oauth-client-browser) requesting exactly what the feature needs and nothing more: repo:exchange.recipe.recipe, repo:exchange.recipe.collection, and blob:image/*. Hosted client metadata in production; the spec's loopback client on 127.0.0.1 for development. A Bluesky Account section in Settings shows who's signed in and offers sign-out.
🦺 Dry-run by default
A real publish takes two deliberate acts: set ATPROTO_PUBLISH_DRY_RUN=false and click through the preview. Dry runs mint visibly synthetic receipts (DRYRUN-… rkeys), and real publishes never trust them — no dry receipt is ever reused as a collection strongRef, re-published over, or "deleted" from a PDS it never reached. On the self-hosted tier the flag reaches the browser via a _document.tsx meta tag (Rex doesn't inline process.env into client bundles).
♿ Accessible confirmation
The publish outcome is announced through a persistent role="status" live region that mounts empty and is populated on success — the pattern screen readers reliably announce (WCAG 2.1 AA 4.1.3) — with focus moved to the confirmation heading as a second signal.
📚 Under the hood
- New shared modules:
atproto-publish.ts(record builders + publish/unpublish),pds-published.ts(receipts),contexts/BlueskyAuth.tsx(OAuth session), plusBlueskySignInModal,PublishPreviewModal,PublishToBlueskyButton,BlueskyAccountStatus, and OAuth callback routes — one implementation shared by both tiers. - Marketing: the AT Protocol section's publish milestone graduates from "Coming next" to a live tile linking a real published collection.
⚠️ Known limitation
Recipes whose photoUrl points at an external host without permissive CORS publish without their photo (gracefully — a console warning, never a failed publish). Recipes with locally-uploaded photos are unaffected. A proxy-assisted path is under consideration.
🧰 Prior groundwork in 0.4.1
The community feed indexer is now self-healing (watchdog reconnect, honest health checks, on-demand backfill) — so records you publish actually show up in the feed.
🤖 Generated with Claude Code