Skip to content

feat(pii): export PII/audit events as a Prometheus counter#10641

Open
walcz-de wants to merge 1 commit into
mudler:masterfrom
walcz-de:feat/pii-event-prometheus-counter
Open

feat(pii): export PII/audit events as a Prometheus counter#10641
walcz-de wants to merge 1 commit into
mudler:masterfrom
walcz-de:feat/pii-event-prometheus-counter

Conversation

@walcz-de

@walcz-de walcz-de commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Adds a single Prometheus counter for the PII middleware / MITM audit pipeline:

localai_pii_events_total{kind, origin, action, direction}

Why

The PII EventStore ring buffer is capacity-bound and meant for recent-audit browsing via /api/pii/events. For operations you also want a monotonic, scrape-friendly signal on /metrics: how many detections/masks/blocks per hour, per origin — and most importantly whether the filter stopped firing after a deploy (silent-failure class; alertable with a simple rate()).

How

EventStore.Record is the single choke point every producer already goes through (request middleware, response scrubbing, MITM proxy connects/intercepts), so one lazily-initialised counter there covers all paths without touching any producer. Same lazy otel.Meter pattern as core/services/routing/billing, so the counter lands on the Prometheus-backed global MeterProvider installed by the monitoring service.

No behaviour change. Label cardinality is bounded — enum-like fields only (no pattern IDs, no user IDs).

Verification

go build ./core/services/routing/pii/ + go test ./core/services/routing/pii/... green. Deployed on a LocalAI instance with the PII middleware + MITM proxy active; counter series appear on /metrics after the first detection/connect event.

The PII EventStore ring buffer is capacity-bound and meant for
recent-audit browsing via /api/pii/events; operators also want a
monotonic, scrape-friendly signal on /metrics — how many
detections/masks/blocks per hour, per origin, and whether the filter
stopped firing after a deploy (silent-failure class).

EventStore.Record is the single choke point every producer already goes
through (request middleware, response scrubbing, MITM proxy
connects/intercepts), so one lazily-initialised counter there covers all
paths without touching any producer:

  localai_pii_events_total{kind, origin, action, direction}

Same lazy otel.Meter pattern as core/services/routing/billing, so the
counter lands on the Prometheus-backed global MeterProvider installed by
the monitoring service. No behaviour change; label cardinality is
bounded (enum-like fields only, no pattern IDs or user IDs).

Assisted-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: stefanwalcz <stefan.walcz@walcz.de>
@walcz-de

walcz-de commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Live verification as promised: deployed this patch on our LocalAI instance (v4.5.6 + this commit, PII middleware active on cloud-proxy models). First request containing PII (name + email, masked before egress) produced:

localai_pii_events_total{action="mask",direction="in",kind="",origin="middleware"} 3

Counter lands on /metrics via the existing Prometheus-backed MeterProvider, series appears on first event, labels populate as designed (kind is empty for plain middleware detections — it's set for MITM proxy_connect events).

@mudler mudler requested a review from richiejp July 2, 2026 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant