Skip to main content

Access Rules

EdgeBase Realtime uses three distinct access models for three channel types. All channels default to authenticated users only — even in development mode.

Overview​

Channel TypeRule SourceEvaluation Time
Database subscriptions (onSnapshot)Table's read access ruleOnce at subscribe time
Presence channelsrealtime.namespaces configOnce at subscribe time
Broadcast channelsrealtime.namespaces configOnce at subscribe/publish time

Database Subscriptions​

Database subscriptions reuse the table's existing read access rule. No additional configuration is needed.

// edgebase.config.ts
export default defineConfig({
databases: {
shared: {
tables: {
posts: {
access: {
read(auth, doc) { return auth !== null }, // ← also used for onSnapshot
update(auth, doc) { return auth?.role === 'admin' },
},
},
},
},
},
});

When a client calls .onSnapshot(), the server evaluates the read rule once at subscribe time using partial evaluation — the auth parameter is fully available, but doc fields are not yet known (the Proxy returns true for all property access). This means:

  • Rules that only check auth work as expected: (auth) => auth !== null
  • Rules that check doc fields pass at subscribe time but are not re-evaluated per event

After subscription, every database change event is delivered to the subscriber. Use server-side filters for per-event filtering.

Presence & Broadcast Channels​

Presence and Broadcast channels use realtime.namespaces in edgebase.config.ts with wildcard pattern matching:

export default defineConfig({
realtime: {
namespaces: {
// Presence: all channels require authentication
'presence:*': {
access: {
subscribe(auth) { return auth !== null },
},
},

// Broadcast: public channels anyone can listen, only auth can send
'broadcast:public-*': {
access: {
subscribe() { return true },
publish(auth) { return auth !== null },
},
},

// Broadcast: game channels require authentication for both
'broadcast:game-*': {
access: {
subscribe(auth) { return auth !== null },
publish(auth) { return auth !== null },
},
},

// Admin broadcast: only admins can subscribe
'broadcast:admin': {
access: {
subscribe(auth) { return auth?.role === 'admin' },
},
},
},
},
});

Rule Types​

RuleDescriptionSignature
subscribeWho can join the channel and receive messages(auth, channelId) => boolean
publishWho can send broadcast messages to the channel(auth, channelId) => boolean
Publish rules are not enforced at runtime

publish rules are defined in configuration but are not currently evaluated at runtime. Only subscribe rules are enforced when a client connects. If you need to restrict who can publish to a channel, implement server-side validation in your application logic.

Wildcard Matching​

The * wildcard matches any substring in the channel name:

PatternMatchesDoesn't Match
presence:*presence:lobby, presence:game-1broadcast:lobby
broadcast:public-*broadcast:public-chat, broadcast:public-newsbroadcast:private-chat
broadcast:game-*broadcast:game-lobby, broadcast:game-123broadcast:chat

Default Policy​

Channels with no matching namespace rule default to:

Authenticated users only — deny by default, regardless of release mode.

This means:

  • In release: true (production): unauthenticated users are denied
  • In release: false (development): unauthenticated users are still denied

This is intentional — realtime channels should always require explicit security configuration.

Permission Re-evaluation​

When a client refreshes their JWT token, the server automatically re-evaluates all active subscriptions:

  1. Client sends a new auth message with the refreshed token
  2. Server verifies the new token and updates the user identity
  3. Server re-evaluates every subscribed channel against the new claims
  4. Channels that fail the re-evaluation are collected into revokedChannels
  5. Server sends auth_refreshed response with the list of revoked channels
  6. SDK automatically cleans up local subscriptions and fires subscription_revoked events
client.realtime.on('subscription_revoked', ({ channel }) => {
console.warn('Lost access to channel:', channel);
// Optionally: show UI notification, redirect, etc.
});

This process is graceful — the WebSocket connection stays open, and only the unauthorized channels are removed. All other subscriptions continue working normally.

info

Re-evaluation only happens at token refresh time (typically every 15–60 minutes). Between refreshes, the original permissions remain in effect. For immediate revocation, use the Instant Kick mechanism below.

Instant Kick​

For emergency situations (account compromise, abuse, etc.), use the instant kick mechanism to forcefully disconnect a user:

  1. An admin action writes a KV blacklist entry for the user
  2. The server force-closes the user's WebSocket connection with a FORCE_DISCONNECT message
  3. When the client reconnects, the access rules re-evaluate and deny access

This is more aggressive than re-evaluation — the user's connection is immediately terminated rather than waiting for the next token refresh.

When to Use Each​

ScenarioMechanismUX Impact
Role change (e.g., downgrade from admin)Re-evaluationSmooth — specific channels removed silently
Account suspension / banInstant kickDisruptive — WebSocket closed immediately
Normal JWT expiryRe-evaluationTransparent — SDK handles automatically
Security incidentInstant kickImmediate — connection terminated