Communication & Networking
Access Control
Authorize actions, queue publishes, and event subscriptions with explicit hooks.
Use access control to decide what authenticated clients are allowed to do.
This is authorization, not authentication:
- Use authentication to identify who is calling.
- Use access-control rules to decide what they can do after connecting.
Permission Surfaces
RivetKit authorization is explicit per surface:
onBeforeConnectrejects unauthenticated or malformed connections.- Action handlers (
actions.*) enforce action permissions. queues.<name>.canPublishallows or denies inbound queue publishes.events.<name>.canSubscribeallows or denies event subscriptions.
Fail By Default
Use deny-by-default rules everywhere:
- Keep
onBeforeConnectstrict and reject invalid credentials. - In each action, explicitly allow expected roles and throw
forbiddenotherwise. - In
canPublishandcanSubscribe, returntrueonly for allowed roles and end withreturn false.
import { actor, event, queue, UserError } from "rivetkit";
type ConnParams = {
authToken: string;
};
type ConnState = {
userId: string;
role: "member" | "admin";
};
async function authenticate(
authToken: string,
): Promise<ConnState | null> {
if (authToken === "admin-token") {
return { userId: "admin-1", role: "admin" };
}
if (authToken === "member-token") {
return { userId: "member-1", role: "member" };
}
return null;
}
export const chatRoom = actor({
state: { messages: [] as Array<{ userId: string; text: string }> },
onBeforeConnect: async (_c, params: ConnParams) => {
if (!params.authToken) {
throw new UserError("Forbidden", { code: "forbidden" });
}
const session = await authenticate(params.authToken);
if (!session) {
throw new UserError("Forbidden", { code: "forbidden" });
}
},
createConnState: async (_c, params: ConnParams): Promise<ConnState> => {
const session = await authenticate(params.authToken);
if (!session) {
throw new UserError("Forbidden", { code: "forbidden" });
}
return session;
},
events: {
messages: event<{ userId: string; text: string }>(),
moderationLog: event<{ entry: string }>({
canSubscribe: (c) => {
if (c.conn?.state.role === "admin") {
return true;
}
return false;
},
}),
},
queues: {
moderationJobs: queue<{ action: "ban"; userId: string }>({
canPublish: (c) => {
if (c.conn?.state.role === "admin") {
return true;
}
return false;
},
}),
},
actions: {
sendMessage: (c, text: string) => {
const role = c.conn?.state.role;
const userId = c.conn?.state.userId;
if (!userId || (role !== "member" && role !== "admin")) {
throw new UserError("Forbidden", { code: "forbidden" });
}
const message = { userId, text };
c.state.messages.push(message);
c.broadcast("messages", message);
},
},
});
Return Value Contract
canPublish and canSubscribe must return a boolean:
true: allowfalse: deny withforbidden
Returning undefined, null, or any non-boolean throws an internal error.
Notes
canPublishonly applies to queue names defined inqueues.- Incoming queue messages for undefined queues are ignored and logged as warnings.
canSubscribeonly applies to event names defined inevents.- Broadcasting an event not defined in
eventslogs a warning but still publishes.