> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Bot Security & DM Policy

> Cross-channel DM pairing, inbound trust defaults, and safe configuration patterns for production bot deployments.

Bot security enables safe deployment of PraisonAI agents across messaging channels with built-in protection against abuse and unauthorized access.

<Note>
  **Secure defaults landed in PraisonAI [#2038](https://github.com/MervinPraison/PraisonAI/pull/2038).** `group_policy` now defaults to `mention_only` (was `respond_all`), and `praisonai doctor --only gateway_security` **FAIL**s on channels with no `allowed_users`, `allowlist`, or `blocklist`. Set `GATEWAY_AUTH_TOKEN` for non-loopback gateway deployments.
</Note>

<Warning>
  **Field name is `allowed_users`, not `allowlist`.** The gateway YAML parser only recognizes `allowed_users:` and `allowed_channels:`. Examples using `allowlist:` will silently produce a bot with **no enforcement**. (Fixed in [PR #1791](https://github.com/MervinPraison/PraisonAI/pull/1791).)
</Warning>

Secure baseline for new configs (passes `gateway_security`):

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
channels:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}
    allowed_users:                    # required for gateway_security to PASS
      - "${TELEGRAM_OWNER_ID}"
    # group_policy: mention_only      # default since #2038 — omit unless you need respond_all
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Bot Security Flow"
        A[📱 Inbound DM] --> B[🔍 Security Check]
        B --> C[✅ Allow]
        B --> D[❌ Block]
        C --> E[🤖 Agent]
    end
    
    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff
    classDef block fill:#8B0000,stroke:#7C90A0,color:#fff
    
    class A input
    class B process
    class C,E output
    class D block
```

## Quick Start

<Steps>
  <Step title="Basic Security Setup">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.bots import BotConfig

    # Secure bot with allowlist
    config = BotConfig(
        allowed_users=["@your_username", "123456789"],
        unknown_user_policy="deny"  # Default secure behavior
    )

    agent = Agent(
        instructions="You are a helpful assistant",
        # Bot configuration handled by adapter
    )
    ```

    In YAML configuration:

    ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    channels:
      telegram_cfo:
        platform: telegram
        token: ${TELEGRAM_CFO_BOT_TOKEN}
        allowed_users: ${TELEGRAM_ALLOWED_USERS}   # comma-separated env var
        routes:
          default: cfo
    ```
  </Step>

  <Step title="Advanced Security Config">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.bots import BotConfig

    # Production security setup with pairing
    config = BotConfig(
        allowed_users=["@admin_user"],
        unknown_user_policy="pair",  # Secure pairing flow
        auto_approve_tools=True,     # For bot environments
        group_policy="mention_only"  # Only respond when mentioned
    )

    agent = Agent(
        instructions="Secure production assistant",
        # Configure with your bot adapter
    )
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Bot
    participant Security
    participant Agent
    
    User->>Bot: Send Message
    Bot->>Security: Check Authorization
    Security-->>Bot: Allowed/Blocked
    alt Allowed
        Bot->>Agent: Process Message
        Agent-->>Bot: Response
        Bot-->>User: Reply
    else Blocked
        Bot-->>User: Access Denied
    end
```

| Component | Purpose               | Status     |
| --------- | --------------------- | ---------- |
| Allowlist | Control access        | Conceptual |
| DM Policy | Message filtering     | Conceptual |
| Pairing   | Channel authorization | Conceptual |

***

## Security Model

<Note>
  **OpenClaw-style security for messaging bots.** This guide covers DM pairing, allowlists, and safe defaults across Telegram, Discord, Slack, WhatsApp, and other channels.
</Note>

PraisonAI treats **inbound DMs as untrusted input by default**. Production deployments should use explicit pairing and allowlists to prevent abuse, spam, and prompt injection from unknown senders.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
flowchart TD
    DM[Inbound DM] --> CHECK{Sender Check}
    CHECK -->|Unknown sender| DENY[Block/Ignore ❌]
    CHECK -->|In allowlist| ALLOW[Process ✅] 
    CHECK -->|Paired channel| ALLOW
    
    ALLOW --> APPROVAL{Tool Approval}
    APPROVAL -->|Approved| EXEC[Execute Tool]
    APPROVAL -->|Denied| BLOCK[Block Tool ❌]
    
    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef success fill:#10B981,stroke:#7C90A0,color:#fff
    classDef deny fill:#8B0000,stroke:#7C90A0,color:#fff
    
    class DM,DENY,BLOCK input
    class CHECK,APPROVAL process
    class ALLOW,EXEC success
```

## Safe Defaults by Channel

### Telegram

**Recommended production config:**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# bot.yaml
channels:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}
    allowed_users:
      - "@your_username"
      - "123456789"  # User ID
    admin_users: ${TELEGRAM_ADMIN_USERS}
    user_allowed_commands: "help,status,whoami"
    # group_policy defaults to mention_only — set explicitly only if you need respond_all
```

**Security features:**

* ✅ User allowlist by platform-native user ID (Telegram: numeric ID; Discord: snowflake; Slack: U...)
* ✅ Group mention-only policy
* ✅ Built-in command filtering — /help, /status, /new and custom commands respect `allowed_users` (PR #1835)
* ✅ Empty `allowed_users` now defers to `unknown_user_policy` (parity with Discord/Slack, [PR #1885](https://github.com/MervinPraison/PraisonAI/pull/1885)).

### Discord

**Recommended production config:**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# bot.yaml  
channels:
  discord:
    token: ${DISCORD_BOT_TOKEN}
    allowed_users:
      - "your_user_id"
      - "guild:server_id"  # Specific server only
    group_policy: "mention_only"
```

**Security features:**

* ✅ User/guild allowlist support (user ID format: Discord snowflake)
* ✅ Role-based restrictions
* ✅ Thread-safe message handling
* ⚠️ DMs from unknown users are processed by default

### Slack

**Recommended production config:**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# bot.yaml
channels:
  slack:
    token: ${SLACK_BOT_TOKEN}
    app_token: ${SLACK_APP_TOKEN}
    allowed_users:
      - "U0123456789"  # User ID
      - "C9876543210"  # Channel ID
    group_policy: "mention_only"
```

**Security features:**

* ✅ User/channel allowlist (user ID format: Slack U...)
* ✅ Enterprise Grid support
* ✅ Socket mode security
* ✅ Built-in DM filtering (mentions required)

### WhatsApp

**Recommended production config:**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# bot.yaml
channels:
  whatsapp:
    allowed_users:
      - "+1234567890"    # Phone numbers
      - "group123@g.us"  # Group IDs
    blocklist:
      - "+spam_number"
```

**Security features:**

* ✅ **Strong default security** - allowlist required for DMs
* ✅ Phone number + group allowlists
* ✅ Built-in self-chat detection
* ✅ Automatic spam filtering

<Tip>
  WhatsApp has the **strongest security defaults** and serves as the reference implementation for other channels.
</Tip>

## Admin Users & Per-Command Access

Layer command-level restrictions on top of user allowlists: `admin_users` grants full command access; `user_allowed_commands` limits everyone else to a comma-separated list. `/help` and `/whoami` are always permitted.

<Card title="Command Access Control" icon="shield-keyhole" href="/docs/features/bot-command-access-control">
  Full guide to admin\_users and user\_allowed\_commands
</Card>

## Owner-DM Pairing

The pairing system is now shipped and enables owner-approval for unknown users with inline Approve/Deny buttons sent directly to your DM.

For production deployments, use **owner-DM pairing** to authorize unknown users dynamically:

### 1. Set Callback Secret

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export PRAISONAI_CALLBACK_SECRET="$(openssl rand -hex 32)"
```

<Warning>
  Without `PRAISONAI_CALLBACK_SECRET`, inline-button callbacks will **not work across restarts**. Set this in production.
</Warning>

### 2. Configure Unknown User Policy

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.bots import BotConfig

config = BotConfig(
    token="your-bot-token",
    unknown_user_policy="pair",     # Enable owner-approval workflow
    owner_user_id="123456789",      # Your platform user ID
)

agent = Agent(
    name="Support Bot",
    instructions="Help users with their questions",
)
```

### 3. Owner Approval Workflow

When an unknown user messages your bot:

1. Bot generates a pairing code
2. **Owner receives DM with inline Approve/Deny buttons**
3. Owner clicks Approve → User is permanently approved
4. Owner clicks Deny → Request is rejected

**CLI Fallback:** If `owner_user_id` is not set, the bot replies:

```
Your pairing code: abc12345. Ask the owner to run: praisonai pairing approve telegram abc12345
```

### 4. Manual Approval (CLI)

Owners can approve pairing requests manually:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Approve a specific pairing code
praisonai pairing approve telegram abc12345

# Approve for Discord
praisonai pairing approve discord def67890

# Approve for Slack
praisonai pairing approve slack ghi13579
```

## Gateway Pairing

For production deployments, use **gateway pairing** to authorize channels dynamically with the shipped pairing system:

<Note>
  The gateway secret is optional - if unset, a per-install secret is auto-generated at `<store_dir>/.gateway_secret` with `0600` permissions and reused across restarts.
</Note>

### Enable Pairing Policy

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents.bots import BotConfig

config = BotConfig(
    allowed_users=["@owner"],
    unknown_user_policy="pair"  # Enable pairing for unknown users
)

# Unknown users will automatically receive pairing codes when they DM the bot
```

### Approve Pairing Requests

When unknown users DM your bot, they receive pairing codes. Approve them via CLI:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# User receives: "Your pairing code: ABCD1234"
# Owner approves:
praisonai pairing approve telegram ABCD1234 --label "alice"
```

### Manage Pairings

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# List all paired channels
praisonai pairing list

# Revoke access for specific channel
praisonai pairing revoke telegram 987654321

# Clear all pairings
praisonai pairing clear --confirm
```

<Tip>
  For detailed pairing documentation, see the [Bot Pairing](/docs/features/bot-pairing) guide.
</Tip>

### List Pending Requests

List all pending pairing codes waiting for approval:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.gateway.pairing import PairingStore

store = PairingStore()

# All pending codes across every channel
for req in store.list_pending():
    print(req["code"], req["channel_type"], req["channel_id"], req["age_seconds"])

# Filter by channel
telegram_only = store.list_pending(channel_type="telegram")
```

**Response Schema:**

| Key            | Type          | Source    | Notes                                                          |
| -------------- | ------------- | --------- | -------------------------------------------------------------- |
| `code`         | `str`         | canonical | 8-char pairing code                                            |
| `channel_type` | `str`         | canonical | e.g. `"telegram"`, `"discord"`, `"slack"`, `"whatsapp"`        |
| `channel_id`   | `str \| None` | canonical | Bound channel id if code was generated with one                |
| `created_at`   | `float`       | canonical | Unix timestamp (seconds) when code was generated               |
| `channel`      | `str`         | UI alias  | Same value as `channel_type`, kept for UI banner compatibility |
| `user_id`      | `str`         | UI alias  | Currently equals `code` (see note in `approve()` docstring)    |
| `user_name`    | `str`         | UI alias  | Formatted as `"User {code}"`                                   |
| `age_seconds`  | `int`         | UI alias  | `int(now - created_at)`                                        |

<Note>
  Canonical keys (`code`, `channel_type`, `channel_id`, `created_at`) are the stable contract. The `channel`, `user_id`, `user_name`, and `age_seconds` aliases are provided for UI consumers and should not be relied on for scripting — use the canonical keys.
</Note>

### CLI Commands

Use the `praisonai pairing` commands to manage pairings from the command line:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# List all paired channels
praisonai pairing list

# Approve a pairing code (this is the exact command shown to users)
praisonai pairing approve telegram abc12345

# Revoke a paired channel
praisonai pairing revoke telegram @username

# Clear all paired channels
praisonai pairing clear
```

**Available Commands:**

| Command                                                | Purpose                        | Required Args            |
| ------------------------------------------------------ | ------------------------------ | ------------------------ |
| `praisonai pairing list`                               | List all paired channels       | —                        |
| `praisonai pairing approve PLATFORM CODE [CHANNEL_ID]` | Approve an 8-char pairing code | `platform`, `code`       |
| `praisonai pairing revoke PLATFORM CHANNEL_ID`         | Revoke a paired channel        | `platform`, `channel_id` |
| `praisonai pairing clear`                              | Clear all paired channels      | —                        |

**Platform values:** `telegram`, `discord`, `slack`, `whatsapp`

**Pairing Flow:**

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Bot
    participant PairingStore
    participant CLI
    
    User->>Bot: unknown message (triggers pairing)
    Bot->>PairingStore: generate_code(channel_type, channel_id)
    PairingStore-->>Bot: abc12345
    Bot->>User: Ask owner to run: praisonai pairing approve telegram abc12345
    CLI->>PairingStore: approve(telegram, abc12345)
    PairingStore->>PairingStore: verify_and_pair()
    PairingStore-->>CLI: success
    User->>Bot: now authorised
    Bot-->>User: response
    
    classDef user fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef bot fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef success fill:#10B981,stroke:#7C90A0,color:#fff
    
    class User user
    class Bot,PairingStore,CLI bot
```

### 7. REST API

The gateway exposes REST endpoints for pairing management:

| Method | Path                   | Body / Query                         | Response                    | Auth Required |
| ------ | ---------------------- | ------------------------------------ | --------------------------- | ------------- |
| `GET`  | `/api/pairing/pending` | —                                    | `list_pending()` schema     | ✅             |
| `POST` | `/api/pairing/approve` | `{ "channel": str, "code": str }`    | `{ "approved": true, ... }` | ✅             |
| `POST` | `/api/pairing/revoke`  | `{ "channel": str, "user_id": str }` | `{ "revoked": true, ... }`  | ✅             |

**Example Usage:**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# List pending requests
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8000/api/pairing/pending

# Approve a code  
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -d '{"channel":"telegram","code":"abc12345"}' \
  http://localhost:8000/api/pairing/approve

# Revoke a channel
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -d '{"channel":"telegram","user_id":"@username"}' \
  http://localhost:8000/api/pairing/revoke
```

<Note>
  All endpoints are **authenticated** and **rate-limited**. Rate limits are applied per client IP with separate buckets for `pairing_pending`, `pairing_approve`, and `pairing_revoke` operations.
</Note>

## Doctor Security Check

Use the built-in doctor to audit your bot security configuration:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai doctor --category bots
```

The security check flags:

* ❌ **Missing allowlists** - channels without allowlist/blocklist
* ⚠️ **Permissive group policies** - `respond_all` in production
* ⚠️ **Missing gateway secret** - pairing codes won't persist
* ✅ **Secure configuration** - allowlists + mention-only policies

Example output:

```
Bot Security Config: WARN  
Security recommendations: 2 channel(s) could use stricter defaults

Details:
telegram: No allowlist/blocklist configured
discord: group_policy='respond_all' - consider 'mention_only' for security

Remediation: Consider allowlists for DM security and 'mention_only' group policy
```

## Self-Hoster Security Checklist

**Before going public with your bot:**

<AccordionGroup>
  <Accordion title="✅ DM Policy Configured" icon="message">
    * [ ] Allowlist configured for each channel
    * [ ] Unknown sender behavior defined (block/ignore/process)
    * [ ] Group policies set to `mention_only` or `command_only`
    * [ ] Blocklist configured for known spam sources
    * [ ] Built-in commands (/help, /status, /new) verified to respect allowlist
    * [ ] `admin_users` set for owner/admin user IDs
    * [ ] `user_allowed_commands` configured to limit regular users to read-only commands
    * [ ] Empty `allowed_users` is intentional **and** paired with `unknown_user_policy: "allow"`, **or** `allowed_users` is non-empty.
  </Accordion>

  <Accordion title="✅ Gateway Pairing Active" icon="link">
    * [ ] `PRAISONAI_GATEWAY_SECRET` set
    * [ ] `PRAISONAI_CALLBACK_SECRET` set (for inline buttons)
    * [ ] `unknown_user_policy` configured (`deny`/`pair`/`allow`)
    * [ ] `owner_user_id` set for inline approvals
    * [ ] Pairing workflow tested and verified
    * [ ] Revocation process documented
  </Accordion>

  <Accordion title="✅ Tool Approval Enabled" icon="shield-check">
    * [ ] Dangerous tools require approval (not auto-approved)
    * [ ] Approval backend configured (Slack/Telegram/HTTP)
    * [ ] Tool risk levels reviewed and appropriate
    * [ ] Approval timeout configured
  </Accordion>

  <Accordion title="✅ Monitoring & Alerts" icon="chart-line">
    * [ ] Bot security doctor check passing
    * [ ] Audit logging enabled (`praisonai.security.enable_audit_log`)
    * [ ] Injection defense active (`enable_injection_defense()` — returns `before_tool` + `before_agent` hook IDs)
    * [ ] Rate limiting configured for API calls
  </Accordion>

  <Accordion title="✅ Infrastructure Security" icon="server">
    * [ ] Bot tokens stored securely (not in code)
    * [ ] Environment variables encrypted at rest
    * [ ] Network access restricted (firewall rules)
    * [ ] Regular security updates scheduled
  </Accordion>
</AccordionGroup>

## Common Security Patterns

### 1. Staged Rollout

Start with restrictive settings and gradually open access:

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Stage 1: Internal testing
channels:
  telegram:
    allowed_users: ["@internal_team"]
    group_policy: "command_only"

# Stage 2: Trusted users
channels:
  telegram:
    allowed_users: ["@internal_team", "@trusted_users"]  
    group_policy: "mention_only"

# Stage 3: Public (with safety nets)
channels:
  telegram:
    # Remove allowlist for open access
    group_policy: "mention_only"
    rate_limit: 10  # messages per minute
```

### 2. Multi-Channel Security

Maintain consistent security across channels with unique tokens and role-specific access:

**Unique token per channel (CRITICAL):**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# ❌ WRONG - Same token reused (security violation)
channels:
  telegram_cfo:
    token: ${TELEGRAM_BOT_TOKEN}  # DON'T REUSE TOKENS
  telegram_ops:
    token: ${TELEGRAM_BOT_TOKEN}  # This will FAIL validation

# ✅ CORRECT - Unique tokens per channel
channels:
  telegram_cfo:
    token: ${TELEGRAM_CFO_BOT_TOKEN}
    allowed_users: ${FINANCE_TEAM_USERS}
  telegram_ops:
    token: ${TELEGRAM_OPS_BOT_TOKEN}
    allowed_users: ${OPS_TEAM_USERS}
```

**Shared allowlists across platforms:**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Shared allowed users
x-allowlist: &shared-users
  - "admin_user_1"
  - "admin_user_2"
  - "trusted_group_1"

channels:
  telegram:
    allowed_users: *shared-users
  discord:
    allowed_users: *shared-users  
  slack:
    allowed_users: *shared-users
```

**Role-specific access control:**

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Different teams access different bots
channels:
  telegram_cfo:
    token: ${TELEGRAM_CFO_BOT_TOKEN}
    allowed_users: ${FINANCE_TEAM_USERS}  # Only finance team
  telegram_ops:
    token: ${TELEGRAM_OPS_BOT_TOKEN}
    allowed_users: ${OPS_TEAM_USERS}     # Only ops team
```

<Warning>
  In a workforce setup, each Telegram/Discord/Slack bot **must** come from a separate BotFather/Developer Portal registration. Sharing tokens between channels is a FAIL in `praisonai doctor` validation.
</Warning>

### 3. Environment-Based Security

Different security levels per environment:

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# development.yaml - loose security
channels:
  telegram:
    # No allowlist for dev testing
    group_policy: "respond_all"

# staging.yaml - moderate security
channels:
  telegram:
    allowed_users: ["@staging_team"]
    group_policy: "mention_only"
    
# production.yaml - strict security  
channels:
  telegram:
    allowed_users: ["@verified_users"]
    group_policy: "command_only"
    approval: true  # All tools need approval
```

## Security Headers & API Protection

When running bot gateways, enable security headers:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.gateway import GatewayServer

server = GatewayServer(
    host="0.0.0.0",
    port=8765,
    security_headers=True,  # Add CORS, CSP, etc.
    rate_limit=True,        # Enable rate limiting
    require_https=True,     # Redirect HTTP to HTTPS
)
```

## Advanced: Custom Security Hooks

Implement custom security logic with hooks:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents.hooks import add_hook, HookResult

@add_hook('before_tool')
def channel_security_check(event_data):
    """Custom security check based on channel type"""
    channel = event_data.context.get('channel_type')
    sender = event_data.context.get('sender_id') 
    tool_name = event_data.tool_name
    
    # Block file operations from Telegram DMs
    if channel == 'telegram' and tool_name in ['write_file', 'delete_file']:
        if not is_verified_user(sender):
            return HookResult.block("File operations not allowed from unverified Telegram users")
    
    # Require approval for shell commands from all channels
    if tool_name == 'execute_command':
        return HookResult.request_approval(f"Shell command from {channel}: {sender}")
    
    return HookResult.allow()

def is_verified_user(user_id: str) -> bool:
    """Check if user is in verified allowlist"""
    verified_users = os.environ.get('VERIFIED_USERS', '').split(',')
    return user_id in verified_users
```

## Troubleshooting

### Pairing Issues

**Problem:** Pairing codes not working
**Solution:**

1. Check `PRAISONAI_GATEWAY_SECRET` is set
2. Verify code hasn't expired (5 min default)
3. Ensure code typed exactly (case sensitive)

**Problem:** Pairing lost after restart
**Solution:**

1. Set `PRAISONAI_GATEWAY_SECRET` env var
2. Codes without persistent secret are temporary

**Problem:** `praisonai pairing approve` reports "Invalid or expired code" even though a code was just generated from the UI
**Solution:**

1. Upgrade to the latest `praisonai` version (fix included in 2026-04-22 release)
2. Older builds had a duplicate internal method that stripped the canonical `code` key when the UI pairing banner was loaded

### Allowlist Issues

**Problem:** Bot not responding to allowed users
**Solution:**

1. Check exact user ID format (username vs numeric ID)
2. Verify allowlist syntax in YAML
3. Run `praisonai doctor` for validation

**Problem:** Bot responding to blocked users
**Solution:**

1. Check allowlist is configured (not just blocklist)
2. Verify `group_policy` setting
3. Check if user has alternate access path

**Problem:** Unauthorized users can still invoke `/help`, `/status`, or `/new`
**Solution:** Upgrade to the PraisonAI release that includes PR #1835. Earlier versions enforced the allowlist only on plain-text messages, not on command handlers.

**Problem:** After upgrading PraisonAI my Telegram bot stopped replying to everyone, including the owner.
**Solution:** PR #1885 fixed a Telegram bug where empty `allowed_users` bypassed `unknown_user_policy`. Either add your user ID to `allowed_users`, or set `unknown_user_policy: "allow"` if you actually want the bot to respond to anyone. Default is `"deny"`.

***

***

## Best Practices

<AccordionGroup>
  <Accordion title="✅ Allowlist Management" icon="shield-check">
    * Use explicit allowlists for all channels
    * Regularly review and update allowed users
    * Implement role-based access where possible
    * Log all access attempts for audit trails
  </Accordion>

  <Accordion title="🔐 Secret Management" icon="key">
    * Store tokens in environment variables
    * Rotate secrets regularly
    * Use encrypted storage for sensitive data
    * Never commit secrets to version control
  </Accordion>

  <Accordion title="⚡ Rate Limiting" icon="gauge">
    * Implement per-user rate limits
    * Set global request quotas
    * Monitor for unusual activity patterns
    * Implement exponential backoff for failed requests
  </Accordion>

  <Accordion title="📊 Monitoring" icon="chart-line">
    * Enable audit logging
    * Monitor security events
    * Set up alerting for suspicious activity
    * Regular security reviews
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Agent Configuration" icon="cog" href="/docs/concepts/agents">
    Core agent setup and configuration
  </Card>

  <Card title="Gateway Setup" icon="server" href="/docs/features/gateway">
    Multi-channel gateway configuration
  </Card>
</CardGroup>

***

By following these security practices, your PraisonAI bots will operate safely in production while maintaining the flexibility to serve legitimate users. Regular security audits help ensure your configuration stays secure over time.
