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.
- raxe/__init__.py +101 -0
- raxe/application/__init__.py +48 -0
- raxe/application/ab_testing.py +170 -0
- raxe/application/analytics/__init__.py +30 -0
- raxe/application/analytics/achievement_service.py +444 -0
- raxe/application/analytics/repositories.py +172 -0
- raxe/application/analytics/retention_service.py +267 -0
- raxe/application/analytics/statistics_service.py +419 -0
- raxe/application/analytics/streak_service.py +283 -0
- raxe/application/apply_policy.py +291 -0
- raxe/application/eager_l2.py +503 -0
- raxe/application/preloader.py +353 -0
- raxe/application/scan_merger.py +321 -0
- raxe/application/scan_pipeline.py +1059 -0
- raxe/application/scan_pipeline_async.py +403 -0
- raxe/application/session_tracker.py +458 -0
- raxe/application/telemetry_manager.py +357 -0
- raxe/application/telemetry_orchestrator.py +1210 -0
- raxe/async_sdk/__init__.py +34 -0
- raxe/async_sdk/cache.py +286 -0
- raxe/async_sdk/client.py +556 -0
- raxe/async_sdk/wrappers/__init__.py +23 -0
- raxe/async_sdk/wrappers/openai.py +238 -0
- raxe/cli/__init__.py +21 -0
- raxe/cli/auth.py +1047 -0
- raxe/cli/branding.py +235 -0
- raxe/cli/config.py +334 -0
- raxe/cli/custom_rules.py +458 -0
- raxe/cli/doctor.py +686 -0
- raxe/cli/error_handler.py +665 -0
- raxe/cli/event.py +648 -0
- raxe/cli/exit_codes.py +57 -0
- raxe/cli/expiry_warning.py +302 -0
- raxe/cli/export.py +183 -0
- raxe/cli/history.py +247 -0
- raxe/cli/l2_formatter.py +872 -0
- raxe/cli/main.py +1137 -0
- raxe/cli/models.py +590 -0
- raxe/cli/output.py +403 -0
- raxe/cli/privacy.py +84 -0
- raxe/cli/profiler.py +262 -0
- raxe/cli/progress.py +379 -0
- raxe/cli/progress_context.py +101 -0
- raxe/cli/repl.py +394 -0
- raxe/cli/rules.py +542 -0
- raxe/cli/setup_wizard.py +721 -0
- raxe/cli/stats.py +292 -0
- raxe/cli/suppress.py +501 -0
- raxe/cli/telemetry.py +1384 -0
- raxe/cli/test.py +130 -0
- raxe/cli/tune.py +315 -0
- raxe/cli/validate.py +218 -0
- raxe/domain/__init__.py +30 -0
- raxe/domain/analytics/__init__.py +97 -0
- raxe/domain/analytics/achievements.py +306 -0
- raxe/domain/analytics/models.py +120 -0
- raxe/domain/analytics/retention.py +168 -0
- raxe/domain/analytics/statistics.py +207 -0
- raxe/domain/analytics/streaks.py +173 -0
- raxe/domain/engine/__init__.py +15 -0
- raxe/domain/engine/executor.py +396 -0
- raxe/domain/engine/matcher.py +212 -0
- raxe/domain/inline_suppression.py +176 -0
- raxe/domain/ml/__init__.py +133 -0
- raxe/domain/ml/embedding_cache.py +309 -0
- raxe/domain/ml/gemma_detector.py +921 -0
- raxe/domain/ml/gemma_models.py +346 -0
- raxe/domain/ml/l2_config.py +428 -0
- raxe/domain/ml/l2_output_schema.py +443 -0
- raxe/domain/ml/manifest_loader.py +309 -0
- raxe/domain/ml/manifest_schema.py +345 -0
- raxe/domain/ml/model_metadata.py +263 -0
- raxe/domain/ml/model_registry.py +786 -0
- raxe/domain/ml/protocol.py +282 -0
- raxe/domain/ml/scoring_models.py +419 -0
- raxe/domain/ml/stub_detector.py +397 -0
- raxe/domain/ml/threat_scorer.py +757 -0
- raxe/domain/ml/tokenizer_registry.py +372 -0
- raxe/domain/ml/voting/__init__.py +89 -0
- raxe/domain/ml/voting/config.py +595 -0
- raxe/domain/ml/voting/engine.py +465 -0
- raxe/domain/ml/voting/head_voters.py +378 -0
- raxe/domain/ml/voting/models.py +222 -0
- raxe/domain/models.py +82 -0
- raxe/domain/packs/__init__.py +17 -0
- raxe/domain/packs/models.py +304 -0
- raxe/domain/policies/__init__.py +20 -0
- raxe/domain/policies/evaluator.py +212 -0
- raxe/domain/policies/models.py +223 -0
- raxe/domain/rules/__init__.py +32 -0
- raxe/domain/rules/custom.py +286 -0
- raxe/domain/rules/models.py +273 -0
- raxe/domain/rules/schema.py +166 -0
- raxe/domain/rules/validator.py +556 -0
- raxe/domain/suppression.py +801 -0
- raxe/domain/suppression_factory.py +174 -0
- raxe/domain/telemetry/__init__.py +116 -0
- raxe/domain/telemetry/backpressure.py +424 -0
- raxe/domain/telemetry/event_creator.py +362 -0
- raxe/domain/telemetry/events.py +1282 -0
- raxe/domain/telemetry/priority.py +263 -0
- raxe/domain/telemetry/scan_telemetry_builder.py +670 -0
- raxe/infrastructure/__init__.py +25 -0
- raxe/infrastructure/analytics/__init__.py +18 -0
- raxe/infrastructure/analytics/aggregator.py +484 -0
- raxe/infrastructure/analytics/aggregator_optimized.py +184 -0
- raxe/infrastructure/analytics/engine.py +748 -0
- raxe/infrastructure/analytics/repository.py +409 -0
- raxe/infrastructure/analytics/streaks.py +467 -0
- raxe/infrastructure/analytics/views.py +178 -0
- raxe/infrastructure/cloud/__init__.py +9 -0
- raxe/infrastructure/config/__init__.py +56 -0
- raxe/infrastructure/config/endpoints.py +641 -0
- raxe/infrastructure/config/scan_config.py +352 -0
- raxe/infrastructure/config/yaml_config.py +459 -0
- raxe/infrastructure/database/__init__.py +10 -0
- raxe/infrastructure/database/connection.py +200 -0
- raxe/infrastructure/database/models.py +325 -0
- raxe/infrastructure/database/scan_history.py +764 -0
- raxe/infrastructure/ml/__init__.py +0 -0
- raxe/infrastructure/ml/download_progress.py +438 -0
- raxe/infrastructure/ml/model_downloader.py +457 -0
- raxe/infrastructure/models/__init__.py +16 -0
- raxe/infrastructure/models/discovery.py +461 -0
- raxe/infrastructure/packs/__init__.py +13 -0
- raxe/infrastructure/packs/loader.py +407 -0
- raxe/infrastructure/packs/registry.py +381 -0
- raxe/infrastructure/policies/__init__.py +16 -0
- raxe/infrastructure/policies/api_client.py +256 -0
- raxe/infrastructure/policies/validator.py +227 -0
- raxe/infrastructure/policies/yaml_loader.py +250 -0
- raxe/infrastructure/rules/__init__.py +18 -0
- raxe/infrastructure/rules/custom_loader.py +224 -0
- raxe/infrastructure/rules/versioning.py +222 -0
- raxe/infrastructure/rules/yaml_loader.py +286 -0
- raxe/infrastructure/security/__init__.py +31 -0
- raxe/infrastructure/security/auth.py +145 -0
- raxe/infrastructure/security/policy_validator.py +124 -0
- raxe/infrastructure/security/signatures.py +171 -0
- raxe/infrastructure/suppression/__init__.py +36 -0
- raxe/infrastructure/suppression/composite_repository.py +154 -0
- raxe/infrastructure/suppression/sqlite_repository.py +231 -0
- raxe/infrastructure/suppression/yaml_composite_repository.py +156 -0
- raxe/infrastructure/suppression/yaml_repository.py +510 -0
- raxe/infrastructure/telemetry/__init__.py +79 -0
- raxe/infrastructure/telemetry/acquisition.py +179 -0
- raxe/infrastructure/telemetry/config.py +254 -0
- raxe/infrastructure/telemetry/credential_store.py +947 -0
- raxe/infrastructure/telemetry/dual_queue.py +1123 -0
- raxe/infrastructure/telemetry/flush_helper.py +343 -0
- raxe/infrastructure/telemetry/flush_scheduler.py +776 -0
- raxe/infrastructure/telemetry/health_client.py +394 -0
- raxe/infrastructure/telemetry/hook.py +347 -0
- raxe/infrastructure/telemetry/queue.py +520 -0
- raxe/infrastructure/telemetry/sender.py +476 -0
- raxe/infrastructure/tracking/__init__.py +13 -0
- raxe/infrastructure/tracking/usage.py +389 -0
- raxe/integrations/__init__.py +55 -0
- raxe/integrations/availability.py +143 -0
- raxe/integrations/registry.py +122 -0
- raxe/integrations/utils.py +135 -0
- raxe/mcp/__init__.py +62 -0
- raxe/mcp/cli.py +97 -0
- raxe/mcp/server.py +409 -0
- raxe/monitoring/__init__.py +51 -0
- raxe/monitoring/metrics.py +372 -0
- raxe/monitoring/profiler.py +388 -0
- raxe/monitoring/server.py +136 -0
- raxe/packs/core/v1.0.0/pack.yaml +1394 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-001@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-006@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-014@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-017@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-022@1.0.0.yaml +67 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-023@1.0.0.yaml +91 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-024@1.0.0.yaml +80 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-025@1.0.0.yaml +81 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-026@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-027@1.0.0.yaml +77 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-028@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-029@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-030@1.0.0.yaml +55 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-033@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-034@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-035@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-046@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-047@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-048@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-049@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-050@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-068@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-078@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-2001@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-2004@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-201@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-202@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-203@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3007@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3016@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3026@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3027@1.0.0.yaml +64 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3028@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3029@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3030@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3031@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3032@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3033@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-3034@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-79@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-80@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-81@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-82@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-83@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-84@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-85@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-86@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-87@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-88@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-89@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-90@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-91@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-92@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-93@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-94@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-95@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-96@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-97@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/PI/pi-98@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-007@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-015@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-016@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-017@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-021@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-022@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-023@1.0.0.yaml +78 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-024@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-025@1.0.0.yaml +93 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-026@1.0.0.yaml +81 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-027@1.0.0.yaml +82 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-028@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-033@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-036@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-037@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-052@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-054@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-056@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-065@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-075@1.0.0.yaml +45 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-079@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1080@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1090@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1104@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1105@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-1112@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-201@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-202@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-203@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-204@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-205@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-206@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-207@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-208@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-209@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-210@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-211@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-212@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-213@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-214@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-215@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-216@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-217@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-218@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-219@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-220@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-221@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-222@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-223@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-224@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-225@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-226@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-227@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-228@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-229@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-230@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-231@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-232@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-233@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-234@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-235@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-236@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-237@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/cmd/cmd-238@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-013@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-019@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-020@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-024@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-029@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-038@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-044@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-067@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-069@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-100@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-101@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-102@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-103@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-104@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-105@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-106@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-107@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-108@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-109@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-110@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-111@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-112@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-113@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-114@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-115@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-116@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-117@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-118@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-119@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-120@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-201@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-203@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3004@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3006@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-3011@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-5016@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-6001@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-6002@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-70@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-71@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-72@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-73@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-74@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-75@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-76@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-77@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-78@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-79@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-80@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-81@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-82@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-83@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-84@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-85@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-86@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-87@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-88@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-89@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-90@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-91@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-92@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-93@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-94@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-95@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-96@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-97@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-98@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/enc/enc-99@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-001@1.0.0.yaml +73 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-002@1.0.0.yaml +71 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-003@1.0.0.yaml +65 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-004@1.0.0.yaml +73 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-101@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-102@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-103@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-104@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-105@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-106@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-107@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-108@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-109@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-110@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-111@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-112@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-113@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-114@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-115@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-116@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-117@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-118@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-119@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-120@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-121@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-122@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-123@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-124@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-125@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-126@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-127@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-128@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-129@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-130@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-131@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-132@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-133@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-134@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-135@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-136@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-137@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-138@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-139@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-140@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-141@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-142@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-143@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-144@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-145@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-146@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-147@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-148@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-149@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-150@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-151@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-152@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-153@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-154@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-155@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-156@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-157@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-158@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-159@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-160@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/hc/hc-161@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-001@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-009@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-020@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-021@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-022@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-028@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-033@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-034@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-036@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-039@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-056@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-066@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-076@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-098@1.0.0.yaml +46 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-103@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-104@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-105@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-110@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-111@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-112@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-113@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-114@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-115@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-116@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-117@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-118@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-119@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-120@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-121@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-122@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-123@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-124@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-125@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-126@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-127@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-128@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-129@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-130@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-131@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-132@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-133@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-134@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-135@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-136@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-137@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-138@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-139@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-140@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-141@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-142@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-143@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-144@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-145@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-146@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-147@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-148@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-149@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-150@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-151@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-152@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-153@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-154@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-155@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-156@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-157@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-158@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-159@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-160@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-161@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-162@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-201@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-203@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-204@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-205@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-206@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/jb/jb-207@1.0.0.yaml +49 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-001@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-009@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-012@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-017@1.0.0.yaml +48 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-022@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-025@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-027@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-028@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-034@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-037@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-040@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-041@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-044@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-050@1.0.0.yaml +57 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-051@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-052@1.0.0.yaml +52 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-053@1.0.0.yaml +56 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-054@1.0.0.yaml +53 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-055@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-056@1.0.0.yaml +51 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-058@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2015@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2025@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2026@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2035@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2037@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-2042@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3001@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3002@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3003@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3004@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3005@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3006@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3007@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3008@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3009@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3010@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3011@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3012@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3013@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3014@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3015@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3016@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3017@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3018@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3019@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3020@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3021@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3022@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3023@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3024@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3025@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3026@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3027@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3028@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3029@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3030@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3031@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3032@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3033@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3034@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3035@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3036@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3037@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3038@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3039@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3040@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3041@1.0.0.yaml +39 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3042@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3043@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3044@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3045@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3046@1.0.0.yaml +37 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3047@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3048@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3049@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3050@1.0.0.yaml +44 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3051@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3052@1.0.0.yaml +36 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3053@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3054@1.0.0.yaml +35 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3055@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3056@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3057@1.0.0.yaml +40 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3058@1.0.0.yaml +43 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3059@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3060@1.0.0.yaml +42 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3061@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3062@1.0.0.yaml +50 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3063@1.0.0.yaml +54 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3064@1.0.0.yaml +78 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3065@1.0.0.yaml +84 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3066@1.0.0.yaml +84 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3067@1.0.0.yaml +88 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3068@1.0.0.yaml +94 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3069@1.0.0.yaml +90 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3070@1.0.0.yaml +99 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3071@1.0.0.yaml +91 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3072@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3073@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3074@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3075@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3076@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3077@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3078@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3079@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3080@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3081@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3082@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3083@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3084@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/pii/pii-3085@1.0.0.yaml +38 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-016@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-028@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-042@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-044@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-045@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-050@1.0.0.yaml +47 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-201@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-202@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3001@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3006@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3009@1.0.0.yaml +41 -0
- raxe/packs/core/v1.0.0/rules/rag/rag-3012@1.0.0.yaml +41 -0
- raxe/plugins/__init__.py +98 -0
- raxe/plugins/custom_rules.py +380 -0
- raxe/plugins/loader.py +389 -0
- raxe/plugins/manager.py +538 -0
- raxe/plugins/protocol.py +428 -0
- raxe/py.typed +0 -0
- raxe/sdk/__init__.py +77 -0
- raxe/sdk/agent_scanner.py +1918 -0
- raxe/sdk/client.py +1603 -0
- raxe/sdk/decorator.py +175 -0
- raxe/sdk/exceptions.py +859 -0
- raxe/sdk/integrations/__init__.py +277 -0
- raxe/sdk/integrations/agent_scanner.py +71 -0
- raxe/sdk/integrations/autogen.py +872 -0
- raxe/sdk/integrations/crewai.py +1368 -0
- raxe/sdk/integrations/dspy.py +845 -0
- raxe/sdk/integrations/extractors.py +363 -0
- raxe/sdk/integrations/huggingface.py +395 -0
- raxe/sdk/integrations/langchain.py +948 -0
- raxe/sdk/integrations/litellm.py +484 -0
- raxe/sdk/integrations/llamaindex.py +1049 -0
- raxe/sdk/integrations/portkey.py +831 -0
- raxe/sdk/suppression_context.py +215 -0
- raxe/sdk/wrappers/__init__.py +163 -0
- raxe/sdk/wrappers/anthropic.py +310 -0
- raxe/sdk/wrappers/openai.py +221 -0
- raxe/sdk/wrappers/vertexai.py +484 -0
- raxe/utils/__init__.py +12 -0
- raxe/utils/error_sanitizer.py +135 -0
- raxe/utils/logging.py +241 -0
- raxe/utils/performance.py +414 -0
- raxe/utils/profiler.py +339 -0
- raxe/utils/validators.py +170 -0
- raxe-0.4.6.dist-info/METADATA +471 -0
- raxe-0.4.6.dist-info/RECORD +668 -0
- raxe-0.4.6.dist-info/WHEEL +5 -0
- raxe-0.4.6.dist-info/entry_points.txt +2 -0
- raxe-0.4.6.dist-info/licenses/LICENSE +56 -0
- 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
|