ADR-0004 — Superset audits each request after its response, in the platform's record, emitted by the plugin
Date: 2026-10-05 Status: accepted
Context
The platform records who read what, and who was refused, in one record shape on the logger
celine.audit (celine.sdk.audit), so one log query answers the question for every service.
The SDK's helpers read a Starlette request and hook FastAPI; Superset is Flask, and its plugin
does not import celine-sdk (ADR-0003).
Superset already has an event logger (EVENT_LOGGER, written to the logs table). It runs
only around the handlers Superset decorated, never for a request before_request refuses; it
is skipped when a handler raises; it does not know the response status; and its payload holds
the request body, SQL text included, and the Superset user id, whose username is the token's
preferred_username or email.
Decision
- The plugin emits the record itself (
celine.superset.plugin.audit), field for field the SDK's: same logger, same fields in the same order, same backstops (email-shaped values pseudonymised, control characters dropped, 256-character cap). before_requestkeeps the verified claims for the request and registers oneafter_this_requestcallback, which writes at most one record from the response status: an allowed read from a fixed list of endpoints, any 401/403 with a verified caller, a listed read that failed. A refusalbefore_requestanswers itself is recorded there, with a reason code.- The dataset tag check only notes its reason: Superset also runs it to decide what to show and
turns the refusal into
False, so only the response says whether the request was refused. - The caller is
subandazp. SQL Lab is recorded as the database id and a SHA-256 of the statement, never the text. - The event logger and its
logstable are left as they are.
Consequences
- The record shape exists twice. A change to
celine.sdk.auditmust be made in the plugin too; depending on the SDK from the plugin would remove the copy, but the SDK's request helpers would still need a Flask variant. - An endpoint missing from the list is recorded only when it refuses. New Superset versions
add and rename endpoints; the list is checked against
superset routeson an upgrade. - A request refused before the security manager runs (Flask-WTF's CSRF check, answered
400) is not recorded: there is no verified caller yet. - The hash of a statement names it without carrying it; whoever holds Superset's
querytable can still match it, which is the point.