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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant