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