Cookiebot
Features

Per-group audit trail

net-new: v1 kept no trail at all. group_audit_events is distributed on group_id (migration 0010) with a UUIDv7 key, so the page is a keyset read on one shard. Written by both surfaces - the Mini App's config endpoints and the Telegram config menu, /newrules and /newwelcome - and a failed audit write never fails the action it describes

DoneM4apiplatform
v1 scenarios
—
Ported
—
Green
—
Failing
—

net-new: v1 kept no trail at all. group_audit_events is distributed on group_id (migration 0010) with a UUIDv7 key, so the page is a keyset read on one shard. Written by both surfaces - the Mini App's config endpoints and the Telegram config menu, /newrules and /newwelcome - and a failed audit write never fails the action it describes

What it does

Every admin action on a group's settings leaves a row: who did it, from which surface, what the value was before, and what it is now. GET /groups/{group_id}/audit reads it back, newest first, keyset-paginated and filterable by action or actor.

Both surfaces write to it — the Mini App's config endpoints and, on the Telegram side, the /config menu, /newrules and /newwelcome — so the trail answers "who turned the captcha off" whichever way it was turned off.

Behaviour that must not change

Rows quote, they do not paraphrase. before and after hold the fields that actually changed with their values; the human-readable summary sits beside them, never instead of them.

A save that changed nothing writes nothing. An admin who opens a form and presses save should not produce a row that reads like they rewrote every setting.

A failed audit write never fails the action it describes. By the time the row is written the setting has already changed; a 500 at that point would tell the client their change did not land when it did. The loss is loud instead — log.error plus cb_audit_write_failures_total.

An anonymous admin has no actor. Telegram gives GroupAnonymousBot rather than an account, so the row records what changed with actor_user_id null, which is the truth rather than a guess.

How to verify it

packages/cb-core/tests/test_audit.py covers the diff and the swallowed-failure path; qa/integration/test_audit_log.py covers the jsonb round trip, keyset paging over seven rows in three pages, per-group isolation and the Task Count: 1 plan that keeps the page single-shard.

On this page