feat(telemetry): observe V1 compatibility paths safely
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// Package telemetry contains operational projections that do not own domain
|
||||
// state. Compatibility events are intentionally redacted and best-effort.
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CompatibilityEvent is one bounded invocation observation. Aggregators can
|
||||
// derive counts and unique request/session counts from the redacted keys.
|
||||
// It deliberately contains no content, secret, or user identifier.
|
||||
type CompatibilityEvent struct {
|
||||
Event string `json:"event"`
|
||||
Path string `json:"path"`
|
||||
Component string `json:"component"`
|
||||
Access string `json:"access"`
|
||||
Reason string `json:"reason"`
|
||||
Gen2PathAvailable bool `json:"gen2_path_available"`
|
||||
Fallback bool `json:"fallback"`
|
||||
Primary bool `json:"primary"`
|
||||
Outcome string `json:"outcome"`
|
||||
InvocationCount int `json:"invocation_count"`
|
||||
UniqueRequestKey string `json:"unique_request_key"`
|
||||
UniqueSessionKey string `json:"unique_session_key"`
|
||||
SafeCorrelationID string `json:"correlation_id"`
|
||||
LastUsedAt string `json:"last_used_at"`
|
||||
ObservedAtUnixMillis int64 `json:"observed_at_unix_millis"`
|
||||
}
|
||||
|
||||
// NewCompatibilityEvent creates a redacted event. requestRef/sessionRef are
|
||||
// hashed immediately; callers must pass identifiers only, never content.
|
||||
func NewCompatibilityEvent(path, component, access, reason string, gen2PathAvailable, fallback, primary bool, outcome, requestRef, sessionRef string, now time.Time) CompatibilityEvent {
|
||||
if now.IsZero() {
|
||||
now = time.Now().UTC()
|
||||
}
|
||||
requestKey := digest(requestRef)
|
||||
sessionKey := digest(sessionRef)
|
||||
correlationInput := requestRef + "\x00" + sessionRef + "\x00" + path + "\x00" + strconv.FormatInt(now.UnixMilli(), 10)
|
||||
return CompatibilityEvent{
|
||||
Event: "compatibility_observation",
|
||||
Path: path,
|
||||
Component: component,
|
||||
Access: access,
|
||||
Reason: reason,
|
||||
Gen2PathAvailable: gen2PathAvailable,
|
||||
Fallback: fallback,
|
||||
Primary: primary,
|
||||
Outcome: outcome,
|
||||
InvocationCount: 1,
|
||||
UniqueRequestKey: requestKey,
|
||||
UniqueSessionKey: sessionKey,
|
||||
SafeCorrelationID: digest(correlationInput),
|
||||
LastUsedAt: now.UTC().Format(time.RFC3339Nano),
|
||||
ObservedAtUnixMillis: now.UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
// RecordCompatibility emits a structured redacted event. Telemetry is
|
||||
// best-effort and cannot change the compatibility path's result.
|
||||
func RecordCompatibility(path, component, access, reason string, gen2PathAvailable, fallback, primary bool, outcome, requestRef, sessionRef string, now time.Time) {
|
||||
event := NewCompatibilityEvent(path, component, access, reason, gen2PathAvailable, fallback, primary, outcome, requestRef, sessionRef, now)
|
||||
payload, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Printf("%s", payload)
|
||||
}
|
||||
|
||||
func digest(value string) string {
|
||||
sum := sha256.Sum256([]byte(value))
|
||||
return hex.EncodeToString(sum[:8])
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCompatibilityEventRedactsRequestAndSessionReferences(t *testing.T) {
|
||||
event := NewCompatibilityEvent("personal-memory-fallback", "core-runtime", "read", "empty-gen2-projection", true, true, false, "SUCCESS", "request-secret-looking", "session-secret-looking", time.UnixMilli(42))
|
||||
if event.InvocationCount != 1 || event.ObservedAtUnixMillis != 42 || event.LastUsedAt == "" {
|
||||
t.Fatalf("event metadata incomplete: %+v", event)
|
||||
}
|
||||
if strings.Contains(event.UniqueRequestKey, "request-secret-looking") || strings.Contains(event.UniqueSessionKey, "session-secret-looking") || strings.Contains(event.SafeCorrelationID, "secret-looking") {
|
||||
t.Fatalf("event leaked reference: %+v", event)
|
||||
}
|
||||
if event.Path != "personal-memory-fallback" || !event.Gen2PathAvailable || !event.Fallback || event.Primary {
|
||||
t.Fatalf("event classification incorrect: %+v", event)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user