Files
beaver-gateway/src/beaver_gateway/security/audit.py
T

67 lines
1.7 KiB
Python

"""Single entry point for writing :class:`AuditLog` rows.
``log()`` is fire-and-forget: it awaits the DB write so callers get
ordering, but never raises — failures are logged and swallowed.
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any
from beaver_gateway.storage import append_audit
if TYPE_CHECKING:
from beaver_gateway.frontends.base import GatewayRuntime
_log = logging.getLogger("beaver_gateway.audit")
KNOWN_KINDS: frozenset[str] = frozenset(
{
"messages",
"mcp_call",
"tool_call",
"login_ok",
"login_failed",
"logout",
"token_create",
"token_revoke",
}
)
"""Kinds the gateway currently emits; ``AuditLog.kind`` stays free-form."""
async def log(
runtime: GatewayRuntime,
*,
actor: str,
kind: str,
agent_name: str | None = None,
**detail: Any,
) -> None:
"""Best-effort audit insert. Never raises; DB failures are logged.
``actor`` is free-form (``"token:<name>"``, ``"admin:<user>"``,
``"anon"``); ``kind`` is a short tag (see :data:`KNOWN_KINDS`); and
``**detail`` is JSON-serialised into ``AuditLog.detail_json`` — keep
it small, not full request bodies.
"""
try:
async with runtime.db.session() as session:
await append_audit(
session,
actor=actor,
kind=kind,
agent_name=agent_name,
detail=detail or None,
)
except Exception: # noqa: BLE001
_log.exception(
"audit write failed: actor=%s kind=%s agent=%s", actor, kind, agent_name
)
__all__ = ["KNOWN_KINDS", "log"]