Task · TSO-0134

PAM config changes never reach the audit-changes metric

Description

Live-verified 2026-09-04 against a real PAM deployment on the lab tailnet. Nine BORDER0_API audit events (PAM_SERVICE_ACCOUNT, PAM_CONNECTOR and PAM_SERVICE creates, one update, one delete) were polled and counted correctly on tailscale.config.audit.events (origin=BORDER0_API, CREATE=7 UPDATE=1 DELETE=1 confirmed in Grafana Cloud), but produced ZERO increments on tailscale.config.audit.changes.

Cause: classifyChange keys on a curated target.property or on the device-churn / api-key type+action rules. A PAM event carries target.type PAM_SERVICE / PAM_CONNECTOR / PAM_SERVICE_ACCOUNT with no curated property, so it falls through. Consequence: the PAM_CONNECTOR and PAM_SERVICE_ACCOUNT entries TSO-0087 added to knownActorTypes in internal/audit/classify.go are unreachable in practice, because normalizeActorType is only called from the changes path.

Second, related gap: enabling PAM emits target.type=TAILNET, target.property=BORDER0_PROVISIONING, action=ENABLE. BORDER0_PROVISIONING is absent from the vendored spec entirely, so it is in neither propertyCategories nor propertyExclusions and the taxonomy_test schema-drift guard cannot fail on it. A third party being granted tenant-wide provisioning is exactly the kind of change the changes metric is for.

Decide whether PAM deserves its own curated change category (pam_service / pam_connector / pam_credential) or whether BORDER0_PROVISIONING alone is enough, then add the guard so the next unspecced property is caught.

Acceptance Criteria

Definition of Done

Implementation Notes

2026-09-04 Wave 12 implementation: classified PAM_SERVICE, PAM_CONNECTOR and PAM_SERVICE_ACCOUNT as bounded pam_service, pam_connector and pam_service_account categories because live object lifecycle events carry no target.property and target type is the stable discriminator. Classified BORDER0_PROVISIONING as pam_provisioning. Added a separate live-only-property taxonomy ledger so an observed property absent from the vendored schema must still be categorized or explicitly excluded. Negative-tested the guard by removing the category and observing the intended failure, then restored it. Focused audit tests pass.

2026-09-04 Wave 12 completion evidence:

Final Summary

Fixed PAM audit change classification with bounded categories for PAM object lifecycle and provisioning events, made PAM actor normalization reachable, and added the durable live-only property drift guard. Verified with focused negative testing, the full local gate, exact-head CI, and completed CodeRabbit review.

View the source file on GitHub