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.