Channels/Allowlists — Command Gating
Slash Command Authorization & Access Group Enforcement
Command gating restricts which users can execute slash commands (/reset, /model, /continue, etc.). It combines access group membership with per-authorizer configuration to produce a binary allow/block decision for each control command.
Access Groups Dual-Text Gate Configurable Modes Zero-Config Default
📚 What is this & why does it matter?
Slash commands can reset context, change models, or modify agent behavior. In group chats with multiple users, you don’t want everyone to have that power. Command gating ensures only authorized users (typically the agent owner) can execute control commands, while still allowing everyone to send regular messages.
💡 Real-world analogy
Think of a shared office printer. Anyone can print documents (send messages), but only IT administrators can change printer settings, clear the queue, or install updates (slash commands). The command gating system is the admin password check that runs before any configuration change is applied.
Contents
Type System
export type CommandAuthorizer = {
configured: boolean; // Whether this authorizer has any entries
allowed: boolean; // Whether the current sender matches
};
export type CommandGatingModeWhenAccessGroupsOff = "allow" | "deny" | "configured";
Resolution Algorithm
flowchart TD
START["resolveCommandAuthorizedFromAuthorizers()"]
START --> AG{"useAccessGroups?"}
AG -->|Yes| ANY_MATCH{"Any authorizer
configured AND allowed?"}
ANY_MATCH -->|Yes| ALLOW["authorized = true"]
ANY_MATCH -->|No| DENY["authorized = false"]
AG -->|No| MODE{"modeWhenAccessGroupsOff?"}
MODE -->|"allow"| ALLOW
MODE -->|"deny"| DENY
MODE -->|"configured"| CONFIGURED{"Any authorizer
configured?"}
CONFIGURED -->|No| ALLOW
CONFIGURED -->|Yes| FALLBACK{"Any configured
authorizer allowed?"}
FALLBACK -->|Yes| ALLOW
FALLBACK -->|No| DENY
export function resolveCommandAuthorizedFromAuthorizers(params: {
useAccessGroups: boolean;
authorizers: CommandAuthorizer[];
modeWhenAccessGroupsOff?: CommandGatingModeWhenAccessGroupsOff;
}): boolean {
const { useAccessGroups, authorizers } = params;
const mode = params.modeWhenAccessGroupsOff ?? "allow";
if (!useAccessGroups) {
if (mode === "allow") return true;
if (mode === "deny") return false;
// mode === "configured"
const anyConfigured = authorizers.some((e) => e.configured);
if (!anyConfigured) return true;
return authorizers.some((e) => e.configured && e.allowed);
}
return authorizers.some((e) => e.configured && e.allowed);
}
Control Command Gate
The control command gate adds the shouldBlock decision, which considers whether the message is a control command at all:
export function resolveControlCommandGate(params: {
useAccessGroups: boolean;
authorizers: CommandAuthorizer[];
allowTextCommands: boolean;
hasControlCommand: boolean;
modeWhenAccessGroupsOff?: CommandGatingModeWhenAccessGroupsOff;
}): { commandAuthorized: boolean; shouldBlock: boolean } {
const commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
useAccessGroups: params.useAccessGroups,
authorizers: params.authorizers,
modeWhenAccessGroupsOff: params.modeWhenAccessGroupsOff,
});
const shouldBlock = params.allowTextCommands
&& params.hasControlCommand && !commandAuthorized;
return { commandAuthorized, shouldBlock };
}
Dual-Text Control Command Gate
For channels with primary and secondary authorizer sources:
flowchart LR
PRI["Primary Authorizer
(owner/admin)"]
SEC["Secondary Authorizer
(access group)"]
PRI --> MERGE["Merge into authorizers[]"]
SEC --> MERGE
MERGE --> GATE["resolveControlCommandGate()"]
GATE --> RESULT["{commandAuthorized, shouldBlock}"]
export function resolveDualTextControlCommandGate(params: {
useAccessGroups: boolean;
primaryConfigured: boolean;
primaryAllowed: boolean;
secondaryConfigured: boolean;
secondaryAllowed: boolean;
hasControlCommand: boolean;
modeWhenAccessGroupsOff?: CommandGatingModeWhenAccessGroupsOff;
}): { commandAuthorized: boolean; shouldBlock: boolean }
Gating Modes Comparison
| Mode | Access Groups ON | Access Groups OFF | Use Case |
|---|---|---|---|
allow | Check authorizers | Always allow | Default; open access when no access groups |
deny | Check authorizers | Always deny | Locked down; require explicit access group setup |
configured | Check authorizers | Check if any configured; if yes, require match | Graceful fallback; respect existing config |
Interaction with Mention Gating
Command gating and mention gating interact: an authorized control command can bypass the mention requirement in group chats. This is handled by resolveMentionGatingWithBypass(), which checks whether the user is authorized for the detected command and, if so, sets shouldBypassMention = true.