raxe 0.4.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (668) hide show
  1. raxe/__init__.py +101 -0
  2. raxe/application/__init__.py +48 -0
  3. raxe/application/ab_testing.py +170 -0
  4. raxe/application/analytics/__init__.py +30 -0
  5. raxe/application/analytics/achievement_service.py +444 -0
  6. raxe/application/analytics/repositories.py +172 -0
  7. raxe/application/analytics/retention_service.py +267 -0
  8. raxe/application/analytics/statistics_service.py +419 -0
  9. raxe/application/analytics/streak_service.py +283 -0
  10. raxe/application/apply_policy.py +291 -0
  11. raxe/application/eager_l2.py +503 -0
  12. raxe/application/preloader.py +353 -0
  13. raxe/application/scan_merger.py +321 -0
  14. raxe/application/scan_pipeline.py +1059 -0
  15. raxe/application/scan_pipeline_async.py +403 -0
  16. raxe/application/session_tracker.py +458 -0
  17. raxe/application/telemetry_manager.py +357 -0
  18. raxe/application/telemetry_orchestrator.py +1210 -0
  19. raxe/async_sdk/__init__.py +34 -0
  20. raxe/async_sdk/cache.py +286 -0
  21. raxe/async_sdk/client.py +556 -0
  22. raxe/async_sdk/wrappers/__init__.py +23 -0
  23. raxe/async_sdk/wrappers/openai.py +238 -0
  24. raxe/cli/__init__.py +21 -0
  25. raxe/cli/auth.py +1047 -0
  26. raxe/cli/branding.py +235 -0
  27. raxe/cli/config.py +334 -0
  28. raxe/cli/custom_rules.py +458 -0
  29. raxe/cli/doctor.py +686 -0
  30. raxe/cli/error_handler.py +665 -0
  31. raxe/cli/event.py +648 -0
  32. raxe/cli/exit_codes.py +57 -0
  33. raxe/cli/expiry_warning.py +302 -0
  34. raxe/cli/export.py +183 -0
  35. raxe/cli/history.py +247 -0
  36. raxe/cli/l2_formatter.py +872 -0
  37. raxe/cli/main.py +1137 -0
  38. raxe/cli/models.py +590 -0
  39. raxe/cli/output.py +403 -0
  40. raxe/cli/privacy.py +84 -0
  41. raxe/cli/profiler.py +262 -0
  42. raxe/cli/progress.py +379 -0
  43. raxe/cli/progress_context.py +101 -0
  44. raxe/cli/repl.py +394 -0
  45. raxe/cli/rules.py +542 -0
  46. raxe/cli/setup_wizard.py +721 -0
  47. raxe/cli/stats.py +292 -0
  48. raxe/cli/suppress.py +501 -0
  49. raxe/cli/telemetry.py +1384 -0
  50. raxe/cli/test.py +130 -0
  51. raxe/cli/tune.py +315 -0
  52. raxe/cli/validate.py +218 -0
  53. raxe/domain/__init__.py +30 -0
  54. raxe/domain/analytics/__init__.py +97 -0
  55. raxe/domain/analytics/achievements.py +306 -0
  56. raxe/domain/analytics/models.py +120 -0
  57. raxe/domain/analytics/retention.py +168 -0
  58. raxe/domain/analytics/statistics.py +207 -0
  59. raxe/domain/analytics/streaks.py +173 -0
  60. raxe/domain/engine/__init__.py +15 -0
  61. raxe/domain/engine/executor.py +396 -0
  62. raxe/domain/engine/matcher.py +212 -0
  63. raxe/domain/inline_suppression.py +176 -0
  64. raxe/domain/ml/__init__.py +133 -0
  65. raxe/domain/ml/embedding_cache.py +309 -0
  66. raxe/domain/ml/gemma_detector.py +921 -0
  67. raxe/domain/ml/gemma_models.py +346 -0
  68. raxe/domain/ml/l2_config.py +428 -0
  69. raxe/domain/ml/l2_output_schema.py +443 -0
  70. raxe/domain/ml/manifest_loader.py +309 -0
  71. raxe/domain/ml/manifest_schema.py +345 -0
  72. raxe/domain/ml/model_metadata.py +263 -0
  73. raxe/domain/ml/model_registry.py +786 -0
  74. raxe/domain/ml/protocol.py +282 -0
  75. raxe/domain/ml/scoring_models.py +419 -0
  76. raxe/domain/ml/stub_detector.py +397 -0
  77. raxe/domain/ml/threat_scorer.py +757 -0
  78. raxe/domain/ml/tokenizer_registry.py +372 -0
  79. raxe/domain/ml/voting/__init__.py +89 -0
  80. raxe/domain/ml/voting/config.py +595 -0
  81. raxe/domain/ml/voting/engine.py +465 -0
  82. raxe/domain/ml/voting/head_voters.py +378 -0
  83. raxe/domain/ml/voting/models.py +222 -0
  84. raxe/domain/models.py +82 -0
  85. raxe/domain/packs/__init__.py +17 -0
  86. raxe/domain/packs/models.py +304 -0
  87. raxe/domain/policies/__init__.py +20 -0
  88. raxe/domain/policies/evaluator.py +212 -0
  89. raxe/domain/policies/models.py +223 -0
  90. raxe/domain/rules/__init__.py +32 -0
  91. raxe/domain/rules/custom.py +286 -0
  92. raxe/domain/rules/models.py +273 -0
  93. raxe/domain/rules/schema.py +166 -0
  94. raxe/domain/rules/validator.py +556 -0
  95. raxe/domain/suppression.py +801 -0
  96. raxe/domain/suppression_factory.py +174 -0
  97. raxe/domain/telemetry/__init__.py +116 -0
  98. raxe/domain/telemetry/backpressure.py +424 -0
  99. raxe/domain/telemetry/event_creator.py +362 -0
  100. raxe/domain/telemetry/events.py +1282 -0
  101. raxe/domain/telemetry/priority.py +263 -0
  102. raxe/domain/telemetry/scan_telemetry_builder.py +670 -0
  103. raxe/infrastructure/__init__.py +25 -0
  104. raxe/infrastructure/analytics/__init__.py +18 -0
  105. raxe/infrastructure/analytics/aggregator.py +484 -0
  106. raxe/infrastructure/analytics/aggregator_optimized.py +184 -0
  107. raxe/infrastructure/analytics/engine.py +748 -0
  108. raxe/infrastructure/analytics/repository.py +409 -0
  109. raxe/infrastructure/analytics/streaks.py +467 -0
  110. raxe/infrastructure/analytics/views.py +178 -0
  111. raxe/infrastructure/cloud/__init__.py +9 -0
  112. raxe/infrastructure/config/__init__.py +56 -0
  113. raxe/infrastructure/config/endpoints.py +641 -0
  114. raxe/infrastructure/config/scan_config.py +352 -0
  115. raxe/infrastructure/config/yaml_config.py +459 -0
  116. raxe/infrastructure/database/__init__.py +10 -0
  117. raxe/infrastructure/database/connection.py +200 -0
  118. raxe/infrastructure/database/models.py +325 -0
  119. raxe/infrastructure/database/scan_history.py +764 -0
  120. raxe/infrastructure/ml/__init__.py +0 -0
  121. raxe/infrastructure/ml/download_progress.py +438 -0
  122. raxe/infrastructure/ml/model_downloader.py +457 -0
  123. raxe/infrastructure/models/__init__.py +16 -0
  124. raxe/infrastructure/models/discovery.py +461 -0
  125. raxe/infrastructure/packs/__init__.py +13 -0
  126. raxe/infrastructure/packs/loader.py +407 -0
  127. raxe/infrastructure/packs/registry.py +381 -0
  128. raxe/infrastructure/policies/__init__.py +16 -0
  129. raxe/infrastructure/policies/api_client.py +256 -0
  130. raxe/infrastructure/policies/validator.py +227 -0
  131. raxe/infrastructure/policies/yaml_loader.py +250 -0
  132. raxe/infrastructure/rules/__init__.py +18 -0
  133. raxe/infrastructure/rules/custom_loader.py +224 -0
  134. raxe/infrastructure/rules/versioning.py +222 -0
  135. raxe/infrastructure/rules/yaml_loader.py +286 -0
  136. raxe/infrastructure/security/__init__.py +31 -0
  137. raxe/infrastructure/security/auth.py +145 -0
  138. raxe/infrastructure/security/policy_validator.py +124 -0
  139. raxe/infrastructure/security/signatures.py +171 -0
  140. raxe/infrastructure/suppression/__init__.py +36 -0
  141. raxe/infrastructure/suppression/composite_repository.py +154 -0
  142. raxe/infrastructure/suppression/sqlite_repository.py +231 -0
  143. raxe/infrastructure/suppression/yaml_composite_repository.py +156 -0
  144. raxe/infrastructure/suppression/yaml_repository.py +510 -0
  145. raxe/infrastructure/telemetry/__init__.py +79 -0
  146. raxe/infrastructure/telemetry/acquisition.py +179 -0
  147. raxe/infrastructure/telemetry/config.py +254 -0
  148. raxe/infrastructure/telemetry/credential_store.py +947 -0
  149. raxe/infrastructure/telemetry/dual_queue.py +1123 -0
  150. raxe/infrastructure/telemetry/flush_helper.py +343 -0
  151. raxe/infrastructure/telemetry/flush_scheduler.py +776 -0
  152. raxe/infrastructure/telemetry/health_client.py +394 -0
  153. raxe/infrastructure/telemetry/hook.py +347 -0
  154. raxe/infrastructure/telemetry/queue.py +520 -0
  155. raxe/infrastructure/telemetry/sender.py +476 -0
  156. raxe/infrastructure/tracking/__init__.py +13 -0
  157. raxe/infrastructure/tracking/usage.py +389 -0
  158. raxe/integrations/__init__.py +55 -0
  159. raxe/integrations/availability.py +143 -0
  160. raxe/integrations/registry.py +122 -0
  161. raxe/integrations/utils.py +135 -0
  162. raxe/mcp/__init__.py +62 -0
  163. raxe/mcp/cli.py +97 -0
  164. raxe/mcp/server.py +409 -0
  165. raxe/monitoring/__init__.py +51 -0
  166. raxe/monitoring/metrics.py +372 -0
  167. raxe/monitoring/profiler.py +388 -0
  168. raxe/monitoring/server.py +136 -0
  169. raxe/packs/core/v1.0.0/pack.yaml +1394 -0
  170. raxe/packs/core/v1.0.0/rules/PI/pi-001@1.0.0.yaml +49 -0
  171. raxe/packs/core/v1.0.0/rules/PI/pi-006@1.0.0.yaml +48 -0
  172. raxe/packs/core/v1.0.0/rules/PI/pi-014@1.0.0.yaml +54 -0
  173. raxe/packs/core/v1.0.0/rules/PI/pi-017@1.0.0.yaml +52 -0
  174. raxe/packs/core/v1.0.0/rules/PI/pi-022@1.0.0.yaml +67 -0
  175. raxe/packs/core/v1.0.0/rules/PI/pi-023@1.0.0.yaml +91 -0
  176. raxe/packs/core/v1.0.0/rules/PI/pi-024@1.0.0.yaml +80 -0
  177. raxe/packs/core/v1.0.0/rules/PI/pi-025@1.0.0.yaml +81 -0
  178. raxe/packs/core/v1.0.0/rules/PI/pi-026@1.0.0.yaml +50 -0
  179. raxe/packs/core/v1.0.0/rules/PI/pi-027@1.0.0.yaml +77 -0
  180. raxe/packs/core/v1.0.0/rules/PI/pi-028@1.0.0.yaml +52 -0
  181. raxe/packs/core/v1.0.0/rules/PI/pi-029@1.0.0.yaml +51 -0
  182. raxe/packs/core/v1.0.0/rules/PI/pi-030@1.0.0.yaml +55 -0
  183. raxe/packs/core/v1.0.0/rules/PI/pi-033@1.0.0.yaml +50 -0
  184. raxe/packs/core/v1.0.0/rules/PI/pi-034@1.0.0.yaml +50 -0
  185. raxe/packs/core/v1.0.0/rules/PI/pi-035@1.0.0.yaml +50 -0
  186. raxe/packs/core/v1.0.0/rules/PI/pi-046@1.0.0.yaml +50 -0
  187. raxe/packs/core/v1.0.0/rules/PI/pi-047@1.0.0.yaml +50 -0
  188. raxe/packs/core/v1.0.0/rules/PI/pi-048@1.0.0.yaml +50 -0
  189. raxe/packs/core/v1.0.0/rules/PI/pi-049@1.0.0.yaml +50 -0
  190. raxe/packs/core/v1.0.0/rules/PI/pi-050@1.0.0.yaml +50 -0
  191. raxe/packs/core/v1.0.0/rules/PI/pi-068@1.0.0.yaml +50 -0
  192. raxe/packs/core/v1.0.0/rules/PI/pi-078@1.0.0.yaml +50 -0
  193. raxe/packs/core/v1.0.0/rules/PI/pi-2001@1.0.0.yaml +35 -0
  194. raxe/packs/core/v1.0.0/rules/PI/pi-2004@1.0.0.yaml +39 -0
  195. raxe/packs/core/v1.0.0/rules/PI/pi-201@1.0.0.yaml +43 -0
  196. raxe/packs/core/v1.0.0/rules/PI/pi-202@1.0.0.yaml +47 -0
  197. raxe/packs/core/v1.0.0/rules/PI/pi-203@1.0.0.yaml +46 -0
  198. raxe/packs/core/v1.0.0/rules/PI/pi-3007@1.0.0.yaml +44 -0
  199. raxe/packs/core/v1.0.0/rules/PI/pi-3016@1.0.0.yaml +44 -0
  200. raxe/packs/core/v1.0.0/rules/PI/pi-3026@1.0.0.yaml +39 -0
  201. raxe/packs/core/v1.0.0/rules/PI/pi-3027@1.0.0.yaml +64 -0
  202. raxe/packs/core/v1.0.0/rules/PI/pi-3028@1.0.0.yaml +51 -0
  203. raxe/packs/core/v1.0.0/rules/PI/pi-3029@1.0.0.yaml +53 -0
  204. raxe/packs/core/v1.0.0/rules/PI/pi-3030@1.0.0.yaml +50 -0
  205. raxe/packs/core/v1.0.0/rules/PI/pi-3031@1.0.0.yaml +50 -0
  206. raxe/packs/core/v1.0.0/rules/PI/pi-3032@1.0.0.yaml +50 -0
  207. raxe/packs/core/v1.0.0/rules/PI/pi-3033@1.0.0.yaml +56 -0
  208. raxe/packs/core/v1.0.0/rules/PI/pi-3034@1.0.0.yaml +50 -0
  209. raxe/packs/core/v1.0.0/rules/PI/pi-79@1.0.0.yaml +38 -0
  210. raxe/packs/core/v1.0.0/rules/PI/pi-80@1.0.0.yaml +38 -0
  211. raxe/packs/core/v1.0.0/rules/PI/pi-81@1.0.0.yaml +38 -0
  212. raxe/packs/core/v1.0.0/rules/PI/pi-82@1.0.0.yaml +38 -0
  213. raxe/packs/core/v1.0.0/rules/PI/pi-83@1.0.0.yaml +38 -0
  214. raxe/packs/core/v1.0.0/rules/PI/pi-84@1.0.0.yaml +38 -0
  215. raxe/packs/core/v1.0.0/rules/PI/pi-85@1.0.0.yaml +38 -0
  216. raxe/packs/core/v1.0.0/rules/PI/pi-86@1.0.0.yaml +38 -0
  217. raxe/packs/core/v1.0.0/rules/PI/pi-87@1.0.0.yaml +38 -0
  218. raxe/packs/core/v1.0.0/rules/PI/pi-88@1.0.0.yaml +38 -0
  219. raxe/packs/core/v1.0.0/rules/PI/pi-89@1.0.0.yaml +38 -0
  220. raxe/packs/core/v1.0.0/rules/PI/pi-90@1.0.0.yaml +38 -0
  221. raxe/packs/core/v1.0.0/rules/PI/pi-91@1.0.0.yaml +38 -0
  222. raxe/packs/core/v1.0.0/rules/PI/pi-92@1.0.0.yaml +38 -0
  223. raxe/packs/core/v1.0.0/rules/PI/pi-93@1.0.0.yaml +38 -0
  224. raxe/packs/core/v1.0.0/rules/PI/pi-94@1.0.0.yaml +38 -0
  225. raxe/packs/core/v1.0.0/rules/PI/pi-95@1.0.0.yaml +38 -0
  226. raxe/packs/core/v1.0.0/rules/PI/pi-96@1.0.0.yaml +38 -0
  227. raxe/packs/core/v1.0.0/rules/PI/pi-97@1.0.0.yaml +38 -0
  228. raxe/packs/core/v1.0.0/rules/PI/pi-98@1.0.0.yaml +38 -0
  229. raxe/packs/core/v1.0.0/rules/cmd/cmd-001@1.0.0.yaml +48 -0
  230. raxe/packs/core/v1.0.0/rules/cmd/cmd-007@1.0.0.yaml +48 -0
  231. raxe/packs/core/v1.0.0/rules/cmd/cmd-015@1.0.0.yaml +56 -0
  232. raxe/packs/core/v1.0.0/rules/cmd/cmd-016@1.0.0.yaml +46 -0
  233. raxe/packs/core/v1.0.0/rules/cmd/cmd-017@1.0.0.yaml +57 -0
  234. raxe/packs/core/v1.0.0/rules/cmd/cmd-021@1.0.0.yaml +46 -0
  235. raxe/packs/core/v1.0.0/rules/cmd/cmd-022@1.0.0.yaml +46 -0
  236. raxe/packs/core/v1.0.0/rules/cmd/cmd-023@1.0.0.yaml +78 -0
  237. raxe/packs/core/v1.0.0/rules/cmd/cmd-024@1.0.0.yaml +46 -0
  238. raxe/packs/core/v1.0.0/rules/cmd/cmd-025@1.0.0.yaml +93 -0
  239. raxe/packs/core/v1.0.0/rules/cmd/cmd-026@1.0.0.yaml +81 -0
  240. raxe/packs/core/v1.0.0/rules/cmd/cmd-027@1.0.0.yaml +82 -0
  241. raxe/packs/core/v1.0.0/rules/cmd/cmd-028@1.0.0.yaml +46 -0
  242. raxe/packs/core/v1.0.0/rules/cmd/cmd-033@1.0.0.yaml +48 -0
  243. raxe/packs/core/v1.0.0/rules/cmd/cmd-036@1.0.0.yaml +47 -0
  244. raxe/packs/core/v1.0.0/rules/cmd/cmd-037@1.0.0.yaml +44 -0
  245. raxe/packs/core/v1.0.0/rules/cmd/cmd-052@1.0.0.yaml +43 -0
  246. raxe/packs/core/v1.0.0/rules/cmd/cmd-054@1.0.0.yaml +44 -0
  247. raxe/packs/core/v1.0.0/rules/cmd/cmd-056@1.0.0.yaml +43 -0
  248. raxe/packs/core/v1.0.0/rules/cmd/cmd-065@1.0.0.yaml +46 -0
  249. raxe/packs/core/v1.0.0/rules/cmd/cmd-075@1.0.0.yaml +45 -0
  250. raxe/packs/core/v1.0.0/rules/cmd/cmd-079@1.0.0.yaml +44 -0
  251. raxe/packs/core/v1.0.0/rules/cmd/cmd-1080@1.0.0.yaml +41 -0
  252. raxe/packs/core/v1.0.0/rules/cmd/cmd-1090@1.0.0.yaml +41 -0
  253. raxe/packs/core/v1.0.0/rules/cmd/cmd-1104@1.0.0.yaml +44 -0
  254. raxe/packs/core/v1.0.0/rules/cmd/cmd-1105@1.0.0.yaml +41 -0
  255. raxe/packs/core/v1.0.0/rules/cmd/cmd-1112@1.0.0.yaml +44 -0
  256. raxe/packs/core/v1.0.0/rules/cmd/cmd-201@1.0.0.yaml +47 -0
  257. raxe/packs/core/v1.0.0/rules/cmd/cmd-202@1.0.0.yaml +42 -0
  258. raxe/packs/core/v1.0.0/rules/cmd/cmd-203@1.0.0.yaml +43 -0
  259. raxe/packs/core/v1.0.0/rules/cmd/cmd-204@1.0.0.yaml +47 -0
  260. raxe/packs/core/v1.0.0/rules/cmd/cmd-205@1.0.0.yaml +44 -0
  261. raxe/packs/core/v1.0.0/rules/cmd/cmd-206@1.0.0.yaml +47 -0
  262. raxe/packs/core/v1.0.0/rules/cmd/cmd-207@1.0.0.yaml +46 -0
  263. raxe/packs/core/v1.0.0/rules/cmd/cmd-208@1.0.0.yaml +42 -0
  264. raxe/packs/core/v1.0.0/rules/cmd/cmd-209@1.0.0.yaml +38 -0
  265. raxe/packs/core/v1.0.0/rules/cmd/cmd-210@1.0.0.yaml +38 -0
  266. raxe/packs/core/v1.0.0/rules/cmd/cmd-211@1.0.0.yaml +38 -0
  267. raxe/packs/core/v1.0.0/rules/cmd/cmd-212@1.0.0.yaml +38 -0
  268. raxe/packs/core/v1.0.0/rules/cmd/cmd-213@1.0.0.yaml +38 -0
  269. raxe/packs/core/v1.0.0/rules/cmd/cmd-214@1.0.0.yaml +38 -0
  270. raxe/packs/core/v1.0.0/rules/cmd/cmd-215@1.0.0.yaml +38 -0
  271. raxe/packs/core/v1.0.0/rules/cmd/cmd-216@1.0.0.yaml +38 -0
  272. raxe/packs/core/v1.0.0/rules/cmd/cmd-217@1.0.0.yaml +38 -0
  273. raxe/packs/core/v1.0.0/rules/cmd/cmd-218@1.0.0.yaml +38 -0
  274. raxe/packs/core/v1.0.0/rules/cmd/cmd-219@1.0.0.yaml +38 -0
  275. raxe/packs/core/v1.0.0/rules/cmd/cmd-220@1.0.0.yaml +38 -0
  276. raxe/packs/core/v1.0.0/rules/cmd/cmd-221@1.0.0.yaml +38 -0
  277. raxe/packs/core/v1.0.0/rules/cmd/cmd-222@1.0.0.yaml +38 -0
  278. raxe/packs/core/v1.0.0/rules/cmd/cmd-223@1.0.0.yaml +38 -0
  279. raxe/packs/core/v1.0.0/rules/cmd/cmd-224@1.0.0.yaml +38 -0
  280. raxe/packs/core/v1.0.0/rules/cmd/cmd-225@1.0.0.yaml +38 -0
  281. raxe/packs/core/v1.0.0/rules/cmd/cmd-226@1.0.0.yaml +38 -0
  282. raxe/packs/core/v1.0.0/rules/cmd/cmd-227@1.0.0.yaml +38 -0
  283. raxe/packs/core/v1.0.0/rules/cmd/cmd-228@1.0.0.yaml +38 -0
  284. raxe/packs/core/v1.0.0/rules/cmd/cmd-229@1.0.0.yaml +38 -0
  285. raxe/packs/core/v1.0.0/rules/cmd/cmd-230@1.0.0.yaml +38 -0
  286. raxe/packs/core/v1.0.0/rules/cmd/cmd-231@1.0.0.yaml +38 -0
  287. raxe/packs/core/v1.0.0/rules/cmd/cmd-232@1.0.0.yaml +38 -0
  288. raxe/packs/core/v1.0.0/rules/cmd/cmd-233@1.0.0.yaml +38 -0
  289. raxe/packs/core/v1.0.0/rules/cmd/cmd-234@1.0.0.yaml +38 -0
  290. raxe/packs/core/v1.0.0/rules/cmd/cmd-235@1.0.0.yaml +38 -0
  291. raxe/packs/core/v1.0.0/rules/cmd/cmd-236@1.0.0.yaml +38 -0
  292. raxe/packs/core/v1.0.0/rules/cmd/cmd-237@1.0.0.yaml +38 -0
  293. raxe/packs/core/v1.0.0/rules/cmd/cmd-238@1.0.0.yaml +38 -0
  294. raxe/packs/core/v1.0.0/rules/enc/enc-001@1.0.0.yaml +48 -0
  295. raxe/packs/core/v1.0.0/rules/enc/enc-013@1.0.0.yaml +46 -0
  296. raxe/packs/core/v1.0.0/rules/enc/enc-019@1.0.0.yaml +43 -0
  297. raxe/packs/core/v1.0.0/rules/enc/enc-020@1.0.0.yaml +47 -0
  298. raxe/packs/core/v1.0.0/rules/enc/enc-024@1.0.0.yaml +46 -0
  299. raxe/packs/core/v1.0.0/rules/enc/enc-029@1.0.0.yaml +44 -0
  300. raxe/packs/core/v1.0.0/rules/enc/enc-038@1.0.0.yaml +44 -0
  301. raxe/packs/core/v1.0.0/rules/enc/enc-044@1.0.0.yaml +46 -0
  302. raxe/packs/core/v1.0.0/rules/enc/enc-067@1.0.0.yaml +42 -0
  303. raxe/packs/core/v1.0.0/rules/enc/enc-069@1.0.0.yaml +42 -0
  304. raxe/packs/core/v1.0.0/rules/enc/enc-100@1.0.0.yaml +38 -0
  305. raxe/packs/core/v1.0.0/rules/enc/enc-101@1.0.0.yaml +38 -0
  306. raxe/packs/core/v1.0.0/rules/enc/enc-102@1.0.0.yaml +38 -0
  307. raxe/packs/core/v1.0.0/rules/enc/enc-103@1.0.0.yaml +38 -0
  308. raxe/packs/core/v1.0.0/rules/enc/enc-104@1.0.0.yaml +38 -0
  309. raxe/packs/core/v1.0.0/rules/enc/enc-105@1.0.0.yaml +38 -0
  310. raxe/packs/core/v1.0.0/rules/enc/enc-106@1.0.0.yaml +38 -0
  311. raxe/packs/core/v1.0.0/rules/enc/enc-107@1.0.0.yaml +38 -0
  312. raxe/packs/core/v1.0.0/rules/enc/enc-108@1.0.0.yaml +38 -0
  313. raxe/packs/core/v1.0.0/rules/enc/enc-109@1.0.0.yaml +38 -0
  314. raxe/packs/core/v1.0.0/rules/enc/enc-110@1.0.0.yaml +38 -0
  315. raxe/packs/core/v1.0.0/rules/enc/enc-111@1.0.0.yaml +38 -0
  316. raxe/packs/core/v1.0.0/rules/enc/enc-112@1.0.0.yaml +38 -0
  317. raxe/packs/core/v1.0.0/rules/enc/enc-113@1.0.0.yaml +38 -0
  318. raxe/packs/core/v1.0.0/rules/enc/enc-114@1.0.0.yaml +38 -0
  319. raxe/packs/core/v1.0.0/rules/enc/enc-115@1.0.0.yaml +38 -0
  320. raxe/packs/core/v1.0.0/rules/enc/enc-116@1.0.0.yaml +38 -0
  321. raxe/packs/core/v1.0.0/rules/enc/enc-117@1.0.0.yaml +38 -0
  322. raxe/packs/core/v1.0.0/rules/enc/enc-118@1.0.0.yaml +38 -0
  323. raxe/packs/core/v1.0.0/rules/enc/enc-119@1.0.0.yaml +38 -0
  324. raxe/packs/core/v1.0.0/rules/enc/enc-120@1.0.0.yaml +38 -0
  325. raxe/packs/core/v1.0.0/rules/enc/enc-201@1.0.0.yaml +37 -0
  326. raxe/packs/core/v1.0.0/rules/enc/enc-202@1.0.0.yaml +41 -0
  327. raxe/packs/core/v1.0.0/rules/enc/enc-203@1.0.0.yaml +41 -0
  328. raxe/packs/core/v1.0.0/rules/enc/enc-3004@1.0.0.yaml +40 -0
  329. raxe/packs/core/v1.0.0/rules/enc/enc-3006@1.0.0.yaml +40 -0
  330. raxe/packs/core/v1.0.0/rules/enc/enc-3011@1.0.0.yaml +40 -0
  331. raxe/packs/core/v1.0.0/rules/enc/enc-5016@1.0.0.yaml +46 -0
  332. raxe/packs/core/v1.0.0/rules/enc/enc-6001@1.0.0.yaml +53 -0
  333. raxe/packs/core/v1.0.0/rules/enc/enc-6002@1.0.0.yaml +41 -0
  334. raxe/packs/core/v1.0.0/rules/enc/enc-70@1.0.0.yaml +38 -0
  335. raxe/packs/core/v1.0.0/rules/enc/enc-71@1.0.0.yaml +38 -0
  336. raxe/packs/core/v1.0.0/rules/enc/enc-72@1.0.0.yaml +38 -0
  337. raxe/packs/core/v1.0.0/rules/enc/enc-73@1.0.0.yaml +38 -0
  338. raxe/packs/core/v1.0.0/rules/enc/enc-74@1.0.0.yaml +38 -0
  339. raxe/packs/core/v1.0.0/rules/enc/enc-75@1.0.0.yaml +38 -0
  340. raxe/packs/core/v1.0.0/rules/enc/enc-76@1.0.0.yaml +38 -0
  341. raxe/packs/core/v1.0.0/rules/enc/enc-77@1.0.0.yaml +38 -0
  342. raxe/packs/core/v1.0.0/rules/enc/enc-78@1.0.0.yaml +38 -0
  343. raxe/packs/core/v1.0.0/rules/enc/enc-79@1.0.0.yaml +38 -0
  344. raxe/packs/core/v1.0.0/rules/enc/enc-80@1.0.0.yaml +38 -0
  345. raxe/packs/core/v1.0.0/rules/enc/enc-81@1.0.0.yaml +38 -0
  346. raxe/packs/core/v1.0.0/rules/enc/enc-82@1.0.0.yaml +38 -0
  347. raxe/packs/core/v1.0.0/rules/enc/enc-83@1.0.0.yaml +38 -0
  348. raxe/packs/core/v1.0.0/rules/enc/enc-84@1.0.0.yaml +38 -0
  349. raxe/packs/core/v1.0.0/rules/enc/enc-85@1.0.0.yaml +38 -0
  350. raxe/packs/core/v1.0.0/rules/enc/enc-86@1.0.0.yaml +38 -0
  351. raxe/packs/core/v1.0.0/rules/enc/enc-87@1.0.0.yaml +38 -0
  352. raxe/packs/core/v1.0.0/rules/enc/enc-88@1.0.0.yaml +38 -0
  353. raxe/packs/core/v1.0.0/rules/enc/enc-89@1.0.0.yaml +38 -0
  354. raxe/packs/core/v1.0.0/rules/enc/enc-90@1.0.0.yaml +38 -0
  355. raxe/packs/core/v1.0.0/rules/enc/enc-91@1.0.0.yaml +38 -0
  356. raxe/packs/core/v1.0.0/rules/enc/enc-92@1.0.0.yaml +38 -0
  357. raxe/packs/core/v1.0.0/rules/enc/enc-93@1.0.0.yaml +38 -0
  358. raxe/packs/core/v1.0.0/rules/enc/enc-94@1.0.0.yaml +38 -0
  359. raxe/packs/core/v1.0.0/rules/enc/enc-95@1.0.0.yaml +38 -0
  360. raxe/packs/core/v1.0.0/rules/enc/enc-96@1.0.0.yaml +38 -0
  361. raxe/packs/core/v1.0.0/rules/enc/enc-97@1.0.0.yaml +38 -0
  362. raxe/packs/core/v1.0.0/rules/enc/enc-98@1.0.0.yaml +38 -0
  363. raxe/packs/core/v1.0.0/rules/enc/enc-99@1.0.0.yaml +38 -0
  364. raxe/packs/core/v1.0.0/rules/hc/hc-001@1.0.0.yaml +73 -0
  365. raxe/packs/core/v1.0.0/rules/hc/hc-002@1.0.0.yaml +71 -0
  366. raxe/packs/core/v1.0.0/rules/hc/hc-003@1.0.0.yaml +65 -0
  367. raxe/packs/core/v1.0.0/rules/hc/hc-004@1.0.0.yaml +73 -0
  368. raxe/packs/core/v1.0.0/rules/hc/hc-101@1.0.0.yaml +47 -0
  369. raxe/packs/core/v1.0.0/rules/hc/hc-102@1.0.0.yaml +47 -0
  370. raxe/packs/core/v1.0.0/rules/hc/hc-103@1.0.0.yaml +47 -0
  371. raxe/packs/core/v1.0.0/rules/hc/hc-104@1.0.0.yaml +47 -0
  372. raxe/packs/core/v1.0.0/rules/hc/hc-105@1.0.0.yaml +48 -0
  373. raxe/packs/core/v1.0.0/rules/hc/hc-106@1.0.0.yaml +40 -0
  374. raxe/packs/core/v1.0.0/rules/hc/hc-107@1.0.0.yaml +47 -0
  375. raxe/packs/core/v1.0.0/rules/hc/hc-108@1.0.0.yaml +47 -0
  376. raxe/packs/core/v1.0.0/rules/hc/hc-109@1.0.0.yaml +50 -0
  377. raxe/packs/core/v1.0.0/rules/hc/hc-110@1.0.0.yaml +56 -0
  378. raxe/packs/core/v1.0.0/rules/hc/hc-111@1.0.0.yaml +49 -0
  379. raxe/packs/core/v1.0.0/rules/hc/hc-112@1.0.0.yaml +53 -0
  380. raxe/packs/core/v1.0.0/rules/hc/hc-113@1.0.0.yaml +52 -0
  381. raxe/packs/core/v1.0.0/rules/hc/hc-114@1.0.0.yaml +52 -0
  382. raxe/packs/core/v1.0.0/rules/hc/hc-115@1.0.0.yaml +52 -0
  383. raxe/packs/core/v1.0.0/rules/hc/hc-116@1.0.0.yaml +53 -0
  384. raxe/packs/core/v1.0.0/rules/hc/hc-117@1.0.0.yaml +54 -0
  385. raxe/packs/core/v1.0.0/rules/hc/hc-118@1.0.0.yaml +52 -0
  386. raxe/packs/core/v1.0.0/rules/hc/hc-119@1.0.0.yaml +51 -0
  387. raxe/packs/core/v1.0.0/rules/hc/hc-120@1.0.0.yaml +52 -0
  388. raxe/packs/core/v1.0.0/rules/hc/hc-121@1.0.0.yaml +51 -0
  389. raxe/packs/core/v1.0.0/rules/hc/hc-122@1.0.0.yaml +51 -0
  390. raxe/packs/core/v1.0.0/rules/hc/hc-123@1.0.0.yaml +52 -0
  391. raxe/packs/core/v1.0.0/rules/hc/hc-124@1.0.0.yaml +53 -0
  392. raxe/packs/core/v1.0.0/rules/hc/hc-125@1.0.0.yaml +53 -0
  393. raxe/packs/core/v1.0.0/rules/hc/hc-126@1.0.0.yaml +53 -0
  394. raxe/packs/core/v1.0.0/rules/hc/hc-127@1.0.0.yaml +53 -0
  395. raxe/packs/core/v1.0.0/rules/hc/hc-128@1.0.0.yaml +53 -0
  396. raxe/packs/core/v1.0.0/rules/hc/hc-129@1.0.0.yaml +51 -0
  397. raxe/packs/core/v1.0.0/rules/hc/hc-130@1.0.0.yaml +51 -0
  398. raxe/packs/core/v1.0.0/rules/hc/hc-131@1.0.0.yaml +51 -0
  399. raxe/packs/core/v1.0.0/rules/hc/hc-132@1.0.0.yaml +51 -0
  400. raxe/packs/core/v1.0.0/rules/hc/hc-133@1.0.0.yaml +53 -0
  401. raxe/packs/core/v1.0.0/rules/hc/hc-134@1.0.0.yaml +51 -0
  402. raxe/packs/core/v1.0.0/rules/hc/hc-135@1.0.0.yaml +51 -0
  403. raxe/packs/core/v1.0.0/rules/hc/hc-136@1.0.0.yaml +51 -0
  404. raxe/packs/core/v1.0.0/rules/hc/hc-137@1.0.0.yaml +51 -0
  405. raxe/packs/core/v1.0.0/rules/hc/hc-138@1.0.0.yaml +51 -0
  406. raxe/packs/core/v1.0.0/rules/hc/hc-139@1.0.0.yaml +51 -0
  407. raxe/packs/core/v1.0.0/rules/hc/hc-140@1.0.0.yaml +51 -0
  408. raxe/packs/core/v1.0.0/rules/hc/hc-141@1.0.0.yaml +41 -0
  409. raxe/packs/core/v1.0.0/rules/hc/hc-142@1.0.0.yaml +37 -0
  410. raxe/packs/core/v1.0.0/rules/hc/hc-143@1.0.0.yaml +37 -0
  411. raxe/packs/core/v1.0.0/rules/hc/hc-144@1.0.0.yaml +37 -0
  412. raxe/packs/core/v1.0.0/rules/hc/hc-145@1.0.0.yaml +37 -0
  413. raxe/packs/core/v1.0.0/rules/hc/hc-146@1.0.0.yaml +37 -0
  414. raxe/packs/core/v1.0.0/rules/hc/hc-147@1.0.0.yaml +37 -0
  415. raxe/packs/core/v1.0.0/rules/hc/hc-148@1.0.0.yaml +37 -0
  416. raxe/packs/core/v1.0.0/rules/hc/hc-149@1.0.0.yaml +37 -0
  417. raxe/packs/core/v1.0.0/rules/hc/hc-150@1.0.0.yaml +37 -0
  418. raxe/packs/core/v1.0.0/rules/hc/hc-151@1.0.0.yaml +37 -0
  419. raxe/packs/core/v1.0.0/rules/hc/hc-152@1.0.0.yaml +37 -0
  420. raxe/packs/core/v1.0.0/rules/hc/hc-153@1.0.0.yaml +37 -0
  421. raxe/packs/core/v1.0.0/rules/hc/hc-154@1.0.0.yaml +37 -0
  422. raxe/packs/core/v1.0.0/rules/hc/hc-155@1.0.0.yaml +37 -0
  423. raxe/packs/core/v1.0.0/rules/hc/hc-156@1.0.0.yaml +37 -0
  424. raxe/packs/core/v1.0.0/rules/hc/hc-157@1.0.0.yaml +37 -0
  425. raxe/packs/core/v1.0.0/rules/hc/hc-158@1.0.0.yaml +37 -0
  426. raxe/packs/core/v1.0.0/rules/hc/hc-159@1.0.0.yaml +37 -0
  427. raxe/packs/core/v1.0.0/rules/hc/hc-160@1.0.0.yaml +37 -0
  428. raxe/packs/core/v1.0.0/rules/hc/hc-161@1.0.0.yaml +37 -0
  429. raxe/packs/core/v1.0.0/rules/jb/jb-001@1.0.0.yaml +47 -0
  430. raxe/packs/core/v1.0.0/rules/jb/jb-009@1.0.0.yaml +47 -0
  431. raxe/packs/core/v1.0.0/rules/jb/jb-020@1.0.0.yaml +47 -0
  432. raxe/packs/core/v1.0.0/rules/jb/jb-021@1.0.0.yaml +46 -0
  433. raxe/packs/core/v1.0.0/rules/jb/jb-022@1.0.0.yaml +47 -0
  434. raxe/packs/core/v1.0.0/rules/jb/jb-028@1.0.0.yaml +43 -0
  435. raxe/packs/core/v1.0.0/rules/jb/jb-033@1.0.0.yaml +46 -0
  436. raxe/packs/core/v1.0.0/rules/jb/jb-034@1.0.0.yaml +46 -0
  437. raxe/packs/core/v1.0.0/rules/jb/jb-036@1.0.0.yaml +41 -0
  438. raxe/packs/core/v1.0.0/rules/jb/jb-039@1.0.0.yaml +41 -0
  439. raxe/packs/core/v1.0.0/rules/jb/jb-056@1.0.0.yaml +38 -0
  440. raxe/packs/core/v1.0.0/rules/jb/jb-066@1.0.0.yaml +37 -0
  441. raxe/packs/core/v1.0.0/rules/jb/jb-076@1.0.0.yaml +37 -0
  442. raxe/packs/core/v1.0.0/rules/jb/jb-098@1.0.0.yaml +46 -0
  443. raxe/packs/core/v1.0.0/rules/jb/jb-103@1.0.0.yaml +47 -0
  444. raxe/packs/core/v1.0.0/rules/jb/jb-104@1.0.0.yaml +52 -0
  445. raxe/packs/core/v1.0.0/rules/jb/jb-105@1.0.0.yaml +56 -0
  446. raxe/packs/core/v1.0.0/rules/jb/jb-110@1.0.0.yaml +56 -0
  447. raxe/packs/core/v1.0.0/rules/jb/jb-111@1.0.0.yaml +57 -0
  448. raxe/packs/core/v1.0.0/rules/jb/jb-112@1.0.0.yaml +38 -0
  449. raxe/packs/core/v1.0.0/rules/jb/jb-113@1.0.0.yaml +38 -0
  450. raxe/packs/core/v1.0.0/rules/jb/jb-114@1.0.0.yaml +38 -0
  451. raxe/packs/core/v1.0.0/rules/jb/jb-115@1.0.0.yaml +38 -0
  452. raxe/packs/core/v1.0.0/rules/jb/jb-116@1.0.0.yaml +38 -0
  453. raxe/packs/core/v1.0.0/rules/jb/jb-117@1.0.0.yaml +38 -0
  454. raxe/packs/core/v1.0.0/rules/jb/jb-118@1.0.0.yaml +38 -0
  455. raxe/packs/core/v1.0.0/rules/jb/jb-119@1.0.0.yaml +38 -0
  456. raxe/packs/core/v1.0.0/rules/jb/jb-120@1.0.0.yaml +38 -0
  457. raxe/packs/core/v1.0.0/rules/jb/jb-121@1.0.0.yaml +38 -0
  458. raxe/packs/core/v1.0.0/rules/jb/jb-122@1.0.0.yaml +38 -0
  459. raxe/packs/core/v1.0.0/rules/jb/jb-123@1.0.0.yaml +38 -0
  460. raxe/packs/core/v1.0.0/rules/jb/jb-124@1.0.0.yaml +38 -0
  461. raxe/packs/core/v1.0.0/rules/jb/jb-125@1.0.0.yaml +38 -0
  462. raxe/packs/core/v1.0.0/rules/jb/jb-126@1.0.0.yaml +38 -0
  463. raxe/packs/core/v1.0.0/rules/jb/jb-127@1.0.0.yaml +38 -0
  464. raxe/packs/core/v1.0.0/rules/jb/jb-128@1.0.0.yaml +38 -0
  465. raxe/packs/core/v1.0.0/rules/jb/jb-129@1.0.0.yaml +38 -0
  466. raxe/packs/core/v1.0.0/rules/jb/jb-130@1.0.0.yaml +38 -0
  467. raxe/packs/core/v1.0.0/rules/jb/jb-131@1.0.0.yaml +38 -0
  468. raxe/packs/core/v1.0.0/rules/jb/jb-132@1.0.0.yaml +38 -0
  469. raxe/packs/core/v1.0.0/rules/jb/jb-133@1.0.0.yaml +38 -0
  470. raxe/packs/core/v1.0.0/rules/jb/jb-134@1.0.0.yaml +38 -0
  471. raxe/packs/core/v1.0.0/rules/jb/jb-135@1.0.0.yaml +38 -0
  472. raxe/packs/core/v1.0.0/rules/jb/jb-136@1.0.0.yaml +38 -0
  473. raxe/packs/core/v1.0.0/rules/jb/jb-137@1.0.0.yaml +38 -0
  474. raxe/packs/core/v1.0.0/rules/jb/jb-138@1.0.0.yaml +38 -0
  475. raxe/packs/core/v1.0.0/rules/jb/jb-139@1.0.0.yaml +38 -0
  476. raxe/packs/core/v1.0.0/rules/jb/jb-140@1.0.0.yaml +38 -0
  477. raxe/packs/core/v1.0.0/rules/jb/jb-141@1.0.0.yaml +38 -0
  478. raxe/packs/core/v1.0.0/rules/jb/jb-142@1.0.0.yaml +38 -0
  479. raxe/packs/core/v1.0.0/rules/jb/jb-143@1.0.0.yaml +38 -0
  480. raxe/packs/core/v1.0.0/rules/jb/jb-144@1.0.0.yaml +38 -0
  481. raxe/packs/core/v1.0.0/rules/jb/jb-145@1.0.0.yaml +38 -0
  482. raxe/packs/core/v1.0.0/rules/jb/jb-146@1.0.0.yaml +38 -0
  483. raxe/packs/core/v1.0.0/rules/jb/jb-147@1.0.0.yaml +38 -0
  484. raxe/packs/core/v1.0.0/rules/jb/jb-148@1.0.0.yaml +38 -0
  485. raxe/packs/core/v1.0.0/rules/jb/jb-149@1.0.0.yaml +38 -0
  486. raxe/packs/core/v1.0.0/rules/jb/jb-150@1.0.0.yaml +38 -0
  487. raxe/packs/core/v1.0.0/rules/jb/jb-151@1.0.0.yaml +38 -0
  488. raxe/packs/core/v1.0.0/rules/jb/jb-152@1.0.0.yaml +38 -0
  489. raxe/packs/core/v1.0.0/rules/jb/jb-153@1.0.0.yaml +38 -0
  490. raxe/packs/core/v1.0.0/rules/jb/jb-154@1.0.0.yaml +38 -0
  491. raxe/packs/core/v1.0.0/rules/jb/jb-155@1.0.0.yaml +38 -0
  492. raxe/packs/core/v1.0.0/rules/jb/jb-156@1.0.0.yaml +38 -0
  493. raxe/packs/core/v1.0.0/rules/jb/jb-157@1.0.0.yaml +38 -0
  494. raxe/packs/core/v1.0.0/rules/jb/jb-158@1.0.0.yaml +38 -0
  495. raxe/packs/core/v1.0.0/rules/jb/jb-159@1.0.0.yaml +38 -0
  496. raxe/packs/core/v1.0.0/rules/jb/jb-160@1.0.0.yaml +38 -0
  497. raxe/packs/core/v1.0.0/rules/jb/jb-161@1.0.0.yaml +38 -0
  498. raxe/packs/core/v1.0.0/rules/jb/jb-162@1.0.0.yaml +38 -0
  499. raxe/packs/core/v1.0.0/rules/jb/jb-201@1.0.0.yaml +40 -0
  500. raxe/packs/core/v1.0.0/rules/jb/jb-202@1.0.0.yaml +41 -0
  501. raxe/packs/core/v1.0.0/rules/jb/jb-203@1.0.0.yaml +51 -0
  502. raxe/packs/core/v1.0.0/rules/jb/jb-204@1.0.0.yaml +50 -0
  503. raxe/packs/core/v1.0.0/rules/jb/jb-205@1.0.0.yaml +50 -0
  504. raxe/packs/core/v1.0.0/rules/jb/jb-206@1.0.0.yaml +50 -0
  505. raxe/packs/core/v1.0.0/rules/jb/jb-207@1.0.0.yaml +49 -0
  506. raxe/packs/core/v1.0.0/rules/pii/pii-001@1.0.0.yaml +48 -0
  507. raxe/packs/core/v1.0.0/rules/pii/pii-009@1.0.0.yaml +48 -0
  508. raxe/packs/core/v1.0.0/rules/pii/pii-012@1.0.0.yaml +48 -0
  509. raxe/packs/core/v1.0.0/rules/pii/pii-017@1.0.0.yaml +48 -0
  510. raxe/packs/core/v1.0.0/rules/pii/pii-022@1.0.0.yaml +47 -0
  511. raxe/packs/core/v1.0.0/rules/pii/pii-025@1.0.0.yaml +47 -0
  512. raxe/packs/core/v1.0.0/rules/pii/pii-027@1.0.0.yaml +47 -0
  513. raxe/packs/core/v1.0.0/rules/pii/pii-028@1.0.0.yaml +47 -0
  514. raxe/packs/core/v1.0.0/rules/pii/pii-034@1.0.0.yaml +47 -0
  515. raxe/packs/core/v1.0.0/rules/pii/pii-037@1.0.0.yaml +47 -0
  516. raxe/packs/core/v1.0.0/rules/pii/pii-040@1.0.0.yaml +47 -0
  517. raxe/packs/core/v1.0.0/rules/pii/pii-041@1.0.0.yaml +47 -0
  518. raxe/packs/core/v1.0.0/rules/pii/pii-044@1.0.0.yaml +47 -0
  519. raxe/packs/core/v1.0.0/rules/pii/pii-050@1.0.0.yaml +57 -0
  520. raxe/packs/core/v1.0.0/rules/pii/pii-051@1.0.0.yaml +53 -0
  521. raxe/packs/core/v1.0.0/rules/pii/pii-052@1.0.0.yaml +52 -0
  522. raxe/packs/core/v1.0.0/rules/pii/pii-053@1.0.0.yaml +56 -0
  523. raxe/packs/core/v1.0.0/rules/pii/pii-054@1.0.0.yaml +53 -0
  524. raxe/packs/core/v1.0.0/rules/pii/pii-055@1.0.0.yaml +51 -0
  525. raxe/packs/core/v1.0.0/rules/pii/pii-056@1.0.0.yaml +51 -0
  526. raxe/packs/core/v1.0.0/rules/pii/pii-058@1.0.0.yaml +47 -0
  527. raxe/packs/core/v1.0.0/rules/pii/pii-2015@1.0.0.yaml +41 -0
  528. raxe/packs/core/v1.0.0/rules/pii/pii-2025@1.0.0.yaml +35 -0
  529. raxe/packs/core/v1.0.0/rules/pii/pii-2026@1.0.0.yaml +39 -0
  530. raxe/packs/core/v1.0.0/rules/pii/pii-2035@1.0.0.yaml +39 -0
  531. raxe/packs/core/v1.0.0/rules/pii/pii-2037@1.0.0.yaml +39 -0
  532. raxe/packs/core/v1.0.0/rules/pii/pii-2042@1.0.0.yaml +39 -0
  533. raxe/packs/core/v1.0.0/rules/pii/pii-3001@1.0.0.yaml +39 -0
  534. raxe/packs/core/v1.0.0/rules/pii/pii-3002@1.0.0.yaml +41 -0
  535. raxe/packs/core/v1.0.0/rules/pii/pii-3003@1.0.0.yaml +36 -0
  536. raxe/packs/core/v1.0.0/rules/pii/pii-3004@1.0.0.yaml +41 -0
  537. raxe/packs/core/v1.0.0/rules/pii/pii-3005@1.0.0.yaml +39 -0
  538. raxe/packs/core/v1.0.0/rules/pii/pii-3006@1.0.0.yaml +35 -0
  539. raxe/packs/core/v1.0.0/rules/pii/pii-3007@1.0.0.yaml +37 -0
  540. raxe/packs/core/v1.0.0/rules/pii/pii-3008@1.0.0.yaml +35 -0
  541. raxe/packs/core/v1.0.0/rules/pii/pii-3009@1.0.0.yaml +42 -0
  542. raxe/packs/core/v1.0.0/rules/pii/pii-3010@1.0.0.yaml +39 -0
  543. raxe/packs/core/v1.0.0/rules/pii/pii-3011@1.0.0.yaml +35 -0
  544. raxe/packs/core/v1.0.0/rules/pii/pii-3012@1.0.0.yaml +35 -0
  545. raxe/packs/core/v1.0.0/rules/pii/pii-3013@1.0.0.yaml +36 -0
  546. raxe/packs/core/v1.0.0/rules/pii/pii-3014@1.0.0.yaml +36 -0
  547. raxe/packs/core/v1.0.0/rules/pii/pii-3015@1.0.0.yaml +42 -0
  548. raxe/packs/core/v1.0.0/rules/pii/pii-3016@1.0.0.yaml +42 -0
  549. raxe/packs/core/v1.0.0/rules/pii/pii-3017@1.0.0.yaml +40 -0
  550. raxe/packs/core/v1.0.0/rules/pii/pii-3018@1.0.0.yaml +38 -0
  551. raxe/packs/core/v1.0.0/rules/pii/pii-3019@1.0.0.yaml +40 -0
  552. raxe/packs/core/v1.0.0/rules/pii/pii-3020@1.0.0.yaml +40 -0
  553. raxe/packs/core/v1.0.0/rules/pii/pii-3021@1.0.0.yaml +39 -0
  554. raxe/packs/core/v1.0.0/rules/pii/pii-3022@1.0.0.yaml +36 -0
  555. raxe/packs/core/v1.0.0/rules/pii/pii-3023@1.0.0.yaml +41 -0
  556. raxe/packs/core/v1.0.0/rules/pii/pii-3024@1.0.0.yaml +37 -0
  557. raxe/packs/core/v1.0.0/rules/pii/pii-3025@1.0.0.yaml +38 -0
  558. raxe/packs/core/v1.0.0/rules/pii/pii-3026@1.0.0.yaml +42 -0
  559. raxe/packs/core/v1.0.0/rules/pii/pii-3027@1.0.0.yaml +38 -0
  560. raxe/packs/core/v1.0.0/rules/pii/pii-3028@1.0.0.yaml +42 -0
  561. raxe/packs/core/v1.0.0/rules/pii/pii-3029@1.0.0.yaml +36 -0
  562. raxe/packs/core/v1.0.0/rules/pii/pii-3030@1.0.0.yaml +42 -0
  563. raxe/packs/core/v1.0.0/rules/pii/pii-3031@1.0.0.yaml +37 -0
  564. raxe/packs/core/v1.0.0/rules/pii/pii-3032@1.0.0.yaml +42 -0
  565. raxe/packs/core/v1.0.0/rules/pii/pii-3033@1.0.0.yaml +39 -0
  566. raxe/packs/core/v1.0.0/rules/pii/pii-3034@1.0.0.yaml +40 -0
  567. raxe/packs/core/v1.0.0/rules/pii/pii-3035@1.0.0.yaml +43 -0
  568. raxe/packs/core/v1.0.0/rules/pii/pii-3036@1.0.0.yaml +41 -0
  569. raxe/packs/core/v1.0.0/rules/pii/pii-3037@1.0.0.yaml +35 -0
  570. raxe/packs/core/v1.0.0/rules/pii/pii-3038@1.0.0.yaml +35 -0
  571. raxe/packs/core/v1.0.0/rules/pii/pii-3039@1.0.0.yaml +35 -0
  572. raxe/packs/core/v1.0.0/rules/pii/pii-3040@1.0.0.yaml +41 -0
  573. raxe/packs/core/v1.0.0/rules/pii/pii-3041@1.0.0.yaml +39 -0
  574. raxe/packs/core/v1.0.0/rules/pii/pii-3042@1.0.0.yaml +36 -0
  575. raxe/packs/core/v1.0.0/rules/pii/pii-3043@1.0.0.yaml +35 -0
  576. raxe/packs/core/v1.0.0/rules/pii/pii-3044@1.0.0.yaml +43 -0
  577. raxe/packs/core/v1.0.0/rules/pii/pii-3045@1.0.0.yaml +36 -0
  578. raxe/packs/core/v1.0.0/rules/pii/pii-3046@1.0.0.yaml +37 -0
  579. raxe/packs/core/v1.0.0/rules/pii/pii-3047@1.0.0.yaml +36 -0
  580. raxe/packs/core/v1.0.0/rules/pii/pii-3048@1.0.0.yaml +36 -0
  581. raxe/packs/core/v1.0.0/rules/pii/pii-3049@1.0.0.yaml +38 -0
  582. raxe/packs/core/v1.0.0/rules/pii/pii-3050@1.0.0.yaml +44 -0
  583. raxe/packs/core/v1.0.0/rules/pii/pii-3051@1.0.0.yaml +35 -0
  584. raxe/packs/core/v1.0.0/rules/pii/pii-3052@1.0.0.yaml +36 -0
  585. raxe/packs/core/v1.0.0/rules/pii/pii-3053@1.0.0.yaml +35 -0
  586. raxe/packs/core/v1.0.0/rules/pii/pii-3054@1.0.0.yaml +35 -0
  587. raxe/packs/core/v1.0.0/rules/pii/pii-3055@1.0.0.yaml +40 -0
  588. raxe/packs/core/v1.0.0/rules/pii/pii-3056@1.0.0.yaml +38 -0
  589. raxe/packs/core/v1.0.0/rules/pii/pii-3057@1.0.0.yaml +40 -0
  590. raxe/packs/core/v1.0.0/rules/pii/pii-3058@1.0.0.yaml +43 -0
  591. raxe/packs/core/v1.0.0/rules/pii/pii-3059@1.0.0.yaml +42 -0
  592. raxe/packs/core/v1.0.0/rules/pii/pii-3060@1.0.0.yaml +42 -0
  593. raxe/packs/core/v1.0.0/rules/pii/pii-3061@1.0.0.yaml +50 -0
  594. raxe/packs/core/v1.0.0/rules/pii/pii-3062@1.0.0.yaml +50 -0
  595. raxe/packs/core/v1.0.0/rules/pii/pii-3063@1.0.0.yaml +54 -0
  596. raxe/packs/core/v1.0.0/rules/pii/pii-3064@1.0.0.yaml +78 -0
  597. raxe/packs/core/v1.0.0/rules/pii/pii-3065@1.0.0.yaml +84 -0
  598. raxe/packs/core/v1.0.0/rules/pii/pii-3066@1.0.0.yaml +84 -0
  599. raxe/packs/core/v1.0.0/rules/pii/pii-3067@1.0.0.yaml +88 -0
  600. raxe/packs/core/v1.0.0/rules/pii/pii-3068@1.0.0.yaml +94 -0
  601. raxe/packs/core/v1.0.0/rules/pii/pii-3069@1.0.0.yaml +90 -0
  602. raxe/packs/core/v1.0.0/rules/pii/pii-3070@1.0.0.yaml +99 -0
  603. raxe/packs/core/v1.0.0/rules/pii/pii-3071@1.0.0.yaml +91 -0
  604. raxe/packs/core/v1.0.0/rules/pii/pii-3072@1.0.0.yaml +38 -0
  605. raxe/packs/core/v1.0.0/rules/pii/pii-3073@1.0.0.yaml +38 -0
  606. raxe/packs/core/v1.0.0/rules/pii/pii-3074@1.0.0.yaml +38 -0
  607. raxe/packs/core/v1.0.0/rules/pii/pii-3075@1.0.0.yaml +38 -0
  608. raxe/packs/core/v1.0.0/rules/pii/pii-3076@1.0.0.yaml +38 -0
  609. raxe/packs/core/v1.0.0/rules/pii/pii-3077@1.0.0.yaml +38 -0
  610. raxe/packs/core/v1.0.0/rules/pii/pii-3078@1.0.0.yaml +38 -0
  611. raxe/packs/core/v1.0.0/rules/pii/pii-3079@1.0.0.yaml +38 -0
  612. raxe/packs/core/v1.0.0/rules/pii/pii-3080@1.0.0.yaml +38 -0
  613. raxe/packs/core/v1.0.0/rules/pii/pii-3081@1.0.0.yaml +38 -0
  614. raxe/packs/core/v1.0.0/rules/pii/pii-3082@1.0.0.yaml +38 -0
  615. raxe/packs/core/v1.0.0/rules/pii/pii-3083@1.0.0.yaml +38 -0
  616. raxe/packs/core/v1.0.0/rules/pii/pii-3084@1.0.0.yaml +38 -0
  617. raxe/packs/core/v1.0.0/rules/pii/pii-3085@1.0.0.yaml +38 -0
  618. raxe/packs/core/v1.0.0/rules/rag/rag-016@1.0.0.yaml +47 -0
  619. raxe/packs/core/v1.0.0/rules/rag/rag-028@1.0.0.yaml +47 -0
  620. raxe/packs/core/v1.0.0/rules/rag/rag-042@1.0.0.yaml +47 -0
  621. raxe/packs/core/v1.0.0/rules/rag/rag-044@1.0.0.yaml +47 -0
  622. raxe/packs/core/v1.0.0/rules/rag/rag-045@1.0.0.yaml +47 -0
  623. raxe/packs/core/v1.0.0/rules/rag/rag-050@1.0.0.yaml +47 -0
  624. raxe/packs/core/v1.0.0/rules/rag/rag-201@1.0.0.yaml +41 -0
  625. raxe/packs/core/v1.0.0/rules/rag/rag-202@1.0.0.yaml +41 -0
  626. raxe/packs/core/v1.0.0/rules/rag/rag-3001@1.0.0.yaml +41 -0
  627. raxe/packs/core/v1.0.0/rules/rag/rag-3006@1.0.0.yaml +41 -0
  628. raxe/packs/core/v1.0.0/rules/rag/rag-3009@1.0.0.yaml +41 -0
  629. raxe/packs/core/v1.0.0/rules/rag/rag-3012@1.0.0.yaml +41 -0
  630. raxe/plugins/__init__.py +98 -0
  631. raxe/plugins/custom_rules.py +380 -0
  632. raxe/plugins/loader.py +389 -0
  633. raxe/plugins/manager.py +538 -0
  634. raxe/plugins/protocol.py +428 -0
  635. raxe/py.typed +0 -0
  636. raxe/sdk/__init__.py +77 -0
  637. raxe/sdk/agent_scanner.py +1918 -0
  638. raxe/sdk/client.py +1603 -0
  639. raxe/sdk/decorator.py +175 -0
  640. raxe/sdk/exceptions.py +859 -0
  641. raxe/sdk/integrations/__init__.py +277 -0
  642. raxe/sdk/integrations/agent_scanner.py +71 -0
  643. raxe/sdk/integrations/autogen.py +872 -0
  644. raxe/sdk/integrations/crewai.py +1368 -0
  645. raxe/sdk/integrations/dspy.py +845 -0
  646. raxe/sdk/integrations/extractors.py +363 -0
  647. raxe/sdk/integrations/huggingface.py +395 -0
  648. raxe/sdk/integrations/langchain.py +948 -0
  649. raxe/sdk/integrations/litellm.py +484 -0
  650. raxe/sdk/integrations/llamaindex.py +1049 -0
  651. raxe/sdk/integrations/portkey.py +831 -0
  652. raxe/sdk/suppression_context.py +215 -0
  653. raxe/sdk/wrappers/__init__.py +163 -0
  654. raxe/sdk/wrappers/anthropic.py +310 -0
  655. raxe/sdk/wrappers/openai.py +221 -0
  656. raxe/sdk/wrappers/vertexai.py +484 -0
  657. raxe/utils/__init__.py +12 -0
  658. raxe/utils/error_sanitizer.py +135 -0
  659. raxe/utils/logging.py +241 -0
  660. raxe/utils/performance.py +414 -0
  661. raxe/utils/profiler.py +339 -0
  662. raxe/utils/validators.py +170 -0
  663. raxe-0.4.6.dist-info/METADATA +471 -0
  664. raxe-0.4.6.dist-info/RECORD +668 -0
  665. raxe-0.4.6.dist-info/WHEEL +5 -0
  666. raxe-0.4.6.dist-info/entry_points.txt +2 -0
  667. raxe-0.4.6.dist-info/licenses/LICENSE +56 -0
  668. raxe-0.4.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1210 @@
