Channels/Allowlists — Access Control Engine
Pattern-Based Sender Filtering & Command Authorization
The Allowlists sub-module controls who can interact with the OpenClaw agent. It compiles sender identity patterns into fast lookup sets, resolves DM and group allowFrom sources, gates slash commands behind access groups, and enforces mention requirements in group chats.
Allowlists Command Gating Mention Gating Access Groups
📚 What is this & why does it matter?
Without access control, any user on any platform could invoke the agent and run arbitrary commands. The allowlists sub-module is the gatekeeper — it decides, for every inbound message, whether the sender is authorized and what level of access they have.
💡 Real-world analogy
Think of a nightclub with a guest list. The bouncer (allowlist engine) has a compiled list of VIP names. When someone arrives, the bouncer checks their ID (sender identity candidates) against the list. A wildcard * entry means “everyone gets in.” For the VIP lounge (slash commands), there’s an additional access check (command gating). In group settings, the bouncer also checks if the guest called out the club owner by name (mention gating).
Architecture
flowchart TD
subgraph Input["Inbound Message"]
SENDER["Sender Identity"]
CMD["Command Detected?"]
MENTION["Was @mentioned?"]
end
subgraph Allowlist["Allowlist Engine"]
COMPILE["compileAllowlist()"]
RESOLVE["resolveCompiledAllowlistMatch()"]
CANDIDATES["Identity Candidates"]
end
subgraph AllowFrom["AllowFrom Sources"]
DMMERGE["mergeDmAllowFromSources()"]
GROUPFROM["resolveGroupAllowFromSources()"]
end
subgraph Gating["Command & Mention Gating"]
CMDGATE["resolveControlCommandGate()"]
MGATE["resolveMentionGating()"]
end
subgraph Result["Access Decision"]
ALLOW["Allowed"]
DENY["Denied / Blocked"]
end
SENDER --> CANDIDATES
CANDIDATES --> COMPILE
COMPILE --> RESOLVE
RESOLVE --> ALLOW
RESOLVE --> DENY
DMMERGE --> COMPILE
GROUPFROM --> COMPILE
CMD --> CMDGATE
CMDGATE --> ALLOW
CMDGATE --> DENY
MENTION --> MGATE
MGATE --> ALLOW
MGATE --> DENY
Allowlist Matching Pipeline
The core matching function accepts a compiled allowlist and an ordered array of identity candidates. Each candidate has a value (the identifier string) and a source (how it was derived). The engine checks candidates in order; the first match wins.
Match Sources
| Source | Description | Example |
|---|---|---|
wildcard | Universal match via * entry | allowFrom: ["*"] |
id | Platform-specific numeric/string ID | "123456789" |
username | Platform username/handle | "johndoe" |
name | Display name match | "John Doe" |
tag | Platform tag (e.g., Discord discriminator) | "johndoe#1234" |
prefixed-id | Channel-prefixed ID | "telegram:123456" |
prefixed-user | Channel-prefixed username | "slack:johndoe" |
prefixed-name | Channel-prefixed display name | "discord:John" |
slug | Normalized slug form | "john-doe" |
localpart | Email local part | "john" from john@example.com |
Compilation
export function compileAllowlist(entries: ReadonlyArray<string>): CompiledAllowlist {
const set = new Set(entries.filter(Boolean));
return {
set,
wildcard: set.has("*"),
};
}
AllowFrom Source Resolution
DM and group contexts have separate allowFrom sources that are merged before compilation:
flowchart LR
subgraph DM["DM Context"]
AF["allowFrom config"]
SAF["storeAllowFrom"]
DMP["dmPolicy"]
end
subgraph Group["Group Context"]
GAF["groupAllowFrom"]
FAF["fallbackToAllowFrom"]
end
DM --> MERGE["mergeDmAllowFromSources()"]
Group --> GRESOLVE["resolveGroupAllowFromSources()"]
MERGE --> COMPILED["CompiledAllowlist"]
GRESOLVE --> COMPILED
export function mergeDmAllowFromSources(params: {
allowFrom?: Array<string | number>;
storeAllowFrom?: Array<string | number>;
dmPolicy?: string;
}): string[] {
const storeEntries = params.dmPolicy === "allowlist" ? [] : (params.storeAllowFrom ?? []);
return [...(params.allowFrom ?? []), ...storeEntries]
.map((value) => String(value).trim())
.filter(Boolean);
}
Command Gating
Slash commands can be restricted to authorized users only. The gating system supports three modes when access groups are disabled:
| Mode | Behavior |
|---|---|
allow | All users can use slash commands (default) |
deny | No users can use slash commands |
configured | Only explicitly configured authorizers can use commands |
export function resolveControlCommandGate(params: {
useAccessGroups: boolean;
authorizers: CommandAuthorizer[];
allowTextCommands: boolean;
hasControlCommand: boolean;
modeWhenAccessGroupsOff?: CommandGatingModeWhenAccessGroupsOff;
}): { commandAuthorized: boolean; shouldBlock: boolean }
Mention Gating
In group chats, the agent can require an @mention before processing. The system handles implicit mentions, bypass rules (authorized control commands), and detection capability flags.
export function resolveMentionGating(params: MentionGateParams): MentionGateResult {
const implicit = params.implicitMention === true;
const bypass = params.shouldBypassMention === true;
const effectiveWasMentioned = params.wasMentioned || implicit || bypass;
const shouldSkip = params.requireMention && params.canDetectMention && !effectiveWasMentioned;
return { effectiveWasMentioned, shouldSkip };
}
Focus Deep-Dives
🔎 Allowlist Matching
Core matching engine: compiled sets, candidate resolution, match source tracking, and wildcard semantics.
🔒 Command Gating
Slash command authorization with access groups, dual-text control, and configurable deny modes.
👥 AllowFrom Rules
DM/group source merging, store-backed allowlists, fallback strategies, and sender ID validation.