approve-each requests use one provider-neutral owner contract for Claude and
Codex. This contract does not widen the selected execution profile and does not
change auto-approve, auto-review, or disabled behavior.
Before Telegram renders an approval, the bridge builds a bounded snapshot with:
- provider, normalized action, and target shape;
- a target-only command/path/permission preview;
- an optional working-directory hint and fixed risk hints;
- a request fingerprint and a fingerprint of the exact final Telegram text.
The renderer removes ANSI and bidirectional/control characters, omits raw
environment/header/file-body fields, applies the shared credential redactor,
and only then truncates UTF-8 output. Telegram receives the final text with
parse_mode=None. Raw provider descriptions and arguments never enter the
audit ledger.
The in-memory request binding is keyed HMAC-SHA-256, which prevents the ledger from becoming a dictionary oracle for low-entropy commands. The already-safe display text uses ordinary SHA-256 so an owner can later verify an exact copied prompt. A changed request fingerprint invalidates the old one-shot token and requires a new prompt even when the provider reuses its request id.
Pending approve-each requests append to:
BOT_DATA_DIR/approval-audit/approval-audit.jsonl
The directory and files must be exactly 0700 and 0600. Symlinks, hardlinks,
wrong owners, unsafe modes, and unsafe ancestors are rejected. Writes are
bounded and locked; malformed old rows are retained but ignored during dedup
and aggregation. Storage failures are body-free and fail-open for
observability, while the existing generation, expiry, fingerprint, and
one-shot-token checks remain fail-closed for execution.
Each approval has at most one asked and one terminal answered record. A
terminal record contains only opaque session/turn/request/actor references,
provider/action/shape, request and display fingerprints, fixed redaction and
display-field labels, decision/reason, timestamps, and latency. Callback/text
races and late replays cannot append a second terminal decision.
Inspect aggregate action, decision, reason, and latency counts without printing request references or fingerprints:
python -m telegram_bot.core.approval_audit \
--directory "$BOT_DATA_DIR/approval-audit" --jsonThe report is evidence for later policy review only. It never creates an allowlist or changes approval policy automatically.