1
+ """
2
+ Telemetry orchestrator for coordinating all telemetry components.
3
+
4
+ This is the main entry point for telemetry operations in the application layer.
5
+ It coordinates between domain-level event creation, session tracking,
6
+ backpressure management, and infrastructure-level persistence/sending.
7
+
8
+ Key Responsibilities:
9
+ - Event creation and queuing
10
+ - Session tracking coordination
11
+ - Backpressure management
12
+ - Flush scheduling
13
+ - Credential/installation management
14
+ - Lazy initialization for minimal startup overhead
15
+
16
+ Privacy Guarantees:
17
+ - No prompts, responses, or matched text transmitted
18
+ - Only SHA-256 hashes for uniqueness tracking
19
+ - Detection metadata only (rule_id, severity, confidence)
20
+ - See CLAUDE.md for full privacy specification
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import atexit
26
+ import logging
27
+ import platform
28
+ import sys
29
+ import threading
30
+ import time
31
+ from datetime import datetime, timezone
32
+ from pathlib import Path
33
+ from typing import TYPE_CHECKING, Any, Literal
34
+
35
+ from raxe.domain.telemetry.backpressure import (
36
+ BackpressureDecision,
37
+ QueueMetrics,
38
+ calculate_backpressure,
39
+ should_sample_event,
40
+ )
41
+ from raxe.domain.telemetry.events import (
42
+ TelemetryEvent,
43
+ create_config_changed_event,
44
+ create_error_event,
45
+ create_feature_usage_event,
46
+ create_installation_event,
47
+ create_prompt_hash,
48
+ create_scan_event,
49
+ generate_installation_id,
50
+ )
51
+ from raxe.infrastructure.config.yaml_config import TelemetryConfig
52
+ from raxe.infrastructure.telemetry.credential_store import CredentialStore
53
+ from raxe.infrastructure.telemetry.credential_store import compute_key_id
54
+ from raxe.infrastructure.telemetry.dual_queue import DualQueue, StateKey
55
+ from raxe.infrastructure.telemetry.flush_scheduler import (
56
+ FlushConfig,
57
+ FlushScheduler,
58
+ HttpShipper,
59
+ SQLiteDualQueueAdapter,
60
+ )
61
+
62
+ from .session_tracker import SessionTracker
63
+
64
+ if TYPE_CHECKING:
65
+ pass
66
+
67
+ logger = logging.getLogger(__name__)
68
+
69
+
70
+ def _get_install_method() -> Literal["pip", "uv", "pipx", "poetry", "conda", "source", "unknown"]:
71
+ """
72
+ Detect the package installation method.
73
+
74
+ Returns:
75
+ Installation method identifier.
76
+ """
77
+ import os
78
+
79
+ # Check for common package manager environment variables
80
+ if os.environ.get("PIPX_HOME"):
81
+ return "pipx"
82
+ if os.environ.get("CONDA_PREFIX"):
83
+ return "conda"
84
+ if os.environ.get("POETRY_ACTIVE"):
85
+ return "poetry"
86
+ if os.environ.get("UV_PROJECT_ENVIRONMENT"):
87
+ return "uv"
88
+
89
+ # Check if running from source (editable install)
90
+ try:
91
+ import raxe
92
+ raxe_path = Path(raxe.__file__).parent
93
+ # If there's a pyproject.toml nearby, likely source install
94
+ if (raxe_path.parent.parent / "pyproject.toml").exists():
95
+ return "source"
96
+ except Exception: # noqa: S110
97
+ pass # Can't determine - fall through to default
98
+
99
+ # Default to pip (most common)
100
+ return "pip"
101
+
102
+
103
+ def _get_platform() -> Literal["darwin", "linux", "win32"]:
104
+ """
105
+ Get the current platform identifier.
106
+
107
+ Returns:
108
+ Platform identifier matching telemetry schema.
109
+ """
110
+ plat = sys.platform
111
+ if plat.startswith("linux"):
112
+ return "linux"
113
+ if plat == "darwin":
114
+ return "darwin"
115
+ if plat in ("win32", "cygwin"):
116
+ return "win32"
117
+ # Default to linux for unknown Unix-like platforms
118
+ return "linux"
119
+
120
+
121
+ def _check_ml_available() -> bool:
122
+ """
123
+ Check if ML dependencies are available.
124
+
125
+ Returns:
126
+ True if ML dependencies (onnxruntime, etc.) are installed.
127
+ """
128
+ try:
129
+ import onnxruntime # noqa: F401
130
+ return True
131
+ except ImportError:
132
+ return False
133
+
134
+
135
+ def _get_installed_extras() -> list[str]:
136
+ """
137
+ Get list of installed optional extras.
138
+
139
+ Returns:
140
+ List of installed extra package groups.
141
+ """
142
+ extras: list[str] = []
143
+
144
+ # Check ML extra
145
+ if _check_ml_available():
146
+ extras.append("ml")
147
+
148
+ # Check OpenAI wrapper
149
+ try:
150
+ import openai # noqa: F401
151
+ extras.append("openai")
152
+ except ImportError:
153
+ pass
154
+
155
+ # Check Anthropic wrapper
156
+ try:
157
+ import anthropic # noqa: F401
158
+ extras.append("anthropic")
159
+ except ImportError:
160
+ pass
161
+
162
+ # Check LangChain integration
163
+ try:
164
+ import langchain # noqa: F401
165
+ extras.append("langchain")
166
+ except ImportError:
167
+ pass
168
+
169
+ return extras
170
+
171
+
172
+ class TelemetryOrchestrator:
173
+ """
174
+ Main orchestrator for telemetry system.
175
+
176
+ Coordinates:
177
+ - Event creation and queuing
178
+ - Session tracking
179
+ - Backpressure management
180
+ - Flush scheduling
181
+ - Credential management
182
+
183
+ The orchestrator uses lazy initialization to minimize startup overhead.
184
+ Database connections and threads are only created when the first event
185
+ is tracked.
186
+
187
+ Example:
188
+ >>> orchestrator = TelemetryOrchestrator()
189
+ >>> orchestrator.start()
190
+ >>> orchestrator.track_scan(
191
+ ... scan_result={"threat_detected": True, ...},
192
+ ... prompt_hash="abc123...",
193
+ ... duration_ms=4.5,
194
+ ... )
195
+ >>> orchestrator.stop(graceful=True)
196
+
197
+ Thread Safety:
198
+ The orchestrator is thread-safe for event tracking operations.
199
+ Start/stop should be called from the main thread.
200
+ """
201
+
202
+ def __init__(
203
+ self,
204
+ config: TelemetryConfig | None = None,
205
+ db_path: Path | None = None,
206
+ ) -> None:
207
+ """
208
+ Initialize the telemetry orchestrator.
209
+
210
+ Components are lazily initialized on first use to minimize
211
+ startup overhead.
212
+
213
+ Args:
214
+ config: Telemetry configuration. If None, uses defaults.
215
+ db_path: Path to SQLite database for queue persistence.
216
+ Defaults to ~/.raxe/telemetry.db.
217
+ """
218
+ self._config = config or TelemetryConfig()
219
+ self._db_path = db_path
220
+
221
+ # Lazy-initialized components
222
+ self._queue: DualQueue | None = None
223
+ self._session_tracker: SessionTracker | None = None
224
+ self._installation_id: str | None = None
225
+
226
+ # Flush scheduler components (lazy-initialized)
227
+ self._queue_adapter: SQLiteDualQueueAdapter | None = None
228
+ self._http_shipper: HttpShipper | None = None
229
+ self._flush_scheduler: FlushScheduler | None = None
230
+
231
+ # State management
232
+ self._initialized = False
233
+ self._started = False
234
+ self._lock = threading.Lock()
235
+ self._shutdown_event = threading.Event()
236
+
237
+ # Statistics
238
+ self._events_queued = 0
239
+ self._events_dropped = 0
240
+ self._start_time: float | None = None
241
+
242
+ logger.debug("TelemetryOrchestrator created (lazy initialization)")
243
+
244
+ def _ensure_initialized(self) -> bool:
245
+ """
246
+ Ensure telemetry components are initialized.
247
+
248
+ This is called lazily on first event tracking. Returns False
249
+ if telemetry is disabled or initialization fails.
250
+
251
+ Returns:
252
+ True if initialized and ready, False otherwise.
253
+ """
254
+ if self._initialized:
255
+ return True
256
+
257
+ if not self._config.enabled:
258
+ logger.debug("Telemetry disabled by configuration")
259
+ return False
260
+
261
+ with self._lock:
262
+ # Double-check after acquiring lock
263
+ if self._initialized:
264
+ return True
265
+
266
+ try:
267
+ # Create queue
268
+ self._queue = DualQueue(db_path=self._db_path)
269
+
270
+ # Get or create installation ID
271
+ self._installation_id = self._ensure_installation_id()
272
+
273
+ # Create session tracker
274
+ self._session_tracker = SessionTracker(
275
+ queue=self._queue,
276
+ installation_id=self._installation_id,
277
+ )
278
+
279
+ self._initialized = True
280
+ logger.debug("TelemetryOrchestrator initialized")
281
+ return True
282
+
283
+ except Exception as e:
284
+ logger.error(f"Failed to initialize telemetry: {e}")
285
+ return False
286
+
287
+ def _ensure_installation_id(self) -> str:
288
+ """
289
+ Ensure installation ID exists, creating if needed.
290
+
291
+ Also fires installation event if this is a new installation.
292
+
293
+ Priority for installation_id:
294
+ 1. SQLite state (for continuity across sessions)
295
+ 2. credentials.json (authoritative source, created by CredentialStore)
296
+ 3. Generate new (last resort)
297
+
298
+ Returns:
299
+ Installation ID string.
300
+ """
301
+ if self._queue is None:
302
+ raise RuntimeError("Queue not initialized")
303
+
304
+ # Priority 1: Check SQLite state for existing installation ID
305
+ existing_id = self._queue.get_state(StateKey.INSTALLATION_ID)
306
+ if existing_id:
307
+ return existing_id
308
+
309
+ # Priority 2: Check credentials.json (authoritative source)
310
+ # Use get_or_create to ensure credentials.json exists with a consistent installation_id
311
+ # before we fire the installation event. This prevents the race condition where
312
+ # we generate an ID here, fire an event, and then get_or_create generates a different ID.
313
+ try:
314
+ credential_store = CredentialStore()
315
+ credentials = credential_store.get_or_create(raise_on_expired=False)
316
+ installation_id = credentials.installation_id
317
+ logger.debug(
318
+ "Using installation_id from credentials: %s",
319
+ installation_id,
320
+ )
321
+ except Exception as e:
322
+ # If credentials can't be loaded, generate new ID
323
+ logger.debug(f"Could not load credentials for installation_id: {e}")
324
+ installation_id = generate_installation_id()
325
+
326
+ # Store installation ID and timestamp in SQLite state
327
+ now = datetime.now(timezone.utc).isoformat()
328
+ self._queue.set_state(StateKey.INSTALLATION_ID, installation_id)
329
+ self._queue.set_state(StateKey.INSTALL_TIMESTAMP, now)
330
+
331
+ # Fire installation event if not already fired
332
+ if not self._queue.has_state(StateKey.INSTALLATION_FIRED):
333
+ self._fire_installation_event(installation_id)
334
+ self._queue.set_state(StateKey.INSTALLATION_FIRED, "true")
335
+
336
+ return installation_id
337
+
338
+ def _fire_installation_event(self, installation_id: str) -> None:
339
+ """
340
+ Fire the installation telemetry event.
341
+
342
+ Args:
343
+ installation_id: Installation ID for this instance.
344
+ """
345
+ from raxe import __version__
346
+
347
+ # Get key_type from credentials (defaults to "temp" for new installations)
348
+ key_type: Literal["temp", "community", "pro", "enterprise"] = "temp"
349
+ try:
350
+ credential_store = CredentialStore()
351
+ credentials = credential_store.load()
352
+ if credentials and credentials.tier:
353
+ # Map tier values to key_type format
354
+ tier = credentials.tier.lower()
355
+ if tier == "temporary":
356
+ key_type = "temp"
357
+ elif tier in ("community", "pro", "enterprise"):
358
+ key_type = tier # type: ignore[assignment]
359
+ except Exception as e:
360
+ logger.debug(f"Could not load credentials for key_type: {e}")
361
+
362
+ event = create_installation_event(
363
+ installation_id=installation_id,
364
+ client_version=__version__,
365
+ python_version=platform.python_version(),
366
+ platform=_get_platform(),
367
+ install_method=_get_install_method(),
368
+ key_type=key_type,
369
+ ml_available=_check_ml_available(),
370
+ installed_extras=_get_installed_extras() or None,
371
+ platform_version=platform.release(),
372
+ )
373
+
374
+ if self._queue:
375
+ self._queue.enqueue(event)
376
+ logger.info(f"Installation event fired: {installation_id} (key_type={key_type})")
377
+
378
+ # =========================================================================
379
+ # Lifecycle Methods
380
+ # =========================================================================
381
+
382
+ def start(self) -> None:
383
+ """
384
+ Initialize and start telemetry system.
385
+
386
+ This triggers lazy initialization, starts the session, and
387
+ begins automatic background flushing (5s for critical, 5m for standard).
388
+ Should be called once at application startup.
389
+ """
390
+ if self._started:
391
+ logger.debug("TelemetryOrchestrator already started")
392
+ return
393
+
394
+ if not self._ensure_initialized():
395
+ return
396
+
397
+ self._started = True
398
+ self._start_time = time.monotonic()
399
+
400
+ # Register atexit handler
401
+ atexit.register(self._atexit_handler)
402
+
403
+ # Start session (if tracker is available)
404
+ if self._session_tracker:
405
+ try:
406
+ self._session_tracker.start_session(entry_point="sdk")
407
+ except RuntimeError:
408
+ pass # Session already started
409
+
410
+ # Initialize and start flush scheduler for automatic background sending
411
+ self._start_flush_scheduler()
412
+
413
+ logger.info("TelemetryOrchestrator started")
414
+
415
+ def _start_flush_scheduler(self) -> None:
416
+ """Initialize and start the background flush scheduler.
417
+
418
+ This sets up automatic background flushing:
419
+ - Critical events: every 5 seconds
420
+ - Standard events: every 5 minutes (300 seconds)
421
+ """
422
+ if self._flush_scheduler is not None:
423
+ logger.debug("Flush scheduler already running")
424
+ return
425
+
426
+ if self._queue is None:
427
+ logger.warning("Queue not initialized, skipping flush scheduler")
428
+ return
429
+
430
+ try:
431
+ # Get API credentials with proper priority chain
432
+ api_key, installation_id = self._get_api_credentials()
433
+
434
+ # Get telemetry endpoint
435
+ endpoint = self._config.endpoint
436
+ if not endpoint:
437
+ from raxe.infrastructure.config.endpoints import get_telemetry_endpoint
438
+ endpoint = get_telemetry_endpoint()
439
+
440
+ # Skip flush scheduler if no API key or endpoint
441
+ if not api_key or not endpoint:
442
+ logger.warning(
443
+ "No API key or endpoint available, flush scheduler disabled",
444
+ has_api_key=bool(api_key),
445
+ has_endpoint=bool(endpoint),
446
+ )
447
+ return
448
+
449
+ # Create queue adapter
450
+ self._queue_adapter = SQLiteDualQueueAdapter(self._queue)
451
+
452
+ # Create HTTP shipper
453
+ # NOTE: Don't pass api_key_id - let the sender compute it from api_key.
454
+ # This ensures events are tagged with the CURRENT key's ID, not a stale
455
+ # ID from queue state. The backend uses the authenticated key's ID for
456
+ # event correlation (key_info.key_id).
457
+ self._http_shipper = HttpShipper(
458
+ endpoint=endpoint,
459
+ api_key=api_key,
460
+ installation_id=installation_id or self._installation_id,
461
+ queue_adapter=self._queue_adapter,
462
+ )
463
+
464
+ # Create flush config (production intervals)
465
+ flush_config = FlushConfig.for_production()
466
+
467
+ # Create and start flush scheduler
468
+ self._flush_scheduler = FlushScheduler(
469
+ queue=self._queue_adapter,
470
+ shipper=self._http_shipper,
471
+ config=flush_config,
472
+ )
473
+ self._flush_scheduler.start()
474
+
475
+ logger.info(
476
+ "Flush scheduler started",
477
+ critical_interval=flush_config.critical_interval_seconds,
478
+ standard_interval=flush_config.standard_interval_seconds,
479
+ endpoint=endpoint[:50] + "..." if endpoint and len(endpoint) > 50 else endpoint,
480
+ )
481
+
482
+ except Exception as e:
483
+ logger.error(f"Failed to start flush scheduler: {e}")
484
+ # Continue without automatic flushing - events still queue
485
+
486
+ def _get_api_credentials(self) -> tuple[str | None, str | None]:
487
+ """Get API credentials with proper priority chain.
488
+
489
+ Priority:
490
+ 1. RAXE_API_KEY environment variable
491
+ 2. credentials.json file
492
+ 3. config.yaml core.api_key
493
+
494
+ Also stores the computed api_key_id in queue state for consistency
495
+ with auth flow (which reads from telemetry state).
496
+
497
+ Returns:
498
+ Tuple of (api_key, installation_id)
499
+ """
500
+ import os
501
+ api_key: str | None = None
502
+ installation_id: str | None = None
503
+
504
+ # Always get installation_id from credentials (machine-specific)
505
+ try:
506
+ credential_store = CredentialStore()
507
+ credentials = credential_store.load()
508
+ if credentials:
509
+ installation_id = credentials.installation_id
510
+ except Exception as e:
511
+ logger.debug(f"Could not load credentials for installation_id: {e}")
512
+
513
+ # Fall back to queue-stored installation_id
514
+ if not installation_id and self._installation_id:
515
+ installation_id = self._installation_id
516
+
517
+ # Priority 1: Environment variable
518
+ env_api_key = os.environ.get("RAXE_API_KEY", "").strip()
519
+ if env_api_key:
520
+ api_key = env_api_key
521
+ logger.debug("Using API key from RAXE_API_KEY environment variable")
522
+ self._store_api_key_id(api_key)
523
+ return api_key, installation_id
524
+
525
+ # Priority 2: credentials.json
526
+ try:
527
+ credential_store = CredentialStore()
528
+ credentials = credential_store.load()
529
+ if credentials and credentials.api_key:
530
+ api_key = credentials.api_key
531
+ logger.debug("Using API key from credentials.json")
532
+ self._store_api_key_id(api_key)
533
+ return api_key, installation_id
534
+ except Exception as e:
535
+ logger.debug(f"Could not load API key from credentials: {e}")
536
+
537
+ # Priority 3: config.yaml
538
+ try:
539
+ from raxe.infrastructure.config.yaml_config import RaxeConfig
540
+ config = RaxeConfig.load()
541
+ if config.core.api_key:
542
+ api_key = config.core.api_key
543
+ logger.debug("Using API key from config.yaml")
544
+ self._store_api_key_id(api_key)
545
+ return api_key, installation_id
546
+ except Exception as e:
547
+ logger.debug(f"Could not load API key from config: {e}")
548
+
549
+ return api_key, installation_id
550
+
551
+ def _store_api_key_id(self, api_key: str) -> None:
552
+ """Store computed api_key_id in queue state for auth flow consistency.
553
+
554
+ The auth flow reads this value to get the current key's ID for linking
555
+ events to a user account.
556
+
557
+ NOTE: This method ALWAYS updates the stored api_key_id to match the current
558
+ api_key. This ensures:
559
+ 1. Auth flow gets the correct temp_key_id BEFORE upgrade (for event linking)
560
+ 2. Event sending uses the correct key_id AFTER upgrade (for BigQuery correlation)
561
+
562
+ The backend uses the authenticated key's ID (key_info.key_id) for event
563
+ storage, so we must always send events with the CURRENT key's ID.
564
+
565
+ Args:
566
+ api_key: The API key to compute the ID from.
567
+ """
568
+ if self._queue is None:
569
+ return
570
+
571
+ try:
572
+ api_key_id = compute_key_id(api_key)
573
+ self._queue.set_state(StateKey.CURRENT_API_KEY_ID, api_key_id)
574
+ logger.debug(f"Stored api_key_id in telemetry state: {api_key_id}")
575
+ except Exception as e:
576
+ logger.debug(f"Could not store api_key_id in telemetry state: {e}")
577
+
578
+ def get_current_api_key_id(self) -> str | None:
579
+ """Get the current api_key_id from telemetry state.
580
+
581
+ This method is used by the auth flow to ensure consistency between
582
+ the temp_key_id sent during authentication and what telemetry events
583
+ are using.
584
+
585
+ Returns:
586
+ The api_key_id if stored, None otherwise.
587
+ """
588
+ if self._queue is None:
589
+ # Try to initialize if not already done
590
+ if not self._ensure_initialized():
591
+ return None
592
+ if self._queue is None:
593
+ return None
594
+
595
+ return self._queue.get_state(StateKey.CURRENT_API_KEY_ID)
596
+
597
+ def stop(self, graceful: bool = True) -> None:
598
+ """
599
+ Stop telemetry system with optional graceful flush.
600
+
601
+ Args:
602
+ graceful: If True, attempt to flush queued events before stopping.
603
+ """
604
+ if not self._started:
605
+ return
606
+
607
+ logger.info("TelemetryOrchestrator stopping...")
608
+
609
+ # Signal shutdown
610
+ self._shutdown_event.set()
611
+
612
+ # End session
613
+ if self._session_tracker and self._session_tracker.is_session_active:
614
+ try:
615
+ self._session_tracker.end_session(end_reason="normal")
616
+ except Exception as e:
617
+ logger.debug(f"Error ending session: {e}")
618
+
619
+ # Stop flush scheduler (will perform graceful flush if configured)
620
+ if self._flush_scheduler:
621
+ try:
622
+ self._flush_scheduler.stop(graceful=graceful)
623
+ logger.debug("Flush scheduler stopped")
624
+ except Exception as e:
625
+ logger.warning(f"Error stopping flush scheduler: {e}")
626
+ self._flush_scheduler = None
627
+ self._http_shipper = None
628
+ self._queue_adapter = None
629
+
630
+ # Close queue
631
+ if self._queue:
632
+ self._queue.close()
633
+
634
+ self._started = False
635
+ logger.info("TelemetryOrchestrator stopped")
636
+
637
+ def is_enabled(self) -> bool:
638
+ """
639
+ Check if telemetry is enabled.
640
+
641
+ Returns:
642
+ True if telemetry is enabled and initialized.
643
+ """
644
+ return self._config.enabled and self._initialized
645
+
646
+ # =========================================================================
647
+ # Event Tracking Methods
648
+ # =========================================================================
649
+
650
+ def track_scan(
651
+ self,
652
+ scan_result: dict[str, Any],
653
+ prompt_hash: str,
654
+ duration_ms: float,
655
+ entry_point: Literal["cli", "sdk", "wrapper", "integration"] = "sdk",
656
+ *,
657
+ event_id: str | None = None,
658
+ wrapper_type: Literal["openai", "anthropic", "langchain", "none"] | None = None,
659
+ l1_duration_ms: float | None = None,
660
+ l2_duration_ms: float | None = None,
661
+ ) -> None:
662
+ """
663
+ Track a scan event. Main entry point for scan telemetry.
664
+
665
+ This method creates a privacy-preserving scan event and queues it
666
+ for transmission. Backpressure is applied if queues are filling up.
667
+
668
+ Args:
669
+ scan_result: Scan result dictionary with detection information.
670
+ prompt_hash: SHA-256 hash of the scanned prompt.
671
+ duration_ms: Total scan duration in milliseconds.
672
+ entry_point: How the scan was triggered.
673
+ event_id: Optional event ID. If not provided, one will be generated.
674
+ wrapper_type: Wrapper used if applicable.
675
+ l1_duration_ms: L1 (rule-based) scan duration.
676
+ l2_duration_ms: L2 (ML-based) scan duration.
677
+
678
+ Privacy:
679
+ - prompt_hash is a SHA-256 hash (not reversible)
680
+ - No actual prompt text is transmitted
681
+ - Only detection metadata (rule_id, severity, confidence)
682
+ """
683
+ if not self._ensure_initialized():
684
+ return
685
+
686
+ if self._queue is None:
687
+ return
688
+
689
+ # Extract scan metrics from result
690
+ threat_detected = scan_result.get("threat_detected", False)
691
+ detection_count = scan_result.get("detection_count", 0)
692
+ highest_severity = scan_result.get("highest_severity")
693
+ rule_ids = scan_result.get("rule_ids", [])
694
+ families = scan_result.get("families", [])
695
+ l1_hit = scan_result.get("l1_hit")
696
+ l2_hit = scan_result.get("l2_hit")
697
+ l2_enabled = scan_result.get("l2_enabled")
698
+ prompt_length = scan_result.get("prompt_length")
699
+ action_taken = scan_result.get("action_taken")
700
+
701
+ # Ensure prompt_hash has sha256: prefix (schema requires ^sha256:[a-f0-9]{64}$)
702
+ if prompt_hash and not prompt_hash.startswith("sha256:"):
703
+ prompt_hash = f"sha256:{prompt_hash}"
704
+
705
+ # Create scan event
706
+ event = create_scan_event(
707
+ prompt_hash=prompt_hash,
708
+ threat_detected=threat_detected,
709
+ scan_duration_ms=duration_ms,
710
+ event_id=event_id,
711
+ detection_count=detection_count if detection_count else None,
712
+ highest_severity=highest_severity,
713
+ rule_ids=rule_ids if rule_ids else None,
714
+ families=families if families else None,
715
+ l1_duration_ms=l1_duration_ms,
716
+ l2_duration_ms=l2_duration_ms,
717
+ l1_hit=l1_hit,
718
+ l2_hit=l2_hit,
719
+ l2_enabled=l2_enabled,
720
+ prompt_length=prompt_length,
721
+ action_taken=action_taken,
722
+ entry_point=entry_point,
723
+ wrapper_type=wrapper_type,
724
+ )
725
+
726
+ # Apply backpressure
727
+ if not self._should_queue_event(event):
728
+ self._events_dropped += 1
729
+ return
730
+
731
+ # Enqueue event
732
+ self._queue.enqueue(event)
733
+ self._events_queued += 1
734
+
735
+ # Update session tracker
736
+ if self._session_tracker:
737
+ self._session_tracker.record_scan()
738
+ if threat_detected:
739
+ self._session_tracker.record_threat()
740
+ # Track first threat activation
741
+ self._session_tracker.track_activation("first_threat")
742
+
743
+ # Track first scan activation
744
+ self._session_tracker.track_activation(
745
+ "first_scan",
746
+ activation_context={"entry_point": entry_point},
747
+ )
748
+
749
+ logger.debug(
750
+ f"Scan event tracked: threat={threat_detected}, "
751
+ f"severity={highest_severity}, duration={duration_ms:.1f}ms"
752
+ )
753
+
754
+ def track_scan_v2(
755
+ self,
756
+ payload: dict[str, Any],
757
+ *,
758
+ event_id: str | None = None,
759
+ org_id: str | None = None,
760
+ team_id: str | None = None,
761
+ ) -> None:
762
+ """Track a scan event using schema v2.0 with full L2 telemetry.
763
+
764
+ This method accepts a pre-built payload from ScanTelemetryBuilder.
765
+ Use this for full L2 telemetry capture as defined in
766
+ docs/SCAN_TELEMETRY_SCHEMA.md.
767
+
768
+ Args:
769
+ payload: Pre-built payload dict from ScanTelemetryBuilder.build()
770
+ event_id: Optional event ID. If not provided, one will be generated.
771
+ org_id: Organization ID for multi-tenant tracking.
772
+ team_id: Team ID for team-level analytics.
773
+
774
+ Privacy:
775
+ - All fields in payload are dynamically calculated from scan results
776
+ - No actual prompt content is transmitted
777
+ - Only hashes, metrics, and enum values
778
+ """
779
+ if not self._ensure_initialized():
780
+ return
781
+
782
+ if self._queue is None:
783
+ return
784
+
785
+ # Import v2 event creator
786
+ from raxe.domain.telemetry.events import create_scan_event_v2
787
+
788
+ # Create scan event with v2 schema
789
+ event = create_scan_event_v2(
790
+ payload=payload,
791
+ event_id=event_id,
792
+ org_id=org_id,
793
+ team_id=team_id,
794
+ )
795
+
796
+ # Apply backpressure
797
+ if not self._should_queue_event(event):
798
+ self._events_dropped += 1
799
+ return
800
+
801
+ # Enqueue event
802
+ self._queue.enqueue(event)
803
+ self._events_queued += 1
804
+
805
+ # Update session tracker
806
+ threat_detected = payload.get("threat_detected", False)
807
+ if self._session_tracker:
808
+ self._session_tracker.record_scan()
809
+ if threat_detected:
810
+ self._session_tracker.record_threat()
811
+ # Track first threat activation
812
+ self._session_tracker.track_activation("first_threat")
813
+
814
+ # Track first scan activation
815
+ entry_point = payload.get("entry_point", "sdk")
816
+ self._session_tracker.track_activation(
817
+ "first_scan",
818
+ activation_context={"entry_point": entry_point},
819
+ )
820
+
821
+ # Log with v2 schema info
822
+ l2_block = payload.get("l2", {})
823
+ l2_classification = l2_block.get("classification", "N/A")
824
+ logger.debug(
825
+ f"Scan event tracked (v2): threat={threat_detected}, "
826
+ f"l2_classification={l2_classification}"
827
+ )
828
+
829
+ def track_error(
830
+ self,
831
+ error_type: Literal[
832
+ "validation_error",
833
+ "configuration_error",
834
+ "rule_loading_error",
835
+ "ml_model_error",
836
+ "network_error",
837
+ "permission_error",
838
+ "timeout_error",
839
+ "internal_error",
840
+ ],
841
+ error_code: str,
842
+ component: Literal["cli", "sdk", "engine", "ml", "rules", "config", "telemetry", "wrapper"],
843
+ error_message: str,
844
+ is_recoverable: bool = True,
845
+ *,
846
+ operation: str | None = None,
847
+ stack_trace: str | None = None,
848
+ ) -> None:
849
+ """
850
+ Track an error event.
851
+
852
+ Error events are critical priority and help improve RAXE reliability.
853
+
854
+ Args:
855
+ error_type: Category of error.
856
+ error_code: Specific error code (e.g., RAXE_001).
857
+ component: Component where error occurred.
858
+ error_message: Error message (will be hashed for privacy).
859
+ is_recoverable: Whether the error was recovered from.
860
+ operation: Operation being performed when error occurred.
861
+ stack_trace: Stack trace (will be hashed for privacy).
862
+
863
+ Privacy:
864
+ - error_message is hashed (SHA-256)
865
+ - stack_trace is hashed (SHA-256)
866
+ - No raw error content transmitted
867
+ """
868
+ if not self._ensure_initialized():
869
+ return
870
+
871
+ if self._queue is None:
872
+ return
873
+
874
+ # Hash error message and stack trace for privacy
875
+ error_message_hash = create_prompt_hash(error_message)
876
+ stack_trace_hash = create_prompt_hash(stack_trace) if stack_trace else None
877
+
878
+ event = create_error_event(
879
+ error_type=error_type,
880
+ error_code=error_code,
881
+ component=component,
882
+ error_message_hash=error_message_hash,
883
+ operation=operation,
884
+ is_recoverable=is_recoverable,
885
+ stack_trace_hash=stack_trace_hash,
886
+ )
887
+
888
+ # Errors are always critical - no backpressure
889
+ self._queue.enqueue(event)
890
+ self._events_queued += 1
891
+
892
+ logger.debug(f"Error event tracked: {error_type}/{error_code} in {component}")
893
+
894
+ def track_feature_usage(
895
+ self,
896
+ feature: Literal[
897
+ "cli_scan",
898
+ "cli_rules_list",
899
+ "cli_rules_show",
900
+ "cli_config",
901
+ "cli_stats",
902
+ "cli_repl",
903
+ "cli_explain",
904
+ "cli_validate_rule",
905
+ "cli_doctor",
906
+ "cli_telemetry_dlq",
907
+ "sdk_scan",
908
+ "sdk_batch_scan",
909
+ "sdk_layer_control",
910
+ "wrapper_openai",
911
+ "wrapper_anthropic",
912
+ "integration_langchain",
913
+ "custom_rule_loaded",
914
+ "policy_applied",
915
+ ],
916
+ action: Literal["invoked", "completed", "failed", "cancelled"] = "invoked",
917
+ duration_ms: float | None = None,
918
+ success: bool = True,
919
+ ) -> None:
920
+ """
921
+ Track feature usage for analytics.
922
+
923
+ Args:
924
+ feature: Feature being used.
925
+ action: Action taken with feature.
926
+ duration_ms: Time spent using feature.
927
+ success: Whether feature usage was successful.
928
+ """
929
+ if not self._ensure_initialized():
930
+ return
931
+
932
+ if self._queue is None:
933
+ return
934
+
935
+ event = create_feature_usage_event(
936
+ feature=feature,
937
+ action=action,
938
+ duration_ms=duration_ms,
939
+ success=success,
940
+ )
941
+
942
+ # Apply backpressure (standard priority)
943
+ if not self._should_queue_event(event):
944
+ self._events_dropped += 1
945
+ return
946
+
947
+ self._queue.enqueue(event)
948
+ self._events_queued += 1
949
+
950
+ # Update session tracker
951
+ if self._session_tracker:
952
+ self._session_tracker.record_feature_used(feature)
953
+
954
+ # Track activation events for first-time feature usage
955
+ if feature.startswith("cli_"):
956
+ self._session_tracker.track_activation("first_cli")
957
+ elif feature.startswith("sdk_"):
958
+ self._session_tracker.track_activation("first_sdk")
959
+ elif feature.startswith("wrapper_"):
960
+ self._session_tracker.track_activation("first_wrapper")
961
+ elif feature == "integration_langchain":
962
+ self._session_tracker.track_activation("first_langchain")
963
+ elif feature == "custom_rule_loaded":
964
+ self._session_tracker.track_activation("first_custom_rule")
965
+
966
+ logger.debug(f"Feature usage tracked: {feature}/{action}")
967
+
968
+ def track_config_change(
969
+ self,
970
+ key: str,
971
+ old_value: Any,
972
+ new_value: Any,
973
+ changed_via: Literal["cli", "sdk", "config_file", "env_var"] = "sdk",
974
+ ) -> None:
975
+ """
976
+ Track configuration change.
977
+
978
+ Args:
979
+ key: Configuration key that changed (e.g., "telemetry.enabled").
980
+ old_value: Previous value.
981
+ new_value: New value.
982
+ changed_via: How configuration was changed.
983
+ """
984
+ if not self._ensure_initialized():
985
+ return
986
+
987
+ if self._queue is None:
988
+ return
989
+
990
+ # Check if this is a telemetry disable (final event)
991
+ is_final_event = key == "telemetry.enabled" and new_value is False
992
+
993
+ event = create_config_changed_event(
994
+ changed_via=changed_via,
995
+ changes=[{"key": key, "old_value": old_value, "new_value": new_value}],
996
+ is_final_event=is_final_event,
997
+ )
998
+
999
+ # Config changes bypass backpressure (they're infrequent)
1000
+ self._queue.enqueue(event)
1001
+ self._events_queued += 1
1002
+
1003
+ logger.debug(f"Config change tracked: {key} = {new_value}")
1004
+
1005
+ # =========================================================================
1006
+ # Manual Operations
1007
+ # =========================================================================
1008
+
1009
+ def flush(self) -> int:
1010
+ """
1011
+ Force flush all queues. Returns events shipped.
1012
+
1013
+ This is a synchronous operation that attempts to send all
1014
+ queued events immediately via the flush scheduler.
1015
+
1016
+ Returns:
1017
+ Number of events processed (not necessarily successfully sent).
1018
+ """
1019
+ if not self._initialized or self._queue is None:
1020
+ return 0
1021
+
1022
+ # Get counts before flush
1023
+ stats = self._queue.get_stats()
1024
+ total_queued: int = stats.get("total_queued", 0)
1025
+
1026
+ # If flush scheduler is available, use it for actual sending
1027
+ if self._flush_scheduler:
1028
+ try:
1029
+ events_sent = self._flush_scheduler.flush_all()
1030
+ logger.info(f"Flush completed: {events_sent} events shipped")
1031
+ return events_sent
1032
+ except Exception as e:
1033
+ logger.warning(f"Error during flush: {e}")
1034
+
1035
+ logger.info(f"Flush requested: {total_queued} events in queue (no scheduler)")
1036
+ return total_queued
1037
+
1038
+ def get_stats(self) -> dict[str, Any]:
1039
+ """
1040
+ Get comprehensive telemetry statistics.
1041
+
1042
+ Returns:
1043
+ Dictionary with telemetry metrics including queue stats,
1044
+ event counts, flush scheduler stats, and health indicators.
1045
+ """
1046
+ stats: dict[str, Any] = {
1047
+ "enabled": self._config.enabled,
1048
+ "initialized": self._initialized,
1049
+ "started": self._started,
1050
+ }
1051
+
1052
+ if not self._initialized:
1053
+ return stats
1054
+
1055
+ # Add queue stats
1056
+ if self._queue:
1057
+ queue_stats = self._queue.get_stats()
1058
+ stats["queue"] = queue_stats
1059
+
1060
+ # Add orchestrator stats
1061
+ stats["events_queued"] = self._events_queued
1062
+ stats["events_dropped"] = self._events_dropped
1063
+
1064
+ # Add uptime
1065
+ if self._start_time:
1066
+ stats["uptime_seconds"] = time.monotonic() - self._start_time
1067
+
1068
+ # Add installation info
1069
+ if self._installation_id:
1070
+ stats["installation_id"] = self._installation_id
1071
+
1072
+ # Add session info
1073
+ if self._session_tracker:
1074
+ stats["session_id"] = self._session_tracker.session_id
1075
+ stats["session_number"] = self._session_tracker.session_number
1076
+ stats["session_active"] = self._session_tracker.is_session_active
1077
+
1078
+ # Add flush scheduler stats
1079
+ if self._flush_scheduler:
1080
+ stats["flush_scheduler"] = self._flush_scheduler.get_stats()
1081
+
1082
+ # Add HTTP shipper stats
1083
+ if self._http_shipper:
1084
+ stats["http_shipper"] = self._http_shipper.get_stats()
1085
+
1086
+ return stats
1087
+
1088
+ def ensure_installation(self) -> str:
1089
+ """
1090
+ Ensure installation event fired, return installation_id.
1091
+
1092
+ This can be called to eagerly initialize telemetry and get
1093
+ the installation ID without tracking any events.
1094
+
1095
+ Returns:
1096
+ Installation ID string.
1097
+ """
1098
+ if not self._ensure_initialized():
1099
+ return ""
1100
+
1101
+ return self._installation_id or ""
1102
+
1103
+ # =========================================================================
1104
+ # Internal Methods
1105
+ # =========================================================================
1106
+
1107
+ def _should_queue_event(self, event: TelemetryEvent) -> bool:
1108
+ """
1109
+ Check if an event should be queued based on backpressure.
1110
+
1111
+ Args:
1112
+ event: Event to check.
1113
+
1114
+ Returns:
1115
+ True if event should be queued, False if dropped.
1116
+ """
1117
+ if self._queue is None:
1118
+ return False
1119
+
1120
+ # Get current queue metrics
1121
+ stats = self._queue.get_stats()
1122
+ metrics = QueueMetrics(
1123
+ critical_queue_size=stats.get("critical_count", 0),
1124
+ standard_queue_size=stats.get("standard_count", 0),
1125
+ critical_queue_max=self._queue.critical_max_size,
1126
+ standard_queue_max=self._queue.standard_max_size,
1127
+ dlq_size=stats.get("dlq_count", 0),
1128
+ )
1129
+
1130
+ # Calculate backpressure decision
1131
+ is_critical = event.priority == "critical"
1132
+ decision: BackpressureDecision = calculate_backpressure(metrics, is_critical)
1133
+
1134
+ # Critical events are never dropped
1135
+ if is_critical:
1136
+ return True
1137
+
1138
+ # If decision says don't queue, drop it
1139
+ if not decision.should_queue:
1140
+ logger.debug(f"Event dropped due to backpressure: {decision.reason}")
1141
+ return False
1142
+
1143
+ # Apply sampling if needed
1144
+ if decision.sample_rate < 1.0:
1145
+ if not should_sample_event(decision.sample_rate, event.event_id):
1146
+ logger.debug(
1147
+ f"Event sampled out at rate {decision.sample_rate}: {event.event_id}"
1148
+ )
1149
+ return False
1150
+
1151
+ return True
1152
+
1153
+ def _atexit_handler(self) -> None:
1154
+ """
1155
+ Handle process exit for graceful shutdown.
1156
+
1157
+ This is registered with atexit to ensure telemetry is properly
1158
+ stopped even if the user doesn't explicitly call stop().
1159
+ """
1160
+ if self._started:
1161
+ try:
1162
+ self.stop(graceful=True)
1163
+ except Exception as e:
1164
+ logger.debug(f"Error during atexit shutdown: {e}")
1165
+
1166
+
1167
+ # =============================================================================
1168
+ # Module-level singleton (optional convenience)
1169
+ # =============================================================================
1170
+
1171
+ _default_orchestrator: TelemetryOrchestrator | None = None
1172
+ _orchestrator_lock = threading.Lock()
1173
+
1174
+
1175
+ def get_orchestrator() -> TelemetryOrchestrator:
1176
+ """
1177
+ Get the default telemetry orchestrator (singleton).
1178
+
1179
+ This provides a convenient way to access telemetry from anywhere
1180
+ in the application without passing the orchestrator explicitly.
1181
+
1182
+ Returns:
1183
+ Default TelemetryOrchestrator instance.
1184
+
1185
+ Example:
1186
+ >>> orchestrator = get_orchestrator()
1187
+ >>> orchestrator.track_scan(...)
1188
+ """
1189
+ global _default_orchestrator
1190
+
1191
+ if _default_orchestrator is None:
1192
+ with _orchestrator_lock:
1193
+ if _default_orchestrator is None:
1194
+ _default_orchestrator = TelemetryOrchestrator()
1195
+
1196
+ return _default_orchestrator
1197
+
1198
+
1199
+ def reset_orchestrator() -> None:
1200
+ """
1201
+ Reset the default orchestrator (for testing).
1202
+
1203
+ This should only be used in tests to reset state between test cases.
1204
+ """
1205
+ global _default_orchestrator
1206
+
1207
+ with _orchestrator_lock:
1208
+ if _default_orchestrator is not None:
1209
+ _default_orchestrator.stop(graceful=False)
1210
+ _default_orchestrator = None