api/catalog.ts is a single Vercel serverless function, dispatched by an op query parameter, backed by the service-role Supabase client. It reads back exactly the public shape of the catalog — never a stream URL.
assertPublicPayload (src/catalog/public.ts) walks every response object before it goes out and throws if any key matches /url|stream|m3u8|referrer|user_agent|user-agent/i (with one explicit exception: watch_path, the in-app deep link). This is not documentation of a convention — it is a runtime assertion on every payload the API returns.

Operations

similar and trending both re-check channels.closed is null at read time against the ids their source table returned, since a channel can close between the nightly recommendation build and the request — the table itself is only refreshed once a night.

GET /api/epg

The compact guide ({ generatedAt, programmes: { [channelId]: [{ start, stop, title }] } }), served from the public guide bucket in Supabase Storage by api/epg.ts. The EPG workflow publishes a fresh copy every three hours; the route caches it at Vercel’s edge for five minutes and serves it stale for an hour while revalidating. The app reads this on load, so a guide refresh never needs a deploy. A missing object is a 404 with no-store, so the next publish is visible at once.

Configuring a client

Points a Catalog MCP client (or any other consumer) at GET /api/catalog. This variable is client-facing and safe to set — the endpoint it points to never returns stream URLs, by the same assertPublicPayload rule above.