feat(projects): user-initiated slug registration + durable backend cache#66
Conversation
Lets a project's author register (and later change) a unique URL slug for their Nostr project, and guarantees a registered project always resolves server-side — fixing the "Proyecto no encontrado" class of bug where a thinly-propagated event missing from the broad snapshot stranded the page. Registry (slug → project): - New `POST /api/projects/registry` (+ `GET` availability check). The owner signs a NIP-98 (kind 27235) auth event; the backend verifies ownership against the project's own relay event, La-Crypta-signs the updated kind-30078 registry event, publishes it, and returns it for the client to also republish (belt-and-suspenders). - Changeable slugs modeled append-only: a change appends a new entry that becomes canonical (latest-wins per id in buildRegistryState); the old slug stays an owner-locked redirect alias. Preserves the monotonic-entries invariant, old links, and 308/canonical behaviour. - Hardening: server-side ownership + recognized-author gate (registry/snapshot), author-filtered project fetch, curated-id rejection, reserved-slug + shape validation, per-author quota, and an Upstash write-lock serializing the read-modify-write. Shared getBackendSecretBytes/buildAndSignRegistryEvent reused by the auto-sync path. Durable "never not found" cache: - New long-lived per-project Upstash copy (projectDurable), written on registration, targeted refetch, and the warm cron (registered projects only). - getProjectWithDurableFallback serves it when the live #d scan misses, with an expected-author guard so a poisoned copy is never served at a victim's canonical URL. `/api/nostr/refresh` gains a targeted per-project refetch branch (author-filtered) instead of a full 6s snapshot rescan. - Consistent lowercased KV keys across all writers/readers. Client: - Owner-only "Registrar/Cambiar URL" card (ProjectSlugCard) on the project page with prefilled slug, live availability, sign + republish, and redirect to the new canonical slug. New projectRegistryClient helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 30 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (13)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
What & why
Adds a user-initiated project-slug registration flow and a backend-first durable cache that guarantees a registered project always resolves server-side.
This fixes the "Proyecto no encontrado" class of bug (e.g.
/projects/pibot): a name-slug URL depended on the project's Nostr event being present in the broad submissions snapshot, and when a thinly-propagated event missed that scan the page degraded to a soft-404 with no server-side fallback. It also gives owners a real way to claim a clean URL instead of relying on the automatic name-derived registry sync.How it works
Registry (
slug → project)POST /api/projects/registry(+GET ?slug=availability check). The owner signs a NIP-98 kind-27235 auth event; the backend verifies ownership against the project's own relay event, La-Crypta-signs the updated kind-30078 registry event, publishes it, and returns it so the client also republishes (belt-and-suspenders).buildRegistryState); the old slug stays an owner-locked redirect alias. This preserves the registry's monotonic-entries invariant, keeps old links alive, and drives the existing id→slug canonical redirect.getBackendSecretBytes/buildAndSignRegistryEventare reused by the existing auto-sync path.Durable "never not found" cache
projectDurable), written on registration, on targeted refetch, and by the warm cron (registered projects only).getProjectWithDurableFallbackserves it when the live#dscan misses; the resolver uses it in the registered-slug + UUID branches.POST /api/nostr/refreshgains a targeted per-project refetch branch (author-filtered, ~4.5s) instead of a full ~6s snapshot rescan.Client
ProjectSlugCard): prefilled slug, live availability, sign + republish, redirect to the new canonical slug. NewprojectRegistryClienthelper.Security hardening (from adversarial review)
#devent (#dis not owner-exclusive for NIP-33).expectedAuthorguard, so a poisoned copy is never served at a victim's canonical URL.Reviewer notes
<Suspense>undercacheComponents): a rawcurlsees200+ a correct<link rel="canonical">, while a real browser navigates to the slug.LACRYPTA_NSECis a Vercel Sensitive var and pulls back empty viavercel env pull; the registration backend is therefore unavailable in local prod-var runs (returns "Registro de URLs no disponible"). It works on the real deployment (key injected at runtime) and in the isolated dev setup (local relay + throwaway key).Verification
tsc --noEmitclean;pnpm buildclean (286 pages). Full flow exercised end-to-end in the isolated dev env (local relay + throwaway key): registered slugs via the real UI (sat-checkout-pos,nostr-id-demo) and API, confirmed/projects/<slug>renders and the id URL redirects to it, and availability is owner-aware.🤖 Generated with Claude Code
Summary by CodeRabbit