docs: update docs for ACL tiers and webhook
All checks were successful
CI / test (3.11) (push) Successful in 1m37s
CI / test (3.12) (push) Successful in 1m35s
CI / test (3.13) (push) Successful in 1m20s

- USAGE.md: permission tiers section, webhook config/API/example
- CHEATSHEET.md: ACL tiers and webhook quick-ref sections
- ROADMAP.md: mark webhook and ACL items done
- TODO.md: mark webhook and ACL items done
- TASKS.md: new sprint for ACL + webhook work
This commit is contained in:
user
2026-02-21 17:59:22 +01:00
parent c483beb555
commit e9528bd879
5 changed files with 155 additions and 26 deletions

View File

@@ -53,10 +53,18 @@ rate_burst = 5 # Burst capacity (default: 5)
paste_threshold = 4 # Max lines before overflow to FlaskPaste (default: 4)
admins = [] # Hostmask patterns (fnmatch), IRCOPs auto-detected
timezone = "UTC" # Timezone for calendar reminders (IANA tz name)
operators = [] # Hostmask patterns for "oper" tier (fnmatch)
trusted = [] # Hostmask patterns for "trusted" tier (fnmatch)
[logging]
level = "info" # Logging level: debug, info, warning, error
format = "text" # Log format: "text" (default) or "json"
[webhook]
enabled = false # Enable HTTP webhook listener
host = "127.0.0.1" # Bind address
port = 8080 # Bind port
secret = "" # HMAC-SHA256 shared secret (empty = no auth)
```
## Built-in Commands
@@ -142,6 +150,7 @@ format = "text" # Log format: "text" (default) or "json"
| `!paste <text>` | Create a paste via FlaskPaste |
| `!pastemoni <add\|del\|list\|check>` | Paste site keyword monitoring |
| `!cron <add\|del\|list>` | Scheduled command execution (admin) |
| `!webhook` | Show webhook listener status (admin) |
### Command Shorthand
@@ -221,24 +230,31 @@ Each line contains:
Default format is `"text"` (human-readable, same as before).
## Admin System
## Permission Tiers (ACL)
Commands marked as `admin` require elevated permissions. Admin access is
granted via:
The bot uses a 4-tier permission model. Each command has a required tier;
users must meet or exceed it.
1. **IRC operator status** -- detected automatically via `WHO`
2. **Hostmask patterns** -- configured in `[bot] admins`, fnmatch-style
```
user < trusted < oper < admin
```
| Tier | Granted by |
|------|------------|
| `user` | Everyone (default) |
| `trusted` | `[bot] trusted` hostmask patterns |
| `oper` | `[bot] operators` hostmask patterns |
| `admin` | `[bot] admins` hostmask patterns or IRC operator status |
```toml
[bot]
admins = [
"*!~user@trusted.host",
"ops!*@*.ops.net",
]
admins = ["*!~root@*.ops.net"]
operators = ["*!~staff@trusted.host"]
trusted = ["*!~user@known.host"]
```
Empty by default -- only IRC operators get admin access unless patterns
are configured.
All lists are empty by default -- only IRC operators get admin access
unless patterns are configured. Patterns use fnmatch-style matching.
### Oper Detection
@@ -256,18 +272,24 @@ set automatically.
| Command | Description |
|---------|-------------|
| `!whoami` | Show your hostmask and admin status |
| `!admins` | Show configured patterns and detected opers (admin) |
| `!whoami` | Show your hostmask and permission tier |
| `!admins` | Show configured tiers and detected opers (admin) |
Admin-restricted commands: `!load`, `!reload`, `!unload`, `!admins`, `!state`,
`!kick`, `!ban`, `!unban`, `!topic`, `!mode`.
`!kick`, `!ban`, `!unban`, `!topic`, `!mode`, `!webhook`.
### Writing Admin Commands
### Writing Tiered Commands
```python
# admin=True still works (maps to tier="admin")
@command("dangerous", help="Admin-only action", admin=True)
async def cmd_dangerous(bot, message):
...
# Explicit tier for finer control
@command("moderate", help="Trusted-only action", tier="trusted")
async def cmd_moderate(bot, message):
...
```
## IRCv3 Capability Negotiation
@@ -405,6 +427,68 @@ restarting the bot.
The `core` plugin cannot be unloaded (prevents losing `!load`/`!reload`),
but it can be reloaded.
## Webhook Listener
Receive HTTP POST requests from external services (CI, monitoring, GitHub,
etc.) and relay messages to IRC channels.
### Configuration
```toml
[webhook]
enabled = true
host = "127.0.0.1"
port = 8080
secret = "your-shared-secret"
```
### HTTP API
Single endpoint: `POST /`
**Request body** (JSON):
```json
{"channel": "#ops", "text": "Deploy v2.3.1 complete"}
```
Optional `"action": true` sends as a `/me` action.
**Authentication**: HMAC-SHA256 via `X-Signature: sha256=<hex>` header.
If `secret` is empty, no authentication is required.
**Response codes**:
| Status | When |
|--------|------|
| 200 OK | Message sent |
| 400 Bad Request | Invalid JSON, missing/invalid channel, empty text |
| 401 Unauthorized | Bad or missing HMAC signature |
| 405 Method Not Allowed | Non-POST request |
| 413 Payload Too Large | Body > 64 KB |
### Usage Example
```bash
SECRET="your-shared-secret"
BODY='{"channel":"#ops","text":"Deploy v2.3.1 complete"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST http://127.0.0.1:8080/ \
-H "Content-Type: application/json" \
-H "X-Signature: sha256=$SIG" \
-d "$BODY"
```
### Status Command
```
!webhook Show listener address, request count, uptime (admin)
```
The server starts on IRC connect (event 001) and runs for the lifetime of
the bot. If the server is already running (e.g. after reconnect), it is
not restarted.
## Writing Plugins
Create a `.py` file in the `plugins/` directory: