field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraDiagnosticPolicy.swift (2879B)


      1 import Foundation
      2 import RadrootsKit
      3 
      4 /// An app-owned closed schema, applied before all sinks and again at the
      5 /// standalone buffer boundary. Pattern redaction alone cannot admit content.
      6 enum TeraDiagnosticPolicy {
      7   static let eventNames: Set<String> = [
      8     "ios.lifecycle.start_requested", "ios.lifecycle.resume_coalesced",
      9     "ios.lifecycle.active", "ios.lifecycle.background",
     10     "ios.lifecycle.protected_data_available", "ios.lifecycle.protected_data_unavailable",
     11     "ios.lifecycle.operation_failed", "ios.lifecycle.operation_completed",
     12     "ios.lifecycle.shutdown_requested", "ios.diagnostics.redacted",
     13   ]
     14   static let phases: Set<String> = [
     15     "starting", "identity_required", "identity_locked", "protected_data_unavailable",
     16     "recovery_required", "corrupt_identity", "configuration_reconfiguration_required",
     17     "running", "stopped", "failed",
     18   ]
     19   private static let operations: Set<String> = [
     20     "start", "stop", "identity_create", "identity_import", "identity_lock", "identity_unlock",
     21     "identity_recover", "configuration_reconfigure", "settings_reconfigure", "identity_key_removal",
     22   ]
     23 
     24   static func sanitized(_ event: RadrootsTelemetryEvent) -> RadrootsTelemetryEvent? {
     25     let known = eventNames.contains(event.name)
     26     let name = known ? event.name : "ios.diagnostics.redacted"
     27     let fields = known ? event.fields.prefix(8).compactMap(sanitizedField) : []
     28     return try? RadrootsTelemetryEvent(
     29       name: name, category: "ios_lifecycle", level: event.level,
     30       fields: fields, occurredAt: event.occurredAt
     31     )
     32   }
     33 
     34   private static func sanitizedField(_ field: RadrootsTelemetryField) -> RadrootsTelemetryField? {
     35     if case let .string(value) = field.value {
     36       switch field.key {
     37       case "operation" where operations.contains(value): return field
     38       case "phase" where phases.contains(value): return field
     39       case "code" where value == "bootstrap_failed": return field
     40       default: return nil
     41       }
     42     }
     43     if case let .integer(value) = field.value,
     44       ["count", "attempts", "schema", "latency_ms"].contains(field.key),
     45       (0 ... 1_000_000).contains(value)
     46     {
     47       return field
     48     }
     49     return nil
     50   }
     51 
     52   static func code(_ value: String, allowed: Set<String>) -> String {
     53     allowed.contains(value) ? value : "unavailable"
     54   }
     55 
     56   static func version(_ value: String) -> String {
     57     code(value, allowed: ["0.1.0-alpha"])
     58   }
     59 
     60   static func build(_ value: String) -> String {
     61     guard !value.isEmpty, value.utf8.count <= 10,
     62       value.utf8.allSatisfy({ (48 ... 57).contains($0) })
     63     else { return "unavailable" }
     64     return value
     65   }
     66 }
     67 
     68 struct TeraSanitizedTelemetry: RadrootsTelemetry {
     69   let sink: any RadrootsTelemetry
     70 
     71   func record(_ event: RadrootsTelemetryEvent) async {
     72     guard let event = TeraDiagnosticPolicy.sanitized(event) else { return }
     73     await sink.record(event)
     74   }
     75 }