Channels and grants

The three private channel levels, and the short-lived grant a member obtains to join them.

  • Availability: Planned
  • Evidence: Read from source
  • Reference

Channels

Channel Name Carries
Job job:<job> Everything about one job: states, queue position, turns, stages, packages, modules, cache hits, retries, logs, usage
Application app:<application> Job state changes, pinned graphs, persisted inventories, releases, domains, notices and application changes
Organization org:<organization> Plan changes, credit, usage and application changes

A name matches ^(org|app|job):[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$. Each event belongs to exactly one channel, the narrowest that applies: the job when there is one, otherwise the application, otherwise the organization. A summary that also appears on a wider channel is a separate event there, with its own id and sequence.

Obtain a grant

Operation Request Capability Retry
realtime.grant POST /v1/realtime/grants events.subscribe natural

The body lists 1 to 20 distinct channels:

JSON
{ "channels": ["app:app_Shop0001x", "job:job_Prep0001x"] }
JavaScriptgrant.mjs
// Ask for a short-lived grant to join private channels. The service checks your
// access to the resource behind every channel; the name alone proves nothing.
const api = process.env.CDN_API_ORIGIN;
const token = process.env.CDN_TOKEN;

const response = await fetch(new URL('/v1/realtime/grants', api), {
	method: 'POST',
	headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
	body: JSON.stringify({ channels: [`app:${process.env.CDN_APPLICATION}`, `job:${process.env.CDN_JOB}`] })
});
const grant = await response.json();

if (!response.ok) {
	console.log(`${response.status} ${grant.error.code} — ${grant.error.message}`);
	process.exit(1);
}

console.log(`granted: ${grant.channels.join(', ')}`);
console.log(`denied or unknown: ${(grant.denied ?? []).join(', ') || 'none'}`);
console.log(`expires: ${grant.expires} — renew before then`);
console.log(`transport: ${grant.transport.kind}, event name ${grant.transport.event}`);
Member of the grant Meaning
token Short-lived permission to join exactly the listed channels. It is scoped to the caller and is never a service key.
channels The channels granted
denied Requested channels the caller may not join. They are indistinguishable from channels that do not exist.
expires When the grant ends. Renew before then.
transport.kind supabase-broadcast, or local for local development
transport.url, transport.key Where to connect, and the publishable client key of the transport when it needs one. Never a service-role key.
transport.event beyond-cdn-events/1: the broadcast event name that carries a microbatch. The same channel can also carry the broadcast event beyond-cdn-pointer/1, a pointer.

When no requested channel is visible to the caller the answer is 404 NOT_FOUND. Issuing a grant has no lasting effect, so a retry simply issues another one. Too many requests answer 429 RATE_LIMITED; honor Retry-After.

What a browser never receives

Job status is published only by authorized services. Browsers receive a caller-scoped grant and, if the transport needs one, a publishable key. Service-role keys, registry credentials and other tenants' data never reach a client, and never appear in events or logs.