When you give an AI agent access to Discord, you are giving it the ability to send messages, kick members, delete channels, and more. Permission profiles and audit logging let you set boundaries and maintain accountability.
Why This Matters
Consider an autonomous agent that:
- Misinterprets a message and kicks a member instead of sending a warning
- Gets caught in a loop and floods a channel with messages
- Receives a prompt injection through a user message and executes unintended actions
Permission profiles prevent these scenarios by restricting which commands the agent can execute, regardless of what it tries to do.
Permission Profiles
discli ships with four built-in profiles:
Full access to all commands. No restrictions.
Allowed: *Denied: (none)Use for: trusted scripts running in controlled environments, admin bots operated by humans.
Messages, reactions, threads, typing, DMs, listening, and server info. Cannot kick, ban, delete channels, or manage roles destructively.
Allowed: message, reaction, thread, typing, dm, listen, serve, config, serverDenied: member kick, member ban, member unban, channel delete, role delete, role create, channel createUse for: AI support agents, chatbots, conversational bots. This is the recommended default for autonomous agents.
Read-only operations only. Can list, search, and inspect, but cannot send messages or modify anything.
Allowed: message list, message get, message search, message history, channel list, channel info, server list, server info, role list, member list, member info, reaction list, thread list, listen, config showDenied: * (everything else)Use for: monitoring, analytics, dashboards, log exporters.
Full access including moderation. Same as full but semantically indicates a moderation role.
Allowed: *Denied: (none)Use for: moderation bots that need kick/ban/role management. Consider pairing with --triggered-by for user permission verification.
Setting Permission Profiles
There are three ways to set the active profile, in order of precedence:
1. Per-invocation flag (highest priority)
discli --profile chat message send "#general" "Hello"discli --profile readonly serve --events messages2. Environment variable
export DISCLI_PROFILE=chatdiscli serve --events messages3. Persistent configuration
# Set the default profilediscli permission set chat
# Check current profilediscli permission show
# List all profilesdiscli permission profilesThe profile is stored in ~/.discli/permissions.json.
For autonomous agents, set the profile via the --profile flag in your subprocess call. This way the restriction is explicit in your code and cannot be changed by environment or config file changes.
How Profiles Work
When a command runs, discli checks:
- Is the command in the
deniedlist? If yes, block it. - Is the command in the
allowedlist? If yes, allow it. - Otherwise, block it.
The check uses prefix matching: if message is in the allowed list, then message send, message list, message delete, and all other message subcommands are allowed.
The special pattern * means “everything”:
"allowed": ["*"]— allow all commands"denied": ["*"]— deny all commands (then only explicitly allowed commands work)
# In your agent code, set the profile when launching serveproc = subprocess.Popen( ["discli", "--profile", "chat", "serve", "--events", "messages"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True,)If the agent tries to execute a denied command:
Error: Command 'member kick' is denied by your permission profile.In serve mode, the error comes back as a response event:
{"event": "error", "message": "Command 'serve' is denied by your permission profile."}The --triggered-by Flag
For moderation actions, the --triggered-by flag verifies that the Discord user who triggered the action actually has the required Discord permission:
discli member kick "My Server" "@spammer" --reason "Spam" --triggered-by 9876543210This checks:
- Is user
9876543210a member of the server? - Does the user have
kick_memberspermission? - Is the user the server owner or has Administrator?
If the check fails, the action is blocked and an audit log entry is created.
The --triggered-by flag is available on member kick, member ban, and member unban. It is your responsibility to pass the correct user ID from the event that triggered the action. An agent could theoretically pass its own ID or a fake ID, so this is a defense-in-depth measure, not a hard security boundary.
Permission mapping
| Action | Required Discord Permission |
|---|---|
member kick | kick_members |
member ban / member unban | ban_members |
Server owners and users with Administrator permission always pass the check.
Destructive Action Confirmation
Certain commands require confirmation before executing:
member kickmember banmember unbanchannel deletemessage deleterole delete
In interactive mode, discli prompts:
⚠ Destructive action: member kick (alice from My Server). Continue? [y/N]To skip the prompt in scripts and automation:
discli --yes member kick "My Server" "@spammer" --reason "Spam"Never use --yes in an autonomous agent without also using a restricted permission profile. The combination of --yes and --profile full means the agent can perform any destructive action without any safety check.
Audit Logging
Every destructive action is logged to ~/.discli/audit.log as JSONL:
{ "timestamp": "2025-03-15T10:30:00+00:00", "command": "member kick", "args": {"server": "My Server", "member": "alice#1234", "reason": "Spam"}, "result": "ok", "user": "9876543210"}Viewing the audit log
# Show last 20 entriesdiscli audit show
# Show last 50 entriesdiscli audit show --limit 50
# JSON output for processingdiscli --json audit show --limit 100
# Pipe to jq for analysisdiscli --json audit show --limit 1000 | jq 'group_by(.command) | map({command: .[0].command, count: length})'Clearing the audit log
discli audit clearWhat gets logged
| Action | Logged? |
|---|---|
member kick | Yes |
member ban | Yes |
member unban | Yes |
channel delete | Yes |
message delete | Yes |
role delete | Yes |
message send | No |
message list | No |
| Permission check failures | Yes |
Rate Limiting
discli applies rate limiting to destructive actions: 5 calls per 5 seconds. If you exceed this, the CLI pauses and prints a warning:
Rate limited. Waiting 3.2s...This prevents runaway agents from mass-deleting messages, kicking all members, or flooding the API. The rate limiter is global across all commands in a single process.
The rate limiter applies to destructive CLI commands (kick, ban, delete). Serve mode actions bypass the CLI-level rate limiter but are still subject to Discord’s own API rate limits. If you hit Discord’s limits, the action will fail with an HTTP error.
Custom Permission Profiles
You can define custom profiles in ~/.discli/permissions.json:
{ "active_profile": "support-agent", "profiles": { "support-agent": { "description": "Can chat and create threads, but not manage members or channels", "allowed": [ "message send", "message reply", "message list", "message get", "message search", "thread create", "thread send", "thread list", "reaction add", "reaction remove", "dm send", "typing", "serve", "listen", "channel list", "channel info", "server list", "server info", "member list", "member info" ], "denied": ["*"] } }}The "denied": ["*"] line means “deny everything not explicitly allowed.” This is the safest approach for custom profiles.
Start with the most restrictive profile that works, then add permissions as needed. It is much easier to add a missing permission than to recover from an agent that deleted a channel it should not have.
Recommended Configurations
AI Chatbot
discli --profile chat serve \ --events messagesCan send messages, create threads, add reactions. Cannot kick, ban, or delete anything.
Monitoring Dashboard
discli --profile readonly serve \ --events messages,membersCan list and search. Cannot send, modify, or delete anything.
Moderation Bot
discli --profile moderation serve \ --events messages,membersFull access. Use --triggered-by to verify the human moderator’s permissions.
CI Notifier
discli --profile chat \ message send "#deploys" "v2.1.0 deployed"Send-only. Does not need listen, serve, or member operations.
Security Checklist
Use environment variables for tokens
Never hardcode tokens. Use DISCORD_BOT_TOKEN or ~/.discli/config.json.
Set a permission profile
Default is full. Change it to chat or readonly for autonomous agents.
Pass --triggered-by for moderation
When your agent kicks or bans on behalf of a user, verify their Discord permissions.
Review audit logs regularly
Check discli audit show to ensure your agent is acting as expected.
Test with readonly first
Start with --profile readonly while developing, then upgrade to chat when ready.
Next Steps
- Building Agents — See Level 5 for a production agent with full safety
- Serve Mode — Protocol reference for all actions
- Scripting & Automation — Secure automation patterns