Repo: github.com/midimurphdesigns/anchor
Live demo: anchor.kevinmurphywebdev.com
Read the full story: Building anchor
Anchor is an AI-native product catalog. Every product has a human page AND three statically-cached LLM-facing endpoints. A /.well-known/agents.json descriptor publishes the site's capabilities in the Agentic Commerce Protocol shape. A delegated-authority checkout endpoint runs an eight-check pipeline before any charge fires. A live AEO dashboard classifies 13 known LLM crawlers by user-agent and shows the per-product fetch counts. 40 prerendered LLM-facing routes, ~5ms TTFB on cache hits, 11/11 assertions pass on the checkout test suite.
How it's built
Next.js 16 App Router with cacheComponents: true, TypeScript strict, zero any. The whole catalog of 10 products generates 30 LLM-facing static routes via generateStaticParams, three format-specific surfaces per slug (/agent/markdown, /agent/json, /agent/plain), plus 10 redirect entries on the canonical /agent URL. Bodies are pure functions of the in-memory catalog wrapped in 'use cache' with cacheLife('hours') and cacheTag('product:<slug>'), so a sale invalidates exactly the one product entry on revalidateTag and leaves the other nine cached.
The product detail page uses Partial Prerendering. Product copy, specs, JSON-LD, and the buy CTA prerender at build time. A single dynamic Suspense hole reads headers() to opt out of static rendering and streams in the live agent-fetch tally from Redis at request time. Cached shell + live hole, served as one document.
The agent surface
LLM-facing content is split across three format-specific routes so each one can prerender independently. /agent/markdown returns the combined block: a citation opening line (Source: <canonical URL>, <brand> <name> is listed at <price>...), a JSON-LD Schema.org Product/Offer in a fenced code block, then prose with specs as bullets and a closing canonical URL line. /agent/json returns just the JSON-LD. /agent/plain returns just the prose. Each format ships ten prerendered files, served from the edge cache.
Telemetry rides outside the body. The proxy (Next 16's renamed middleware.ts) runs at the edge on every /agent request regardless of cache status, classifies the User-Agent against 13 known LLM crawlers, and queues a Redis write via after(). Cache hits stay observed because the proxy still executes. The tradeoff that mattered: putting the logging inside the route handler would have missed every cache hit.
Delegated-authority checkout
POST /api/agent/checkout runs an ordered eight-check pipeline. Signature verifies first (HMAC-SHA256 with constant-time comparison, === would leak signature bytes byte-by-byte via timing). Expiry second. Agent binding third. Scope fourth (action + SKU + maxCents must all match). Nonce (the token's jti) fifth, single-use via Redis. Idempotency-Key sixth, cached responses returned without re-running the pipeline. Inventory + floor-price seventh. Charge + tag invalidation eighth.
The order is cheapest-first so an attacker spamming malformed tokens never touches Redis. The crypto rejects them in ~1ms. Production-ready in shape, demo-ready in price (HMAC instead of asymmetric ed25519, the verification logic is identical either way, only the key model changes). Five test scenarios in scripts/test-checkout.ts cover happy path, replay attack, over-budget, wrong-SKU, and idempotent retry; eleven assertions all pass.
Generative UI with structured-output dispatch
A /compare page lets the user pick two products. The model decides which UI shape fits, side-by-side spec table for same-category rivals, pros/cons split for overlapping use cases, recommendation paragraph for unrelated products. Implemented with streamObject from AI SDK v6 against a Zod discriminated union (three shapes, each tagged with a kind literal). The client switches on kind and React-renders the matching component. Type-safe at every boundary; the model never emits markup, only structured fields.
Self-documenting build
Every route's render mode is documented in /docs/rendering, a single page that lists the full route table with the rationale for each choice (SSG vs PPR vs Dynamic) plus the ten Next 16 + AI SDK v6 primitives the build leans on, with file paths to grep for each. A dev-only render inspector overlays colored boundaries on every annotated region with a counterfactual savings panel that compares the current TTFB against a fully-dynamic baseline. Toggle it on; the architecture becomes visible.
Artifacts worth reading
lib/principal.ts. The eight-check verifier. HMAC, constant-time compare, ordered failure modes with stable codes for HTTP mapping.lib/agents-descriptor.ts. The single source of truth for/.well-known/agents.json,/llms.txt, and the human/agentspage. Documentation IS the implementation.proxy.ts. Cited-by attribution + AEO logging cross-cutting concerns. Whereafter()rides on cached routes.app/products/[slug]/agent/markdown/route.ts. The static citation-shaped LLM surface; nine lines of route logic, every byte pure.lib/render-modes.ts. The render-mode catalog the docs page reads from. New routes get an entry here and the documentation updates automatically.
The trade-offs
The HMAC token shape is a demo-grade simplification. A production deployment would use asymmetric signatures (ed25519) issued by the user's wallet, with anchor verifying via the user's public key, the eight-check logic is identical, only the key model changes. The catalog of ten products is fictional; pushing this to real ecommerce data would land via a Supabase loader behind the same 'use cache' wrapper without changing the cache shape. The in-memory Redis fallback (for local dev without Upstash) is intentionally per-process and doesn't survive restarts; production needs Upstash configured for cross-instance state. The comparison agent uses Claude Haiku 4.5 at temperature 0.3 for fast structured output; a production version would A/B test the model + temperature pair against the eval scoreboard. The architecture is shaped for these additions; the demo intentionally stops before them.