# Hermes WeCom DM Authorization Internals

Traced from source code on 2026-06-15.

## Two-layer authorization

### Layer 1: WeCom Adapter (`gateway/platforms/wecom.py`)

`dm_policy` config in `config.yaml` → `platforms.wecom.extra.dm_policy`:
- `open` (default) — always returns True
- `allowlist` — checks `_allow_from` list
- `disabled` — always returns False

Code at line 838-843:
```python
def _is_dm_allowed(self, sender_id: str) -> bool:
    if self._dm_policy == "disabled":
        return False
    if self._dm_policy == "allowlist":
        return _entry_matches(self._allow_from, sender_id)
    return True
```

### Layer 2: Gateway Authorization (`gateway/run.py`)

`_is_user_authorized()` at line 5475. **Checked for EVERY inbound message, regardless of platform adapter's own `_is_dm_allowed()`.**

Check order (line 5562-5600):

```
1. Per-platform ALLOW_ALL flag (e.g. WECOM_ALLOW_ALL_USERS=true) → RETURN TRUE   [line 5563-5565]
2. Discord role-based access (Discord only)                                     [line 5578-5582]
3. Pairing store — if user is paired → RETURN TRUE                              [line 5584-5587]
4. Platform-specific allowlists (WECOM_ALLOWED_USERS, etc.)                     [line 5589-5596]
5. If NO allowlists configured anywhere → check GATEWAY_ALLOW_ALL_USERS         [line 5598-5600]
6. If allowlists exist → check user membership in any list                      [line 5642+]
7. Default: DENY
```

**Critical**: Step 1 is FIRST. If `WECOM_ALLOW_ALL_USERS=true`, the function returns True immediately. Steps 2-7 are never reached. No pairing, no allowlist — nothing else matters.

### Platform env var mappings

```python
platform_allow_all_map = {
    Platform.WECOM: "WECOM_ALLOW_ALL_USERS",
    ...
}
platform_env_map = {
    Platform.WECOM: "WECOM_ALLOWED_USERS",
    ...
}
```

## Diagnostic pattern

When user reports "仅创建者可使用":

1. **First**: Check `WECOM_ALLOW_ALL_USERS=true` in `.env` → if yes, Hermes is NOT the blocker
2. **Second**: Check WeCom platform visibility (协作 → 智能机器人 → 可见范围) → this is the real blocker
3. **Third**: Only if both above are correct, check gateway logs for auth failures

## Related files

- `gateway/run.py` lines 5475-5650: `_is_user_authorized()`
- `gateway/platforms/wecom.py` lines 838-843: `_is_dm_allowed()`
- `gateway/pairing.py`: PairingStore implementation
