Webhook audit logging records semantic channel events in memory so operators can inspect recent activity from the Web UI. It is designed for real-time visibility, not long-term forensic storage.
flowchart TD
CH["<b>Channels</b><br/>Discord / Teams / Facebook / Telegram"] --> H["<b>Channel Handler</b>"]
H --> REC["<b>runtimeinfo.RecordWebhookAudit</b>"]
REC --> BUF["<b>runtimeinfo.WebhookAuditLogs</b><br/>In-memory ring buffer (newest 500)"]
BUF --> API["<b>GET /api/audit</b>"]
type WebhookAuditLog struct {
Timestamp time.Time `json:"timestamp"`
Channel string `json:"channel"`
EventType string `json:"event_type"`
AuthorID string `json:"author_id"`
AuthorName string `json:"author_name"`
PeerID string `json:"peer_id"`
PeerKind string `json:"peer_kind"`
Preview string `json:"preview"`
Status string `json:"status"`
}
RecordWebhookAudit sets Timestamp when missing, cuts Preview to its first 120 bytes followed by …, and keeps only the newest 500 records. The buffer is in memory: a restart empties it.
Status is processed, error (handling or answering the message failed), duplicate (a repeated Teams delivery that was ignored), ignored, or, for a Teams SSO attempt, unimplemented.
| Event type | Meaning |
|---|---|
chat |
Regular text message |
command |
Slash command or command-style message |
typing |
Typing indicator, usually ignored |
reaction |
Message reaction, usually ignored |
comment |
Conversation update or comment-style event |
login_attempt |
Teams SSO token exchange (recorded with channel msteams_sso) |
unknown |
Channel event that did not match a known class |
GET /api/audit?channel=facebook&event=chat&user=USER123&limit=50
All query parameters are optional.
| Parameter | Description |
|---|---|
channel |
Filter by channel: discord, msteams, msteams_sso, facebook or telegram |
event |
Filter by event type |
user |
Filter by author ID |
limit |
Maximum rows, 1 to 500 (default 100). A missing, invalid, zero, negative or larger value gives 100 |
Response:
{
"logs": [
{
"timestamp": "2026-06-20T08:45:00Z",
"channel": "discord",
"event_type": "chat",
"author_id": "123456789",
"author_name": "User",
"peer_id": "987654321",
"peer_kind": "direct",
"preview": "Hello, can you help me?",
"status": "processed"
}
],
"total": 1
}
| File | Event source |
|---|---|
internal/channel/discord/discord_handler.go |
Discord messages |
internal/channel/discord/discord_slash.go |
Discord slash commands |
internal/channel/msteams/msteams.go |
Teams activities |
internal/channel/msteams/msteams_sso.go |
Teams SSO token exchange attempts (channel msteams_sso, event login_attempt) |
internal/channel/facebook/facebook.go |
Facebook messages |
internal/channel/telegram/telegram.go |
Telegram messages |
A separate trail, internal/audit, records destructive or administrative
actions as one line each in the gateway log:
[audit] action=sop.delete actor=… role=… source=… ip=… resource=sop/<id> success=true
It is written for sop.delete (from the sop tool and the SOP API) and for
sop.channel.pin / sop.channel.unpin (the SOP channels API). Find them with
grep '\[audit\]' gateway.log. The last 500 are also kept in memory and
served by GET /api/audit?kind=admin, newest first, filtered by ?action=,
?actor= and ?resource= (a resource type such as sop) and limited by
?limit= (default 100); they do not survive a restart.
go test ./internal/runtimeinfo -count=1
The runtimeinfo tests cover recording, timestamp defaults, preview truncation, ring buffer overflow, filters, and newest-first ordering.