truthound 1.0.8__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 (877) hide show
  1. truthound/__init__.py +162 -0
  2. truthound/adapters.py +100 -0
  3. truthound/api.py +365 -0
  4. truthound/audit/__init__.py +248 -0
  5. truthound/audit/core.py +967 -0
  6. truthound/audit/filters.py +620 -0
  7. truthound/audit/formatters.py +707 -0
  8. truthound/audit/logger.py +902 -0
  9. truthound/audit/middleware.py +571 -0
  10. truthound/audit/storage.py +1083 -0
  11. truthound/benchmark/__init__.py +123 -0
  12. truthound/benchmark/base.py +757 -0
  13. truthound/benchmark/comparison.py +635 -0
  14. truthound/benchmark/generators.py +706 -0
  15. truthound/benchmark/reporters.py +718 -0
  16. truthound/benchmark/runner.py +635 -0
  17. truthound/benchmark/scenarios.py +712 -0
  18. truthound/cache.py +252 -0
  19. truthound/checkpoint/__init__.py +136 -0
  20. truthound/checkpoint/actions/__init__.py +164 -0
  21. truthound/checkpoint/actions/base.py +324 -0
  22. truthound/checkpoint/actions/custom.py +234 -0
  23. truthound/checkpoint/actions/discord_notify.py +290 -0
  24. truthound/checkpoint/actions/email_notify.py +405 -0
  25. truthound/checkpoint/actions/github_action.py +406 -0
  26. truthound/checkpoint/actions/opsgenie.py +1499 -0
  27. truthound/checkpoint/actions/pagerduty.py +226 -0
  28. truthound/checkpoint/actions/slack_notify.py +233 -0
  29. truthound/checkpoint/actions/store_result.py +249 -0
  30. truthound/checkpoint/actions/teams_notify.py +1570 -0
  31. truthound/checkpoint/actions/telegram_notify.py +419 -0
  32. truthound/checkpoint/actions/update_docs.py +552 -0
  33. truthound/checkpoint/actions/webhook.py +293 -0
  34. truthound/checkpoint/analytics/__init__.py +147 -0
  35. truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
  36. truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
  37. truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
  38. truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
  39. truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
  40. truthound/checkpoint/analytics/analyzers/base.py +270 -0
  41. truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
  42. truthound/checkpoint/analytics/analyzers/trend.py +314 -0
  43. truthound/checkpoint/analytics/models.py +292 -0
  44. truthound/checkpoint/analytics/protocols.py +549 -0
  45. truthound/checkpoint/analytics/service.py +718 -0
  46. truthound/checkpoint/analytics/stores/__init__.py +16 -0
  47. truthound/checkpoint/analytics/stores/base.py +306 -0
  48. truthound/checkpoint/analytics/stores/memory_store.py +353 -0
  49. truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
  50. truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
  51. truthound/checkpoint/async_actions.py +794 -0
  52. truthound/checkpoint/async_base.py +708 -0
  53. truthound/checkpoint/async_checkpoint.py +617 -0
  54. truthound/checkpoint/async_runner.py +639 -0
  55. truthound/checkpoint/checkpoint.py +527 -0
  56. truthound/checkpoint/ci/__init__.py +61 -0
  57. truthound/checkpoint/ci/detector.py +355 -0
  58. truthound/checkpoint/ci/reporter.py +436 -0
  59. truthound/checkpoint/ci/templates.py +454 -0
  60. truthound/checkpoint/circuitbreaker/__init__.py +133 -0
  61. truthound/checkpoint/circuitbreaker/breaker.py +542 -0
  62. truthound/checkpoint/circuitbreaker/core.py +252 -0
  63. truthound/checkpoint/circuitbreaker/detection.py +459 -0
  64. truthound/checkpoint/circuitbreaker/middleware.py +389 -0
  65. truthound/checkpoint/circuitbreaker/registry.py +357 -0
  66. truthound/checkpoint/distributed/__init__.py +139 -0
  67. truthound/checkpoint/distributed/backends/__init__.py +35 -0
  68. truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
  69. truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
  70. truthound/checkpoint/distributed/backends/local_backend.py +397 -0
  71. truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
  72. truthound/checkpoint/distributed/base.py +774 -0
  73. truthound/checkpoint/distributed/orchestrator.py +765 -0
  74. truthound/checkpoint/distributed/protocols.py +842 -0
  75. truthound/checkpoint/distributed/registry.py +449 -0
  76. truthound/checkpoint/idempotency/__init__.py +120 -0
  77. truthound/checkpoint/idempotency/core.py +295 -0
  78. truthound/checkpoint/idempotency/fingerprint.py +454 -0
  79. truthound/checkpoint/idempotency/locking.py +604 -0
  80. truthound/checkpoint/idempotency/service.py +592 -0
  81. truthound/checkpoint/idempotency/stores.py +653 -0
  82. truthound/checkpoint/monitoring/__init__.py +134 -0
  83. truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
  84. truthound/checkpoint/monitoring/aggregators/base.py +372 -0
  85. truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
  86. truthound/checkpoint/monitoring/aggregators/window.py +493 -0
  87. truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
  88. truthound/checkpoint/monitoring/collectors/base.py +257 -0
  89. truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
  90. truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
  91. truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
  92. truthound/checkpoint/monitoring/events.py +410 -0
  93. truthound/checkpoint/monitoring/protocols.py +636 -0
  94. truthound/checkpoint/monitoring/service.py +578 -0
  95. truthound/checkpoint/monitoring/views/__init__.py +17 -0
  96. truthound/checkpoint/monitoring/views/base.py +172 -0
  97. truthound/checkpoint/monitoring/views/queue_view.py +220 -0
  98. truthound/checkpoint/monitoring/views/task_view.py +240 -0
  99. truthound/checkpoint/monitoring/views/worker_view.py +263 -0
  100. truthound/checkpoint/registry.py +337 -0
  101. truthound/checkpoint/runner.py +356 -0
  102. truthound/checkpoint/transaction/__init__.py +133 -0
  103. truthound/checkpoint/transaction/base.py +389 -0
  104. truthound/checkpoint/transaction/compensatable.py +537 -0
  105. truthound/checkpoint/transaction/coordinator.py +576 -0
  106. truthound/checkpoint/transaction/executor.py +622 -0
  107. truthound/checkpoint/transaction/idempotency.py +534 -0
  108. truthound/checkpoint/transaction/saga/__init__.py +143 -0
  109. truthound/checkpoint/transaction/saga/builder.py +584 -0
  110. truthound/checkpoint/transaction/saga/definition.py +515 -0
  111. truthound/checkpoint/transaction/saga/event_store.py +542 -0
  112. truthound/checkpoint/transaction/saga/patterns.py +833 -0
  113. truthound/checkpoint/transaction/saga/runner.py +718 -0
  114. truthound/checkpoint/transaction/saga/state_machine.py +793 -0
  115. truthound/checkpoint/transaction/saga/strategies.py +780 -0
  116. truthound/checkpoint/transaction/saga/testing.py +886 -0
  117. truthound/checkpoint/triggers/__init__.py +58 -0
  118. truthound/checkpoint/triggers/base.py +237 -0
  119. truthound/checkpoint/triggers/event.py +385 -0
  120. truthound/checkpoint/triggers/schedule.py +355 -0
  121. truthound/cli.py +2358 -0
  122. truthound/cli_modules/__init__.py +124 -0
  123. truthound/cli_modules/advanced/__init__.py +45 -0
  124. truthound/cli_modules/advanced/benchmark.py +343 -0
  125. truthound/cli_modules/advanced/docs.py +225 -0
  126. truthound/cli_modules/advanced/lineage.py +209 -0
  127. truthound/cli_modules/advanced/ml.py +320 -0
  128. truthound/cli_modules/advanced/realtime.py +196 -0
  129. truthound/cli_modules/checkpoint/__init__.py +46 -0
  130. truthound/cli_modules/checkpoint/init.py +114 -0
  131. truthound/cli_modules/checkpoint/list.py +71 -0
  132. truthound/cli_modules/checkpoint/run.py +159 -0
  133. truthound/cli_modules/checkpoint/validate.py +67 -0
  134. truthound/cli_modules/common/__init__.py +71 -0
  135. truthound/cli_modules/common/errors.py +414 -0
  136. truthound/cli_modules/common/options.py +419 -0
  137. truthound/cli_modules/common/output.py +507 -0
  138. truthound/cli_modules/common/protocol.py +552 -0
  139. truthound/cli_modules/core/__init__.py +48 -0
  140. truthound/cli_modules/core/check.py +123 -0
  141. truthound/cli_modules/core/compare.py +104 -0
  142. truthound/cli_modules/core/learn.py +57 -0
  143. truthound/cli_modules/core/mask.py +77 -0
  144. truthound/cli_modules/core/profile.py +65 -0
  145. truthound/cli_modules/core/scan.py +61 -0
  146. truthound/cli_modules/profiler/__init__.py +51 -0
  147. truthound/cli_modules/profiler/auto_profile.py +175 -0
  148. truthound/cli_modules/profiler/metadata.py +107 -0
  149. truthound/cli_modules/profiler/suite.py +283 -0
  150. truthound/cli_modules/registry.py +431 -0
  151. truthound/cli_modules/scaffolding/__init__.py +89 -0
  152. truthound/cli_modules/scaffolding/base.py +631 -0
  153. truthound/cli_modules/scaffolding/commands.py +545 -0
  154. truthound/cli_modules/scaffolding/plugins.py +1072 -0
  155. truthound/cli_modules/scaffolding/reporters.py +594 -0
  156. truthound/cli_modules/scaffolding/validators.py +1127 -0
  157. truthound/common/__init__.py +18 -0
  158. truthound/common/resilience/__init__.py +130 -0
  159. truthound/common/resilience/bulkhead.py +266 -0
  160. truthound/common/resilience/circuit_breaker.py +516 -0
  161. truthound/common/resilience/composite.py +332 -0
  162. truthound/common/resilience/config.py +292 -0
  163. truthound/common/resilience/protocols.py +217 -0
  164. truthound/common/resilience/rate_limiter.py +404 -0
  165. truthound/common/resilience/retry.py +341 -0
  166. truthound/datadocs/__init__.py +260 -0
  167. truthound/datadocs/base.py +571 -0
  168. truthound/datadocs/builder.py +761 -0
  169. truthound/datadocs/charts.py +764 -0
  170. truthound/datadocs/dashboard/__init__.py +63 -0
  171. truthound/datadocs/dashboard/app.py +576 -0
  172. truthound/datadocs/dashboard/components.py +584 -0
  173. truthound/datadocs/dashboard/state.py +240 -0
  174. truthound/datadocs/engine/__init__.py +46 -0
  175. truthound/datadocs/engine/context.py +376 -0
  176. truthound/datadocs/engine/pipeline.py +618 -0
  177. truthound/datadocs/engine/registry.py +469 -0
  178. truthound/datadocs/exporters/__init__.py +49 -0
  179. truthound/datadocs/exporters/base.py +198 -0
  180. truthound/datadocs/exporters/html.py +178 -0
  181. truthound/datadocs/exporters/json_exporter.py +253 -0
  182. truthound/datadocs/exporters/markdown.py +284 -0
  183. truthound/datadocs/exporters/pdf.py +392 -0
  184. truthound/datadocs/i18n/__init__.py +86 -0
  185. truthound/datadocs/i18n/catalog.py +960 -0
  186. truthound/datadocs/i18n/formatting.py +505 -0
  187. truthound/datadocs/i18n/loader.py +256 -0
  188. truthound/datadocs/i18n/plurals.py +378 -0
  189. truthound/datadocs/renderers/__init__.py +42 -0
  190. truthound/datadocs/renderers/base.py +401 -0
  191. truthound/datadocs/renderers/custom.py +342 -0
  192. truthound/datadocs/renderers/jinja.py +697 -0
  193. truthound/datadocs/sections.py +736 -0
  194. truthound/datadocs/styles.py +931 -0
  195. truthound/datadocs/themes/__init__.py +101 -0
  196. truthound/datadocs/themes/base.py +336 -0
  197. truthound/datadocs/themes/default.py +417 -0
  198. truthound/datadocs/themes/enterprise.py +419 -0
  199. truthound/datadocs/themes/loader.py +336 -0
  200. truthound/datadocs/themes.py +301 -0
  201. truthound/datadocs/transformers/__init__.py +57 -0
  202. truthound/datadocs/transformers/base.py +268 -0
  203. truthound/datadocs/transformers/enrichers.py +544 -0
  204. truthound/datadocs/transformers/filters.py +447 -0
  205. truthound/datadocs/transformers/i18n.py +468 -0
  206. truthound/datadocs/versioning/__init__.py +62 -0
  207. truthound/datadocs/versioning/diff.py +639 -0
  208. truthound/datadocs/versioning/storage.py +497 -0
  209. truthound/datadocs/versioning/version.py +358 -0
  210. truthound/datasources/__init__.py +223 -0
  211. truthound/datasources/_async_protocols.py +222 -0
  212. truthound/datasources/_protocols.py +159 -0
  213. truthound/datasources/adapters.py +428 -0
  214. truthound/datasources/async_base.py +599 -0
  215. truthound/datasources/async_factory.py +511 -0
  216. truthound/datasources/base.py +516 -0
  217. truthound/datasources/factory.py +433 -0
  218. truthound/datasources/nosql/__init__.py +47 -0
  219. truthound/datasources/nosql/base.py +487 -0
  220. truthound/datasources/nosql/elasticsearch.py +801 -0
  221. truthound/datasources/nosql/mongodb.py +636 -0
  222. truthound/datasources/pandas_optimized.py +582 -0
  223. truthound/datasources/pandas_source.py +216 -0
  224. truthound/datasources/polars_source.py +395 -0
  225. truthound/datasources/spark_source.py +479 -0
  226. truthound/datasources/sql/__init__.py +154 -0
  227. truthound/datasources/sql/base.py +710 -0
  228. truthound/datasources/sql/bigquery.py +410 -0
  229. truthound/datasources/sql/cloud_base.py +199 -0
  230. truthound/datasources/sql/databricks.py +471 -0
  231. truthound/datasources/sql/mysql.py +316 -0
  232. truthound/datasources/sql/oracle.py +427 -0
  233. truthound/datasources/sql/postgresql.py +321 -0
  234. truthound/datasources/sql/redshift.py +479 -0
  235. truthound/datasources/sql/snowflake.py +439 -0
  236. truthound/datasources/sql/sqlite.py +286 -0
  237. truthound/datasources/sql/sqlserver.py +437 -0
  238. truthound/datasources/streaming/__init__.py +47 -0
  239. truthound/datasources/streaming/base.py +350 -0
  240. truthound/datasources/streaming/kafka.py +670 -0
  241. truthound/decorators.py +98 -0
  242. truthound/docs/__init__.py +69 -0
  243. truthound/docs/extractor.py +971 -0
  244. truthound/docs/generator.py +601 -0
  245. truthound/docs/parser.py +1037 -0
  246. truthound/docs/renderer.py +999 -0
  247. truthound/drift/__init__.py +22 -0
  248. truthound/drift/compare.py +189 -0
  249. truthound/drift/detectors.py +464 -0
  250. truthound/drift/report.py +160 -0
  251. truthound/execution/__init__.py +65 -0
  252. truthound/execution/_protocols.py +324 -0
  253. truthound/execution/base.py +576 -0
  254. truthound/execution/distributed/__init__.py +179 -0
  255. truthound/execution/distributed/aggregations.py +731 -0
  256. truthound/execution/distributed/arrow_bridge.py +817 -0
  257. truthound/execution/distributed/base.py +550 -0
  258. truthound/execution/distributed/dask_engine.py +976 -0
  259. truthound/execution/distributed/mixins.py +766 -0
  260. truthound/execution/distributed/protocols.py +756 -0
  261. truthound/execution/distributed/ray_engine.py +1127 -0
  262. truthound/execution/distributed/registry.py +446 -0
  263. truthound/execution/distributed/spark_engine.py +1011 -0
  264. truthound/execution/distributed/validator_adapter.py +682 -0
  265. truthound/execution/pandas_engine.py +401 -0
  266. truthound/execution/polars_engine.py +497 -0
  267. truthound/execution/pushdown/__init__.py +230 -0
  268. truthound/execution/pushdown/ast.py +1550 -0
  269. truthound/execution/pushdown/builder.py +1550 -0
  270. truthound/execution/pushdown/dialects.py +1072 -0
  271. truthound/execution/pushdown/executor.py +829 -0
  272. truthound/execution/pushdown/optimizer.py +1041 -0
  273. truthound/execution/sql_engine.py +518 -0
  274. truthound/infrastructure/__init__.py +189 -0
  275. truthound/infrastructure/audit.py +1515 -0
  276. truthound/infrastructure/config.py +1133 -0
  277. truthound/infrastructure/encryption.py +1132 -0
  278. truthound/infrastructure/logging.py +1503 -0
  279. truthound/infrastructure/metrics.py +1220 -0
  280. truthound/lineage/__init__.py +89 -0
  281. truthound/lineage/base.py +746 -0
  282. truthound/lineage/impact_analysis.py +474 -0
  283. truthound/lineage/integrations/__init__.py +22 -0
  284. truthound/lineage/integrations/openlineage.py +548 -0
  285. truthound/lineage/tracker.py +512 -0
  286. truthound/lineage/visualization/__init__.py +33 -0
  287. truthound/lineage/visualization/protocols.py +145 -0
  288. truthound/lineage/visualization/renderers/__init__.py +20 -0
  289. truthound/lineage/visualization/renderers/cytoscape.py +329 -0
  290. truthound/lineage/visualization/renderers/d3.py +331 -0
  291. truthound/lineage/visualization/renderers/graphviz.py +276 -0
  292. truthound/lineage/visualization/renderers/mermaid.py +308 -0
  293. truthound/maskers.py +113 -0
  294. truthound/ml/__init__.py +124 -0
  295. truthound/ml/anomaly_models/__init__.py +31 -0
  296. truthound/ml/anomaly_models/ensemble.py +362 -0
  297. truthound/ml/anomaly_models/isolation_forest.py +444 -0
  298. truthound/ml/anomaly_models/statistical.py +392 -0
  299. truthound/ml/base.py +1178 -0
  300. truthound/ml/drift_detection/__init__.py +26 -0
  301. truthound/ml/drift_detection/concept.py +381 -0
  302. truthound/ml/drift_detection/distribution.py +361 -0
  303. truthound/ml/drift_detection/feature.py +442 -0
  304. truthound/ml/drift_detection/multivariate.py +495 -0
  305. truthound/ml/monitoring/__init__.py +88 -0
  306. truthound/ml/monitoring/alerting/__init__.py +33 -0
  307. truthound/ml/monitoring/alerting/handlers.py +427 -0
  308. truthound/ml/monitoring/alerting/rules.py +508 -0
  309. truthound/ml/monitoring/collectors/__init__.py +19 -0
  310. truthound/ml/monitoring/collectors/composite.py +105 -0
  311. truthound/ml/monitoring/collectors/drift.py +324 -0
  312. truthound/ml/monitoring/collectors/performance.py +179 -0
  313. truthound/ml/monitoring/collectors/quality.py +369 -0
  314. truthound/ml/monitoring/monitor.py +536 -0
  315. truthound/ml/monitoring/protocols.py +451 -0
  316. truthound/ml/monitoring/stores/__init__.py +15 -0
  317. truthound/ml/monitoring/stores/memory.py +201 -0
  318. truthound/ml/monitoring/stores/prometheus.py +296 -0
  319. truthound/ml/rule_learning/__init__.py +25 -0
  320. truthound/ml/rule_learning/constraint_miner.py +443 -0
  321. truthound/ml/rule_learning/pattern_learner.py +499 -0
  322. truthound/ml/rule_learning/profile_learner.py +462 -0
  323. truthound/multitenancy/__init__.py +326 -0
  324. truthound/multitenancy/core.py +852 -0
  325. truthound/multitenancy/integration.py +597 -0
  326. truthound/multitenancy/isolation.py +630 -0
  327. truthound/multitenancy/manager.py +770 -0
  328. truthound/multitenancy/middleware.py +765 -0
  329. truthound/multitenancy/quota.py +537 -0
  330. truthound/multitenancy/resolvers.py +603 -0
  331. truthound/multitenancy/storage.py +703 -0
  332. truthound/observability/__init__.py +307 -0
  333. truthound/observability/context.py +531 -0
  334. truthound/observability/instrumentation.py +611 -0
  335. truthound/observability/logging.py +887 -0
  336. truthound/observability/metrics.py +1157 -0
  337. truthound/observability/tracing/__init__.py +178 -0
  338. truthound/observability/tracing/baggage.py +310 -0
  339. truthound/observability/tracing/config.py +426 -0
  340. truthound/observability/tracing/exporter.py +787 -0
  341. truthound/observability/tracing/integration.py +1018 -0
  342. truthound/observability/tracing/otel/__init__.py +146 -0
  343. truthound/observability/tracing/otel/adapter.py +982 -0
  344. truthound/observability/tracing/otel/bridge.py +1177 -0
  345. truthound/observability/tracing/otel/compat.py +681 -0
  346. truthound/observability/tracing/otel/config.py +691 -0
  347. truthound/observability/tracing/otel/detection.py +327 -0
  348. truthound/observability/tracing/otel/protocols.py +426 -0
  349. truthound/observability/tracing/processor.py +561 -0
  350. truthound/observability/tracing/propagator.py +757 -0
  351. truthound/observability/tracing/provider.py +569 -0
  352. truthound/observability/tracing/resource.py +515 -0
  353. truthound/observability/tracing/sampler.py +487 -0
  354. truthound/observability/tracing/span.py +676 -0
  355. truthound/plugins/__init__.py +198 -0
  356. truthound/plugins/base.py +599 -0
  357. truthound/plugins/cli.py +680 -0
  358. truthound/plugins/dependencies/__init__.py +42 -0
  359. truthound/plugins/dependencies/graph.py +422 -0
  360. truthound/plugins/dependencies/resolver.py +417 -0
  361. truthound/plugins/discovery.py +379 -0
  362. truthound/plugins/docs/__init__.py +46 -0
  363. truthound/plugins/docs/extractor.py +444 -0
  364. truthound/plugins/docs/renderer.py +499 -0
  365. truthound/plugins/enterprise_manager.py +877 -0
  366. truthound/plugins/examples/__init__.py +19 -0
  367. truthound/plugins/examples/custom_validators.py +317 -0
  368. truthound/plugins/examples/slack_notifier.py +312 -0
  369. truthound/plugins/examples/xml_reporter.py +254 -0
  370. truthound/plugins/hooks.py +558 -0
  371. truthound/plugins/lifecycle/__init__.py +43 -0
  372. truthound/plugins/lifecycle/hot_reload.py +402 -0
  373. truthound/plugins/lifecycle/manager.py +371 -0
  374. truthound/plugins/manager.py +736 -0
  375. truthound/plugins/registry.py +338 -0
  376. truthound/plugins/security/__init__.py +93 -0
  377. truthound/plugins/security/exceptions.py +332 -0
  378. truthound/plugins/security/policies.py +348 -0
  379. truthound/plugins/security/protocols.py +643 -0
  380. truthound/plugins/security/sandbox/__init__.py +45 -0
  381. truthound/plugins/security/sandbox/context.py +158 -0
  382. truthound/plugins/security/sandbox/engines/__init__.py +19 -0
  383. truthound/plugins/security/sandbox/engines/container.py +379 -0
  384. truthound/plugins/security/sandbox/engines/noop.py +144 -0
  385. truthound/plugins/security/sandbox/engines/process.py +336 -0
  386. truthound/plugins/security/sandbox/factory.py +211 -0
  387. truthound/plugins/security/signing/__init__.py +57 -0
  388. truthound/plugins/security/signing/service.py +330 -0
  389. truthound/plugins/security/signing/trust_store.py +368 -0
  390. truthound/plugins/security/signing/verifier.py +459 -0
  391. truthound/plugins/versioning/__init__.py +41 -0
  392. truthound/plugins/versioning/constraints.py +297 -0
  393. truthound/plugins/versioning/resolver.py +329 -0
  394. truthound/profiler/__init__.py +1729 -0
  395. truthound/profiler/_lazy.py +452 -0
  396. truthound/profiler/ab_testing/__init__.py +80 -0
  397. truthound/profiler/ab_testing/analysis.py +449 -0
  398. truthound/profiler/ab_testing/base.py +257 -0
  399. truthound/profiler/ab_testing/experiment.py +395 -0
  400. truthound/profiler/ab_testing/tracking.py +368 -0
  401. truthound/profiler/auto_threshold.py +1170 -0
  402. truthound/profiler/base.py +579 -0
  403. truthound/profiler/cache_patterns.py +911 -0
  404. truthound/profiler/caching.py +1303 -0
  405. truthound/profiler/column_profiler.py +712 -0
  406. truthound/profiler/comparison.py +1007 -0
  407. truthound/profiler/custom_patterns.py +1170 -0
  408. truthound/profiler/dashboard/__init__.py +50 -0
  409. truthound/profiler/dashboard/app.py +476 -0
  410. truthound/profiler/dashboard/components.py +457 -0
  411. truthound/profiler/dashboard/config.py +72 -0
  412. truthound/profiler/distributed/__init__.py +83 -0
  413. truthound/profiler/distributed/base.py +281 -0
  414. truthound/profiler/distributed/dask_backend.py +498 -0
  415. truthound/profiler/distributed/local_backend.py +293 -0
  416. truthound/profiler/distributed/profiler.py +304 -0
  417. truthound/profiler/distributed/ray_backend.py +374 -0
  418. truthound/profiler/distributed/spark_backend.py +375 -0
  419. truthound/profiler/distributed.py +1366 -0
  420. truthound/profiler/enterprise_sampling.py +1065 -0
  421. truthound/profiler/errors.py +488 -0
  422. truthound/profiler/evolution/__init__.py +91 -0
  423. truthound/profiler/evolution/alerts.py +426 -0
  424. truthound/profiler/evolution/changes.py +206 -0
  425. truthound/profiler/evolution/compatibility.py +365 -0
  426. truthound/profiler/evolution/detector.py +372 -0
  427. truthound/profiler/evolution/protocols.py +121 -0
  428. truthound/profiler/generators/__init__.py +48 -0
  429. truthound/profiler/generators/base.py +384 -0
  430. truthound/profiler/generators/ml_rules.py +375 -0
  431. truthound/profiler/generators/pattern_rules.py +384 -0
  432. truthound/profiler/generators/schema_rules.py +267 -0
  433. truthound/profiler/generators/stats_rules.py +324 -0
  434. truthound/profiler/generators/suite_generator.py +857 -0
  435. truthound/profiler/i18n.py +1542 -0
  436. truthound/profiler/incremental.py +554 -0
  437. truthound/profiler/incremental_validation.py +1710 -0
  438. truthound/profiler/integration/__init__.py +73 -0
  439. truthound/profiler/integration/adapters.py +345 -0
  440. truthound/profiler/integration/context.py +371 -0
  441. truthound/profiler/integration/executor.py +527 -0
  442. truthound/profiler/integration/naming.py +75 -0
  443. truthound/profiler/integration/protocols.py +243 -0
  444. truthound/profiler/memory.py +1185 -0
  445. truthound/profiler/migration/__init__.py +60 -0
  446. truthound/profiler/migration/base.py +345 -0
  447. truthound/profiler/migration/manager.py +444 -0
  448. truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
  449. truthound/profiler/ml/__init__.py +73 -0
  450. truthound/profiler/ml/base.py +244 -0
  451. truthound/profiler/ml/classifier.py +507 -0
  452. truthound/profiler/ml/feature_extraction.py +604 -0
  453. truthound/profiler/ml/pretrained.py +448 -0
  454. truthound/profiler/ml_inference.py +1276 -0
  455. truthound/profiler/native_patterns.py +815 -0
  456. truthound/profiler/observability.py +1184 -0
  457. truthound/profiler/process_timeout.py +1566 -0
  458. truthound/profiler/progress.py +568 -0
  459. truthound/profiler/progress_callbacks.py +1734 -0
  460. truthound/profiler/quality.py +1345 -0
  461. truthound/profiler/resilience.py +1180 -0
  462. truthound/profiler/sampled_matcher.py +794 -0
  463. truthound/profiler/sampling.py +1288 -0
  464. truthound/profiler/scheduling/__init__.py +82 -0
  465. truthound/profiler/scheduling/protocols.py +214 -0
  466. truthound/profiler/scheduling/scheduler.py +474 -0
  467. truthound/profiler/scheduling/storage.py +457 -0
  468. truthound/profiler/scheduling/triggers.py +449 -0
  469. truthound/profiler/schema.py +603 -0
  470. truthound/profiler/streaming.py +685 -0
  471. truthound/profiler/streaming_patterns.py +1354 -0
  472. truthound/profiler/suite_cli.py +625 -0
  473. truthound/profiler/suite_config.py +789 -0
  474. truthound/profiler/suite_export.py +1268 -0
  475. truthound/profiler/table_profiler.py +547 -0
  476. truthound/profiler/timeout.py +565 -0
  477. truthound/profiler/validation.py +1532 -0
  478. truthound/profiler/visualization/__init__.py +118 -0
  479. truthound/profiler/visualization/base.py +346 -0
  480. truthound/profiler/visualization/generator.py +1259 -0
  481. truthound/profiler/visualization/plotly_renderer.py +811 -0
  482. truthound/profiler/visualization/renderers.py +669 -0
  483. truthound/profiler/visualization/sections.py +540 -0
  484. truthound/profiler/visualization.py +2122 -0
  485. truthound/profiler/yaml_validation.py +1151 -0
  486. truthound/py.typed +0 -0
  487. truthound/ratelimit/__init__.py +248 -0
  488. truthound/ratelimit/algorithms.py +1108 -0
  489. truthound/ratelimit/core.py +573 -0
  490. truthound/ratelimit/integration.py +532 -0
  491. truthound/ratelimit/limiter.py +663 -0
  492. truthound/ratelimit/middleware.py +700 -0
  493. truthound/ratelimit/policy.py +792 -0
  494. truthound/ratelimit/storage.py +763 -0
  495. truthound/rbac/__init__.py +340 -0
  496. truthound/rbac/core.py +976 -0
  497. truthound/rbac/integration.py +760 -0
  498. truthound/rbac/manager.py +1052 -0
  499. truthound/rbac/middleware.py +842 -0
  500. truthound/rbac/policy.py +954 -0
  501. truthound/rbac/storage.py +878 -0
  502. truthound/realtime/__init__.py +141 -0
  503. truthound/realtime/adapters/__init__.py +43 -0
  504. truthound/realtime/adapters/base.py +533 -0
  505. truthound/realtime/adapters/kafka.py +487 -0
  506. truthound/realtime/adapters/kinesis.py +479 -0
  507. truthound/realtime/adapters/mock.py +243 -0
  508. truthound/realtime/base.py +553 -0
  509. truthound/realtime/factory.py +382 -0
  510. truthound/realtime/incremental.py +660 -0
  511. truthound/realtime/processing/__init__.py +67 -0
  512. truthound/realtime/processing/exactly_once.py +575 -0
  513. truthound/realtime/processing/state.py +547 -0
  514. truthound/realtime/processing/windows.py +647 -0
  515. truthound/realtime/protocols.py +569 -0
  516. truthound/realtime/streaming.py +605 -0
  517. truthound/realtime/testing/__init__.py +32 -0
  518. truthound/realtime/testing/containers.py +615 -0
  519. truthound/realtime/testing/fixtures.py +484 -0
  520. truthound/report.py +280 -0
  521. truthound/reporters/__init__.py +46 -0
  522. truthound/reporters/_protocols.py +30 -0
  523. truthound/reporters/base.py +324 -0
  524. truthound/reporters/ci/__init__.py +66 -0
  525. truthound/reporters/ci/azure.py +436 -0
  526. truthound/reporters/ci/base.py +509 -0
  527. truthound/reporters/ci/bitbucket.py +567 -0
  528. truthound/reporters/ci/circleci.py +547 -0
  529. truthound/reporters/ci/detection.py +364 -0
  530. truthound/reporters/ci/factory.py +182 -0
  531. truthound/reporters/ci/github.py +388 -0
  532. truthound/reporters/ci/gitlab.py +471 -0
  533. truthound/reporters/ci/jenkins.py +525 -0
  534. truthound/reporters/console_reporter.py +299 -0
  535. truthound/reporters/factory.py +211 -0
  536. truthound/reporters/html_reporter.py +524 -0
  537. truthound/reporters/json_reporter.py +256 -0
  538. truthound/reporters/markdown_reporter.py +280 -0
  539. truthound/reporters/sdk/__init__.py +174 -0
  540. truthound/reporters/sdk/builder.py +558 -0
  541. truthound/reporters/sdk/mixins.py +1150 -0
  542. truthound/reporters/sdk/schema.py +1493 -0
  543. truthound/reporters/sdk/templates.py +666 -0
  544. truthound/reporters/sdk/testing.py +968 -0
  545. truthound/scanners.py +170 -0
  546. truthound/scheduling/__init__.py +122 -0
  547. truthound/scheduling/cron.py +1136 -0
  548. truthound/scheduling/presets.py +212 -0
  549. truthound/schema.py +275 -0
  550. truthound/secrets/__init__.py +173 -0
  551. truthound/secrets/base.py +618 -0
  552. truthound/secrets/cloud.py +682 -0
  553. truthound/secrets/integration.py +507 -0
  554. truthound/secrets/manager.py +633 -0
  555. truthound/secrets/oidc/__init__.py +172 -0
  556. truthound/secrets/oidc/base.py +902 -0
  557. truthound/secrets/oidc/credential_provider.py +623 -0
  558. truthound/secrets/oidc/exchangers.py +1001 -0
  559. truthound/secrets/oidc/github/__init__.py +110 -0
  560. truthound/secrets/oidc/github/claims.py +718 -0
  561. truthound/secrets/oidc/github/enhanced_provider.py +693 -0
  562. truthound/secrets/oidc/github/trust_policy.py +742 -0
  563. truthound/secrets/oidc/github/verification.py +723 -0
  564. truthound/secrets/oidc/github/workflow.py +691 -0
  565. truthound/secrets/oidc/providers.py +825 -0
  566. truthound/secrets/providers.py +506 -0
  567. truthound/secrets/resolver.py +495 -0
  568. truthound/stores/__init__.py +177 -0
  569. truthound/stores/backends/__init__.py +18 -0
  570. truthound/stores/backends/_protocols.py +340 -0
  571. truthound/stores/backends/azure_blob.py +530 -0
  572. truthound/stores/backends/concurrent_filesystem.py +915 -0
  573. truthound/stores/backends/connection_pool.py +1365 -0
  574. truthound/stores/backends/database.py +743 -0
  575. truthound/stores/backends/filesystem.py +538 -0
  576. truthound/stores/backends/gcs.py +399 -0
  577. truthound/stores/backends/memory.py +354 -0
  578. truthound/stores/backends/s3.py +434 -0
  579. truthound/stores/backpressure/__init__.py +84 -0
  580. truthound/stores/backpressure/base.py +375 -0
  581. truthound/stores/backpressure/circuit_breaker.py +434 -0
  582. truthound/stores/backpressure/monitor.py +376 -0
  583. truthound/stores/backpressure/strategies.py +677 -0
  584. truthound/stores/base.py +551 -0
  585. truthound/stores/batching/__init__.py +65 -0
  586. truthound/stores/batching/base.py +305 -0
  587. truthound/stores/batching/buffer.py +370 -0
  588. truthound/stores/batching/store.py +248 -0
  589. truthound/stores/batching/writer.py +521 -0
  590. truthound/stores/caching/__init__.py +60 -0
  591. truthound/stores/caching/backends.py +684 -0
  592. truthound/stores/caching/base.py +356 -0
  593. truthound/stores/caching/store.py +305 -0
  594. truthound/stores/compression/__init__.py +193 -0
  595. truthound/stores/compression/adaptive.py +694 -0
  596. truthound/stores/compression/base.py +514 -0
  597. truthound/stores/compression/pipeline.py +868 -0
  598. truthound/stores/compression/providers.py +672 -0
  599. truthound/stores/compression/streaming.py +832 -0
  600. truthound/stores/concurrency/__init__.py +81 -0
  601. truthound/stores/concurrency/atomic.py +556 -0
  602. truthound/stores/concurrency/index.py +775 -0
  603. truthound/stores/concurrency/locks.py +576 -0
  604. truthound/stores/concurrency/manager.py +482 -0
  605. truthound/stores/encryption/__init__.py +297 -0
  606. truthound/stores/encryption/base.py +952 -0
  607. truthound/stores/encryption/keys.py +1191 -0
  608. truthound/stores/encryption/pipeline.py +903 -0
  609. truthound/stores/encryption/providers.py +953 -0
  610. truthound/stores/encryption/streaming.py +950 -0
  611. truthound/stores/expectations.py +227 -0
  612. truthound/stores/factory.py +246 -0
  613. truthound/stores/migration/__init__.py +75 -0
  614. truthound/stores/migration/base.py +480 -0
  615. truthound/stores/migration/manager.py +347 -0
  616. truthound/stores/migration/registry.py +382 -0
  617. truthound/stores/migration/store.py +559 -0
  618. truthound/stores/observability/__init__.py +106 -0
  619. truthound/stores/observability/audit.py +718 -0
  620. truthound/stores/observability/config.py +270 -0
  621. truthound/stores/observability/factory.py +208 -0
  622. truthound/stores/observability/metrics.py +636 -0
  623. truthound/stores/observability/protocols.py +410 -0
  624. truthound/stores/observability/store.py +570 -0
  625. truthound/stores/observability/tracing.py +784 -0
  626. truthound/stores/replication/__init__.py +76 -0
  627. truthound/stores/replication/base.py +260 -0
  628. truthound/stores/replication/monitor.py +269 -0
  629. truthound/stores/replication/store.py +439 -0
  630. truthound/stores/replication/syncer.py +391 -0
  631. truthound/stores/results.py +359 -0
  632. truthound/stores/retention/__init__.py +77 -0
  633. truthound/stores/retention/base.py +378 -0
  634. truthound/stores/retention/policies.py +621 -0
  635. truthound/stores/retention/scheduler.py +279 -0
  636. truthound/stores/retention/store.py +526 -0
  637. truthound/stores/streaming/__init__.py +138 -0
  638. truthound/stores/streaming/base.py +801 -0
  639. truthound/stores/streaming/database.py +984 -0
  640. truthound/stores/streaming/filesystem.py +719 -0
  641. truthound/stores/streaming/reader.py +629 -0
  642. truthound/stores/streaming/s3.py +843 -0
  643. truthound/stores/streaming/writer.py +790 -0
  644. truthound/stores/tiering/__init__.py +108 -0
  645. truthound/stores/tiering/base.py +462 -0
  646. truthound/stores/tiering/manager.py +249 -0
  647. truthound/stores/tiering/policies.py +692 -0
  648. truthound/stores/tiering/store.py +526 -0
  649. truthound/stores/versioning/__init__.py +56 -0
  650. truthound/stores/versioning/base.py +376 -0
  651. truthound/stores/versioning/store.py +660 -0
  652. truthound/stores/versioning/strategies.py +353 -0
  653. truthound/types.py +56 -0
  654. truthound/validators/__init__.py +774 -0
  655. truthound/validators/aggregate/__init__.py +27 -0
  656. truthound/validators/aggregate/central.py +116 -0
  657. truthound/validators/aggregate/extremes.py +116 -0
  658. truthound/validators/aggregate/spread.py +118 -0
  659. truthound/validators/aggregate/sum.py +64 -0
  660. truthound/validators/aggregate/type.py +78 -0
  661. truthound/validators/anomaly/__init__.py +93 -0
  662. truthound/validators/anomaly/base.py +431 -0
  663. truthound/validators/anomaly/ml_based.py +1190 -0
  664. truthound/validators/anomaly/multivariate.py +647 -0
  665. truthound/validators/anomaly/statistical.py +599 -0
  666. truthound/validators/base.py +1089 -0
  667. truthound/validators/business_rule/__init__.py +46 -0
  668. truthound/validators/business_rule/base.py +147 -0
  669. truthound/validators/business_rule/checksum.py +509 -0
  670. truthound/validators/business_rule/financial.py +526 -0
  671. truthound/validators/cache.py +733 -0
  672. truthound/validators/completeness/__init__.py +39 -0
  673. truthound/validators/completeness/conditional.py +73 -0
  674. truthound/validators/completeness/default.py +98 -0
  675. truthound/validators/completeness/empty.py +103 -0
  676. truthound/validators/completeness/nan.py +337 -0
  677. truthound/validators/completeness/null.py +152 -0
  678. truthound/validators/cross_table/__init__.py +17 -0
  679. truthound/validators/cross_table/aggregate.py +333 -0
  680. truthound/validators/cross_table/row_count.py +122 -0
  681. truthound/validators/datetime/__init__.py +29 -0
  682. truthound/validators/datetime/format.py +78 -0
  683. truthound/validators/datetime/freshness.py +269 -0
  684. truthound/validators/datetime/order.py +73 -0
  685. truthound/validators/datetime/parseable.py +185 -0
  686. truthound/validators/datetime/range.py +202 -0
  687. truthound/validators/datetime/timezone.py +69 -0
  688. truthound/validators/distribution/__init__.py +49 -0
  689. truthound/validators/distribution/distribution.py +128 -0
  690. truthound/validators/distribution/monotonic.py +119 -0
  691. truthound/validators/distribution/outlier.py +178 -0
  692. truthound/validators/distribution/quantile.py +80 -0
  693. truthound/validators/distribution/range.py +254 -0
  694. truthound/validators/distribution/set.py +125 -0
  695. truthound/validators/distribution/statistical.py +459 -0
  696. truthound/validators/drift/__init__.py +79 -0
  697. truthound/validators/drift/base.py +427 -0
  698. truthound/validators/drift/multi_feature.py +401 -0
  699. truthound/validators/drift/numeric.py +395 -0
  700. truthound/validators/drift/psi.py +446 -0
  701. truthound/validators/drift/statistical.py +510 -0
  702. truthound/validators/enterprise.py +1658 -0
  703. truthound/validators/geospatial/__init__.py +80 -0
  704. truthound/validators/geospatial/base.py +97 -0
  705. truthound/validators/geospatial/boundary.py +238 -0
  706. truthound/validators/geospatial/coordinate.py +351 -0
  707. truthound/validators/geospatial/distance.py +399 -0
  708. truthound/validators/geospatial/polygon.py +665 -0
  709. truthound/validators/i18n/__init__.py +308 -0
  710. truthound/validators/i18n/bidi.py +571 -0
  711. truthound/validators/i18n/catalogs.py +570 -0
  712. truthound/validators/i18n/dialects.py +763 -0
  713. truthound/validators/i18n/extended_catalogs.py +549 -0
  714. truthound/validators/i18n/formatting.py +1434 -0
  715. truthound/validators/i18n/loader.py +1020 -0
  716. truthound/validators/i18n/messages.py +521 -0
  717. truthound/validators/i18n/plural.py +683 -0
  718. truthound/validators/i18n/protocols.py +855 -0
  719. truthound/validators/i18n/tms.py +1162 -0
  720. truthound/validators/localization/__init__.py +53 -0
  721. truthound/validators/localization/base.py +122 -0
  722. truthound/validators/localization/chinese.py +362 -0
  723. truthound/validators/localization/japanese.py +275 -0
  724. truthound/validators/localization/korean.py +524 -0
  725. truthound/validators/memory/__init__.py +94 -0
  726. truthound/validators/memory/approximate_knn.py +506 -0
  727. truthound/validators/memory/base.py +547 -0
  728. truthound/validators/memory/sgd_online.py +719 -0
  729. truthound/validators/memory/streaming_ecdf.py +753 -0
  730. truthound/validators/ml_feature/__init__.py +54 -0
  731. truthound/validators/ml_feature/base.py +249 -0
  732. truthound/validators/ml_feature/correlation.py +299 -0
  733. truthound/validators/ml_feature/leakage.py +344 -0
  734. truthound/validators/ml_feature/null_impact.py +270 -0
  735. truthound/validators/ml_feature/scale.py +264 -0
  736. truthound/validators/multi_column/__init__.py +89 -0
  737. truthound/validators/multi_column/arithmetic.py +284 -0
  738. truthound/validators/multi_column/base.py +231 -0
  739. truthound/validators/multi_column/comparison.py +273 -0
  740. truthound/validators/multi_column/consistency.py +312 -0
  741. truthound/validators/multi_column/statistical.py +299 -0
  742. truthound/validators/optimization/__init__.py +164 -0
  743. truthound/validators/optimization/aggregation.py +563 -0
  744. truthound/validators/optimization/covariance.py +556 -0
  745. truthound/validators/optimization/geo.py +626 -0
  746. truthound/validators/optimization/graph.py +587 -0
  747. truthound/validators/optimization/orchestrator.py +970 -0
  748. truthound/validators/optimization/profiling.py +1312 -0
  749. truthound/validators/privacy/__init__.py +223 -0
  750. truthound/validators/privacy/base.py +635 -0
  751. truthound/validators/privacy/ccpa.py +670 -0
  752. truthound/validators/privacy/gdpr.py +728 -0
  753. truthound/validators/privacy/global_patterns.py +604 -0
  754. truthound/validators/privacy/plugins.py +867 -0
  755. truthound/validators/profiling/__init__.py +52 -0
  756. truthound/validators/profiling/base.py +175 -0
  757. truthound/validators/profiling/cardinality.py +312 -0
  758. truthound/validators/profiling/entropy.py +391 -0
  759. truthound/validators/profiling/frequency.py +455 -0
  760. truthound/validators/pushdown_support.py +660 -0
  761. truthound/validators/query/__init__.py +91 -0
  762. truthound/validators/query/aggregate.py +346 -0
  763. truthound/validators/query/base.py +246 -0
  764. truthound/validators/query/column.py +249 -0
  765. truthound/validators/query/expression.py +274 -0
  766. truthound/validators/query/result.py +323 -0
  767. truthound/validators/query/row_count.py +264 -0
  768. truthound/validators/referential/__init__.py +80 -0
  769. truthound/validators/referential/base.py +395 -0
  770. truthound/validators/referential/cascade.py +391 -0
  771. truthound/validators/referential/circular.py +563 -0
  772. truthound/validators/referential/foreign_key.py +624 -0
  773. truthound/validators/referential/orphan.py +485 -0
  774. truthound/validators/registry.py +112 -0
  775. truthound/validators/schema/__init__.py +41 -0
  776. truthound/validators/schema/column_count.py +142 -0
  777. truthound/validators/schema/column_exists.py +80 -0
  778. truthound/validators/schema/column_order.py +82 -0
  779. truthound/validators/schema/column_pair.py +85 -0
  780. truthound/validators/schema/column_pair_set.py +195 -0
  781. truthound/validators/schema/column_type.py +94 -0
  782. truthound/validators/schema/multi_column.py +53 -0
  783. truthound/validators/schema/multi_column_aggregate.py +175 -0
  784. truthound/validators/schema/referential.py +274 -0
  785. truthound/validators/schema/table_schema.py +91 -0
  786. truthound/validators/schema_validator.py +219 -0
  787. truthound/validators/sdk/__init__.py +250 -0
  788. truthound/validators/sdk/builder.py +680 -0
  789. truthound/validators/sdk/decorators.py +474 -0
  790. truthound/validators/sdk/enterprise/__init__.py +211 -0
  791. truthound/validators/sdk/enterprise/docs.py +725 -0
  792. truthound/validators/sdk/enterprise/fuzzing.py +659 -0
  793. truthound/validators/sdk/enterprise/licensing.py +709 -0
  794. truthound/validators/sdk/enterprise/manager.py +543 -0
  795. truthound/validators/sdk/enterprise/resources.py +628 -0
  796. truthound/validators/sdk/enterprise/sandbox.py +766 -0
  797. truthound/validators/sdk/enterprise/signing.py +603 -0
  798. truthound/validators/sdk/enterprise/templates.py +865 -0
  799. truthound/validators/sdk/enterprise/versioning.py +659 -0
  800. truthound/validators/sdk/templates.py +757 -0
  801. truthound/validators/sdk/testing.py +807 -0
  802. truthound/validators/security/__init__.py +181 -0
  803. truthound/validators/security/redos/__init__.py +182 -0
  804. truthound/validators/security/redos/core.py +861 -0
  805. truthound/validators/security/redos/cpu_monitor.py +593 -0
  806. truthound/validators/security/redos/cve_database.py +791 -0
  807. truthound/validators/security/redos/ml/__init__.py +155 -0
  808. truthound/validators/security/redos/ml/base.py +785 -0
  809. truthound/validators/security/redos/ml/datasets.py +618 -0
  810. truthound/validators/security/redos/ml/features.py +359 -0
  811. truthound/validators/security/redos/ml/models.py +1000 -0
  812. truthound/validators/security/redos/ml/predictor.py +507 -0
  813. truthound/validators/security/redos/ml/storage.py +632 -0
  814. truthound/validators/security/redos/ml/training.py +571 -0
  815. truthound/validators/security/redos/ml_analyzer.py +937 -0
  816. truthound/validators/security/redos/optimizer.py +674 -0
  817. truthound/validators/security/redos/profiler.py +682 -0
  818. truthound/validators/security/redos/re2_engine.py +709 -0
  819. truthound/validators/security/redos.py +886 -0
  820. truthound/validators/security/sql_security.py +1247 -0
  821. truthound/validators/streaming/__init__.py +126 -0
  822. truthound/validators/streaming/base.py +292 -0
  823. truthound/validators/streaming/completeness.py +210 -0
  824. truthound/validators/streaming/mixin.py +575 -0
  825. truthound/validators/streaming/range.py +308 -0
  826. truthound/validators/streaming/sources.py +846 -0
  827. truthound/validators/string/__init__.py +57 -0
  828. truthound/validators/string/casing.py +158 -0
  829. truthound/validators/string/charset.py +96 -0
  830. truthound/validators/string/format.py +501 -0
  831. truthound/validators/string/json.py +77 -0
  832. truthound/validators/string/json_schema.py +184 -0
  833. truthound/validators/string/length.py +104 -0
  834. truthound/validators/string/like_pattern.py +237 -0
  835. truthound/validators/string/regex.py +202 -0
  836. truthound/validators/string/regex_extended.py +435 -0
  837. truthound/validators/table/__init__.py +88 -0
  838. truthound/validators/table/base.py +78 -0
  839. truthound/validators/table/column_count.py +198 -0
  840. truthound/validators/table/freshness.py +362 -0
  841. truthound/validators/table/row_count.py +251 -0
  842. truthound/validators/table/schema.py +333 -0
  843. truthound/validators/table/size.py +285 -0
  844. truthound/validators/timeout/__init__.py +102 -0
  845. truthound/validators/timeout/advanced/__init__.py +247 -0
  846. truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
  847. truthound/validators/timeout/advanced/prediction.py +773 -0
  848. truthound/validators/timeout/advanced/priority.py +618 -0
  849. truthound/validators/timeout/advanced/redis_backend.py +770 -0
  850. truthound/validators/timeout/advanced/retry.py +721 -0
  851. truthound/validators/timeout/advanced/sampling.py +788 -0
  852. truthound/validators/timeout/advanced/sla.py +661 -0
  853. truthound/validators/timeout/advanced/telemetry.py +804 -0
  854. truthound/validators/timeout/cascade.py +477 -0
  855. truthound/validators/timeout/deadline.py +657 -0
  856. truthound/validators/timeout/degradation.py +525 -0
  857. truthound/validators/timeout/distributed.py +597 -0
  858. truthound/validators/timeseries/__init__.py +89 -0
  859. truthound/validators/timeseries/base.py +326 -0
  860. truthound/validators/timeseries/completeness.py +617 -0
  861. truthound/validators/timeseries/gap.py +485 -0
  862. truthound/validators/timeseries/monotonic.py +310 -0
  863. truthound/validators/timeseries/seasonality.py +422 -0
  864. truthound/validators/timeseries/trend.py +510 -0
  865. truthound/validators/uniqueness/__init__.py +59 -0
  866. truthound/validators/uniqueness/approximate.py +475 -0
  867. truthound/validators/uniqueness/distinct_values.py +253 -0
  868. truthound/validators/uniqueness/duplicate.py +118 -0
  869. truthound/validators/uniqueness/primary_key.py +140 -0
  870. truthound/validators/uniqueness/unique.py +191 -0
  871. truthound/validators/uniqueness/within_record.py +599 -0
  872. truthound/validators/utils.py +756 -0
  873. truthound-1.0.8.dist-info/METADATA +474 -0
  874. truthound-1.0.8.dist-info/RECORD +877 -0
  875. truthound-1.0.8.dist-info/WHEEL +4 -0
  876. truthound-1.0.8.dist-info/entry_points.txt +2 -0
  877. truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
@@ -0,0 +1,1132 @@
1
+ """Enterprise data encryption system for Truthound.
2
+
3
+ This module extends the base encryption system with enterprise features:
4
+ - At-rest encryption for validation results
5
+ - Field-level encryption for sensitive columns
6
+ - Cloud KMS integration (AWS, GCP, Azure, Vault)
7
+ - Key rotation and lifecycle management
8
+
9
+ Architecture:
10
+ AtRestEncryption
11
+ |
12
+ +---> Local encryption (AES-256-GCM)
13
+ +---> Cloud KMS wrapping
14
+ |
15
+ FieldLevelEncryption
16
+ |
17
+ +---> Per-column encryption policies
18
+ +---> Format-preserving encryption
19
+ |
20
+ KeyProvider
21
+ |
22
+ +---> VaultKeyProvider (HashiCorp Vault)
23
+ +---> AwsKmsProvider (AWS KMS)
24
+ +---> GcpKmsProvider (Google Cloud KMS)
25
+ +---> AzureKeyVaultProvider (Azure Key Vault)
26
+
27
+ Usage:
28
+ >>> from truthound.infrastructure.encryption import (
29
+ ... get_encryptor, configure_encryption,
30
+ ... AtRestEncryption, FieldLevelEncryption,
31
+ ... )
32
+ >>>
33
+ >>> # Configure encryption
34
+ >>> configure_encryption(
35
+ ... provider="aws_kms",
36
+ ... key_id="alias/truthound-data-key",
37
+ ... region="us-east-1",
38
+ ... )
39
+ >>>
40
+ >>> # Encrypt data at rest
41
+ >>> encryptor = get_encryptor()
42
+ >>> encrypted = encryptor.encrypt(sensitive_data)
43
+ >>>
44
+ >>> # Field-level encryption
45
+ >>> field_enc = FieldLevelEncryption(
46
+ ... policies={
47
+ ... "ssn": {"algorithm": "format_preserving"},
48
+ ... "email": {"algorithm": "aes_gcm"},
49
+ ... }
50
+ ... )
51
+ """
52
+
53
+ from __future__ import annotations
54
+
55
+ import base64
56
+ import hashlib
57
+ import json
58
+ import os
59
+ import struct
60
+ import threading
61
+ import time
62
+ from abc import ABC, abstractmethod
63
+ from contextlib import contextmanager
64
+ from dataclasses import dataclass, field
65
+ from datetime import datetime, timedelta, timezone
66
+ from enum import Enum
67
+ from pathlib import Path
68
+ from typing import Any, Callable, Iterator, TypeVar
69
+
70
+ # Re-export base encryption components
71
+ from truthound.stores.encryption import (
72
+ EncryptionAlgorithm,
73
+ EncryptionConfig,
74
+ EncryptionError,
75
+ DecryptionError,
76
+ generate_key,
77
+ generate_nonce,
78
+ get_encryptor as get_base_encryptor,
79
+ AesGcmEncryptor,
80
+ ChaCha20Poly1305Encryptor,
81
+ )
82
+
83
+
84
+ # =============================================================================
85
+ # Key Provider Protocol
86
+ # =============================================================================
87
+
88
+
89
+ class KeyProvider(ABC):
90
+ """Abstract base class for key providers.
91
+
92
+ Key providers supply encryption keys from various sources:
93
+ - Cloud KMS (AWS, GCP, Azure)
94
+ - HashiCorp Vault
95
+ - Local key stores
96
+
97
+ The provider handles key retrieval, caching, and rotation.
98
+ """
99
+
100
+ @abstractmethod
101
+ def get_key(self, key_id: str) -> bytes:
102
+ """Get encryption key by ID.
103
+
104
+ Args:
105
+ key_id: Key identifier.
106
+
107
+ Returns:
108
+ Raw key bytes.
109
+ """
110
+ pass
111
+
112
+ @abstractmethod
113
+ def encrypt_data_key(self, data_key: bytes, key_id: str) -> bytes:
114
+ """Encrypt a data key with a master key (envelope encryption).
115
+
116
+ Args:
117
+ data_key: Plain data key.
118
+ key_id: Master key ID.
119
+
120
+ Returns:
121
+ Encrypted data key.
122
+ """
123
+ pass
124
+
125
+ @abstractmethod
126
+ def decrypt_data_key(self, encrypted_key: bytes, key_id: str) -> bytes:
127
+ """Decrypt a data key with a master key.
128
+
129
+ Args:
130
+ encrypted_key: Encrypted data key.
131
+ key_id: Master key ID.
132
+
133
+ Returns:
134
+ Plain data key.
135
+ """
136
+ pass
137
+
138
+ def generate_data_key(self, key_id: str, key_length: int = 32) -> tuple[bytes, bytes]:
139
+ """Generate a new data key and encrypt it.
140
+
141
+ Args:
142
+ key_id: Master key ID for wrapping.
143
+ key_length: Key length in bytes.
144
+
145
+ Returns:
146
+ Tuple of (plain_key, encrypted_key).
147
+ """
148
+ plain_key = os.urandom(key_length)
149
+ encrypted_key = self.encrypt_data_key(plain_key, key_id)
150
+ return plain_key, encrypted_key
151
+
152
+ def close(self) -> None:
153
+ """Clean up provider resources."""
154
+ pass
155
+
156
+
157
+ # =============================================================================
158
+ # Cloud Key Providers
159
+ # =============================================================================
160
+
161
+
162
+ class VaultKeyProvider(KeyProvider):
163
+ """HashiCorp Vault key provider.
164
+
165
+ Uses Vault's Transit secrets engine for key management.
166
+ """
167
+
168
+ def __init__(
169
+ self,
170
+ url: str,
171
+ *,
172
+ token: str | None = None,
173
+ mount_point: str = "transit",
174
+ cache_ttl: float = 300.0,
175
+ ) -> None:
176
+ """Initialize Vault provider.
177
+
178
+ Args:
179
+ url: Vault server URL.
180
+ token: Vault token (or VAULT_TOKEN env var).
181
+ mount_point: Transit engine mount point.
182
+ cache_ttl: Key cache TTL in seconds.
183
+ """
184
+ self._url = url.rstrip("/")
185
+ self._token = token or os.getenv("VAULT_TOKEN", "")
186
+ self._mount_point = mount_point
187
+ self._cache_ttl = cache_ttl
188
+ self._cache: dict[str, tuple[bytes, float]] = {}
189
+ self._lock = threading.Lock()
190
+
191
+ def get_key(self, key_id: str) -> bytes:
192
+ """Get key from Vault (uses transit for wrapping only)."""
193
+ # For Vault transit, we generate local data keys and use transit for wrapping
194
+ # This returns a locally generated key that will be wrapped by Vault
195
+ return os.urandom(32)
196
+
197
+ def encrypt_data_key(self, data_key: bytes, key_id: str) -> bytes:
198
+ """Encrypt data key using Vault transit."""
199
+ try:
200
+ import urllib.request
201
+
202
+ # Base64 encode the data key
203
+ plaintext_b64 = base64.b64encode(data_key).decode("utf-8")
204
+
205
+ url = f"{self._url}/v1/{self._mount_point}/encrypt/{key_id}"
206
+ payload = json.dumps({"plaintext": plaintext_b64}).encode("utf-8")
207
+
208
+ request = urllib.request.Request(
209
+ url,
210
+ data=payload,
211
+ headers={
212
+ "X-Vault-Token": self._token,
213
+ "Content-Type": "application/json",
214
+ },
215
+ method="POST",
216
+ )
217
+
218
+ with urllib.request.urlopen(request, timeout=30) as response:
219
+ data = json.loads(response.read().decode("utf-8"))
220
+ ciphertext = data["data"]["ciphertext"]
221
+ return ciphertext.encode("utf-8")
222
+
223
+ except Exception as e:
224
+ raise EncryptionError(f"Vault encrypt failed: {e}")
225
+
226
+ def decrypt_data_key(self, encrypted_key: bytes, key_id: str) -> bytes:
227
+ """Decrypt data key using Vault transit."""
228
+ try:
229
+ import urllib.request
230
+
231
+ ciphertext = encrypted_key.decode("utf-8")
232
+
233
+ url = f"{self._url}/v1/{self._mount_point}/decrypt/{key_id}"
234
+ payload = json.dumps({"ciphertext": ciphertext}).encode("utf-8")
235
+
236
+ request = urllib.request.Request(
237
+ url,
238
+ data=payload,
239
+ headers={
240
+ "X-Vault-Token": self._token,
241
+ "Content-Type": "application/json",
242
+ },
243
+ method="POST",
244
+ )
245
+
246
+ with urllib.request.urlopen(request, timeout=30) as response:
247
+ data = json.loads(response.read().decode("utf-8"))
248
+ plaintext_b64 = data["data"]["plaintext"]
249
+ return base64.b64decode(plaintext_b64)
250
+
251
+ except Exception as e:
252
+ raise DecryptionError(f"Vault decrypt failed: {e}")
253
+
254
+
255
+ class AwsKmsProvider(KeyProvider):
256
+ """AWS KMS key provider.
257
+
258
+ Uses AWS KMS for key management and envelope encryption.
259
+ """
260
+
261
+ def __init__(
262
+ self,
263
+ key_id: str,
264
+ *,
265
+ region: str | None = None,
266
+ cache_ttl: float = 300.0,
267
+ ) -> None:
268
+ """Initialize AWS KMS provider.
269
+
270
+ Args:
271
+ key_id: KMS key ID or alias (e.g., alias/my-key).
272
+ region: AWS region.
273
+ cache_ttl: Key cache TTL.
274
+ """
275
+ self._key_id = key_id
276
+ self._region = region or os.getenv("AWS_REGION", "us-east-1")
277
+ self._cache_ttl = cache_ttl
278
+ self._client = None
279
+ self._lock = threading.Lock()
280
+
281
+ def _get_client(self) -> Any:
282
+ """Get or create KMS client."""
283
+ if self._client is None:
284
+ try:
285
+ import boto3
286
+
287
+ self._client = boto3.client("kms", region_name=self._region)
288
+ except ImportError:
289
+ raise EncryptionError("boto3 not installed")
290
+ return self._client
291
+
292
+ def get_key(self, key_id: str) -> bytes:
293
+ """Generate data key from KMS."""
294
+ plain_key, _ = self.generate_data_key(key_id)
295
+ return plain_key
296
+
297
+ def encrypt_data_key(self, data_key: bytes, key_id: str) -> bytes:
298
+ """Encrypt data key with KMS."""
299
+ try:
300
+ client = self._get_client()
301
+ response = client.encrypt(
302
+ KeyId=key_id or self._key_id,
303
+ Plaintext=data_key,
304
+ )
305
+ return response["CiphertextBlob"]
306
+ except Exception as e:
307
+ raise EncryptionError(f"AWS KMS encrypt failed: {e}")
308
+
309
+ def decrypt_data_key(self, encrypted_key: bytes, key_id: str) -> bytes:
310
+ """Decrypt data key with KMS."""
311
+ try:
312
+ client = self._get_client()
313
+ response = client.decrypt(
314
+ KeyId=key_id or self._key_id,
315
+ CiphertextBlob=encrypted_key,
316
+ )
317
+ return response["Plaintext"]
318
+ except Exception as e:
319
+ raise DecryptionError(f"AWS KMS decrypt failed: {e}")
320
+
321
+ def generate_data_key(self, key_id: str, key_length: int = 32) -> tuple[bytes, bytes]:
322
+ """Generate data key using KMS GenerateDataKey."""
323
+ try:
324
+ client = self._get_client()
325
+ key_spec = "AES_256" if key_length == 32 else "AES_128"
326
+ response = client.generate_data_key(
327
+ KeyId=key_id or self._key_id,
328
+ KeySpec=key_spec,
329
+ )
330
+ return response["Plaintext"], response["CiphertextBlob"]
331
+ except Exception as e:
332
+ raise EncryptionError(f"AWS KMS generate data key failed: {e}")
333
+
334
+
335
+ class GcpKmsProvider(KeyProvider):
336
+ """Google Cloud KMS key provider.
337
+
338
+ Uses Google Cloud KMS for key management.
339
+ """
340
+
341
+ def __init__(
342
+ self,
343
+ key_name: str,
344
+ *,
345
+ project_id: str | None = None,
346
+ location: str = "global",
347
+ key_ring: str = "truthound",
348
+ ) -> None:
349
+ """Initialize GCP KMS provider.
350
+
351
+ Args:
352
+ key_name: KMS key name.
353
+ project_id: GCP project ID.
354
+ location: KMS location.
355
+ key_ring: Key ring name.
356
+ """
357
+ self._key_name = key_name
358
+ self._project_id = project_id or os.getenv("GCP_PROJECT_ID", "")
359
+ self._location = location
360
+ self._key_ring = key_ring
361
+ self._client = None
362
+
363
+ def _get_client(self) -> Any:
364
+ """Get or create KMS client."""
365
+ if self._client is None:
366
+ try:
367
+ from google.cloud import kms
368
+
369
+ self._client = kms.KeyManagementServiceClient()
370
+ except ImportError:
371
+ raise EncryptionError("google-cloud-kms not installed")
372
+ return self._client
373
+
374
+ def _get_key_path(self, key_id: str) -> str:
375
+ """Get full key path."""
376
+ key = key_id or self._key_name
377
+ return f"projects/{self._project_id}/locations/{self._location}/keyRings/{self._key_ring}/cryptoKeys/{key}"
378
+
379
+ def get_key(self, key_id: str) -> bytes:
380
+ """Generate data key."""
381
+ plain_key, _ = self.generate_data_key(key_id)
382
+ return plain_key
383
+
384
+ def encrypt_data_key(self, data_key: bytes, key_id: str) -> bytes:
385
+ """Encrypt data key with GCP KMS."""
386
+ try:
387
+ client = self._get_client()
388
+ key_path = self._get_key_path(key_id)
389
+ response = client.encrypt(
390
+ request={"name": key_path, "plaintext": data_key}
391
+ )
392
+ return response.ciphertext
393
+ except Exception as e:
394
+ raise EncryptionError(f"GCP KMS encrypt failed: {e}")
395
+
396
+ def decrypt_data_key(self, encrypted_key: bytes, key_id: str) -> bytes:
397
+ """Decrypt data key with GCP KMS."""
398
+ try:
399
+ client = self._get_client()
400
+ key_path = self._get_key_path(key_id)
401
+ response = client.decrypt(
402
+ request={"name": key_path, "ciphertext": encrypted_key}
403
+ )
404
+ return response.plaintext
405
+ except Exception as e:
406
+ raise DecryptionError(f"GCP KMS decrypt failed: {e}")
407
+
408
+
409
+ class AzureKeyVaultProvider(KeyProvider):
410
+ """Azure Key Vault key provider.
411
+
412
+ Uses Azure Key Vault for key management.
413
+ """
414
+
415
+ def __init__(
416
+ self,
417
+ vault_url: str,
418
+ key_name: str,
419
+ *,
420
+ credential: Any = None,
421
+ ) -> None:
422
+ """Initialize Azure Key Vault provider.
423
+
424
+ Args:
425
+ vault_url: Key Vault URL.
426
+ key_name: Key name.
427
+ credential: Azure credential (DefaultAzureCredential if None).
428
+ """
429
+ self._vault_url = vault_url
430
+ self._key_name = key_name
431
+ self._credential = credential
432
+ self._client = None
433
+ self._crypto_client = None
434
+
435
+ def _get_clients(self) -> tuple[Any, Any]:
436
+ """Get or create Key Vault clients."""
437
+ if self._client is None:
438
+ try:
439
+ from azure.identity import DefaultAzureCredential
440
+ from azure.keyvault.keys import KeyClient
441
+ from azure.keyvault.keys.crypto import CryptographyClient
442
+
443
+ credential = self._credential or DefaultAzureCredential()
444
+ self._client = KeyClient(
445
+ vault_url=self._vault_url,
446
+ credential=credential,
447
+ )
448
+ key = self._client.get_key(self._key_name)
449
+ self._crypto_client = CryptographyClient(key, credential=credential)
450
+ except ImportError:
451
+ raise EncryptionError("azure-keyvault-keys not installed")
452
+
453
+ return self._client, self._crypto_client
454
+
455
+ def get_key(self, key_id: str) -> bytes:
456
+ """Generate data key."""
457
+ return os.urandom(32)
458
+
459
+ def encrypt_data_key(self, data_key: bytes, key_id: str) -> bytes:
460
+ """Encrypt data key with Azure Key Vault."""
461
+ try:
462
+ from azure.keyvault.keys.crypto import EncryptionAlgorithm as AzureAlgorithm
463
+
464
+ _, crypto_client = self._get_clients()
465
+ result = crypto_client.encrypt(AzureAlgorithm.rsa_oaep, data_key)
466
+ return result.ciphertext
467
+ except Exception as e:
468
+ raise EncryptionError(f"Azure Key Vault encrypt failed: {e}")
469
+
470
+ def decrypt_data_key(self, encrypted_key: bytes, key_id: str) -> bytes:
471
+ """Decrypt data key with Azure Key Vault."""
472
+ try:
473
+ from azure.keyvault.keys.crypto import EncryptionAlgorithm as AzureAlgorithm
474
+
475
+ _, crypto_client = self._get_clients()
476
+ result = crypto_client.decrypt(AzureAlgorithm.rsa_oaep, encrypted_key)
477
+ return result.plaintext
478
+ except Exception as e:
479
+ raise DecryptionError(f"Azure Key Vault decrypt failed: {e}")
480
+
481
+
482
+ # =============================================================================
483
+ # Local Key Provider
484
+ # =============================================================================
485
+
486
+
487
+ class LocalKeyProvider(KeyProvider):
488
+ """Local key provider for development/testing.
489
+
490
+ Stores keys locally using password-based encryption.
491
+ NOT recommended for production use.
492
+ """
493
+
494
+ def __init__(
495
+ self,
496
+ key_file: str | Path = ".truthound_keys",
497
+ *,
498
+ master_password: str | None = None,
499
+ ) -> None:
500
+ """Initialize local key provider.
501
+
502
+ Args:
503
+ key_file: Path to key storage file.
504
+ master_password: Master password (or TRUTHOUND_MASTER_KEY env).
505
+ """
506
+ self._key_file = Path(key_file)
507
+ self._master_password = master_password or os.getenv("TRUTHOUND_MASTER_KEY", "")
508
+ self._keys: dict[str, bytes] = {}
509
+ self._lock = threading.Lock()
510
+ self._load_keys()
511
+
512
+ def _load_keys(self) -> None:
513
+ """Load keys from file."""
514
+ if not self._key_file.exists():
515
+ return
516
+
517
+ try:
518
+ content = self._key_file.read_bytes()
519
+ # Simple XOR with master key hash for obfuscation (not secure!)
520
+ key_hash = hashlib.sha256(self._master_password.encode()).digest()
521
+ decrypted = bytes(b ^ key_hash[i % 32] for i, b in enumerate(content))
522
+ self._keys = json.loads(decrypted.decode("utf-8"))
523
+ # Convert hex strings back to bytes
524
+ self._keys = {k: bytes.fromhex(v) for k, v in self._keys.items()}
525
+ except Exception:
526
+ self._keys = {}
527
+
528
+ def _save_keys(self) -> None:
529
+ """Save keys to file."""
530
+ try:
531
+ # Convert bytes to hex for JSON
532
+ keys_hex = {k: v.hex() for k, v in self._keys.items()}
533
+ content = json.dumps(keys_hex).encode("utf-8")
534
+ # Simple XOR with master key hash
535
+ key_hash = hashlib.sha256(self._master_password.encode()).digest()
536
+ encrypted = bytes(b ^ key_hash[i % 32] for i, b in enumerate(content))
537
+ self._key_file.write_bytes(encrypted)
538
+ except Exception:
539
+ pass
540
+
541
+ def get_key(self, key_id: str) -> bytes:
542
+ """Get or generate key."""
543
+ with self._lock:
544
+ if key_id not in self._keys:
545
+ self._keys[key_id] = os.urandom(32)
546
+ self._save_keys()
547
+ return self._keys[key_id]
548
+
549
+ def encrypt_data_key(self, data_key: bytes, key_id: str) -> bytes:
550
+ """Encrypt data key with master key."""
551
+ master_key = self.get_key(key_id)
552
+ encryptor = AesGcmEncryptor()
553
+ return encryptor.encrypt(data_key, master_key)
554
+
555
+ def decrypt_data_key(self, encrypted_key: bytes, key_id: str) -> bytes:
556
+ """Decrypt data key with master key."""
557
+ master_key = self.get_key(key_id)
558
+ encryptor = AesGcmEncryptor()
559
+ return encryptor.decrypt(encrypted_key, master_key)
560
+
561
+
562
+ # =============================================================================
563
+ # At-Rest Encryption
564
+ # =============================================================================
565
+
566
+
567
+ @dataclass
568
+ class EncryptedData:
569
+ """Encrypted data with metadata.
570
+
571
+ Attributes:
572
+ ciphertext: Encrypted data.
573
+ encrypted_key: Wrapped data encryption key.
574
+ key_id: Master key ID used for wrapping.
575
+ algorithm: Encryption algorithm used.
576
+ nonce: Nonce/IV used for encryption.
577
+ timestamp: Encryption timestamp.
578
+ metadata: Additional metadata.
579
+ """
580
+
581
+ ciphertext: bytes
582
+ encrypted_key: bytes
583
+ key_id: str
584
+ algorithm: str = "AES-256-GCM"
585
+ nonce: bytes = b""
586
+ timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
587
+ metadata: dict[str, Any] = field(default_factory=dict)
588
+
589
+ def to_bytes(self) -> bytes:
590
+ """Serialize to bytes."""
591
+ header = {
592
+ "key_id": self.key_id,
593
+ "algorithm": self.algorithm,
594
+ "timestamp": self.timestamp.isoformat(),
595
+ "metadata": self.metadata,
596
+ }
597
+ header_json = json.dumps(header).encode("utf-8")
598
+
599
+ # Format: header_len (4) | header | nonce_len (1) | nonce | key_len (2) | encrypted_key | ciphertext
600
+ parts = [
601
+ struct.pack(">I", len(header_json)),
602
+ header_json,
603
+ struct.pack(">B", len(self.nonce)),
604
+ self.nonce,
605
+ struct.pack(">H", len(self.encrypted_key)),
606
+ self.encrypted_key,
607
+ self.ciphertext,
608
+ ]
609
+ return b"".join(parts)
610
+
611
+ @classmethod
612
+ def from_bytes(cls, data: bytes) -> "EncryptedData":
613
+ """Deserialize from bytes."""
614
+ offset = 0
615
+
616
+ # Read header
617
+ header_len = struct.unpack(">I", data[offset : offset + 4])[0]
618
+ offset += 4
619
+ header = json.loads(data[offset : offset + header_len].decode("utf-8"))
620
+ offset += header_len
621
+
622
+ # Read nonce
623
+ nonce_len = struct.unpack(">B", data[offset : offset + 1])[0]
624
+ offset += 1
625
+ nonce = data[offset : offset + nonce_len]
626
+ offset += nonce_len
627
+
628
+ # Read encrypted key
629
+ key_len = struct.unpack(">H", data[offset : offset + 2])[0]
630
+ offset += 2
631
+ encrypted_key = data[offset : offset + key_len]
632
+ offset += key_len
633
+
634
+ # Rest is ciphertext
635
+ ciphertext = data[offset:]
636
+
637
+ return cls(
638
+ ciphertext=ciphertext,
639
+ encrypted_key=encrypted_key,
640
+ key_id=header["key_id"],
641
+ algorithm=header["algorithm"],
642
+ nonce=nonce,
643
+ timestamp=datetime.fromisoformat(header["timestamp"]),
644
+ metadata=header.get("metadata", {}),
645
+ )
646
+
647
+
648
+ class AtRestEncryption:
649
+ """At-rest encryption for data files.
650
+
651
+ Provides envelope encryption using a cloud KMS or local key provider.
652
+ Data is encrypted with a unique data encryption key (DEK), which is
653
+ then wrapped with a key encryption key (KEK) from the provider.
654
+
655
+ Example:
656
+ >>> encryptor = AtRestEncryption(
657
+ ... provider=AwsKmsProvider("alias/my-key"),
658
+ ... key_id="alias/my-key",
659
+ ... )
660
+ >>>
661
+ >>> # Encrypt
662
+ >>> encrypted = encryptor.encrypt(b"sensitive data")
663
+ >>>
664
+ >>> # Decrypt
665
+ >>> decrypted = encryptor.decrypt(encrypted)
666
+ """
667
+
668
+ def __init__(
669
+ self,
670
+ provider: KeyProvider,
671
+ key_id: str,
672
+ *,
673
+ algorithm: EncryptionAlgorithm = EncryptionAlgorithm.AES_256_GCM,
674
+ ) -> None:
675
+ """Initialize at-rest encryption.
676
+
677
+ Args:
678
+ provider: Key provider.
679
+ key_id: Default key ID for encryption.
680
+ algorithm: Encryption algorithm.
681
+ """
682
+ self._provider = provider
683
+ self._key_id = key_id
684
+ self._algorithm = algorithm
685
+
686
+ # Get encryptor for the algorithm
687
+ if algorithm == EncryptionAlgorithm.AES_256_GCM:
688
+ self._encryptor = AesGcmEncryptor()
689
+ elif algorithm == EncryptionAlgorithm.CHACHA20_POLY1305:
690
+ self._encryptor = ChaCha20Poly1305Encryptor()
691
+ else:
692
+ self._encryptor = AesGcmEncryptor()
693
+
694
+ def encrypt(
695
+ self,
696
+ data: bytes,
697
+ *,
698
+ key_id: str | None = None,
699
+ metadata: dict[str, Any] | None = None,
700
+ ) -> EncryptedData:
701
+ """Encrypt data.
702
+
703
+ Args:
704
+ data: Data to encrypt.
705
+ key_id: Key ID (uses default if None).
706
+ metadata: Additional metadata.
707
+
708
+ Returns:
709
+ EncryptedData with ciphertext and wrapped key.
710
+ """
711
+ key_id = key_id or self._key_id
712
+
713
+ # Generate data key
714
+ plain_key, encrypted_key = self._provider.generate_data_key(key_id)
715
+
716
+ # Generate nonce
717
+ nonce = generate_nonce(self._algorithm)
718
+
719
+ # Encrypt data
720
+ ciphertext = self._encryptor.encrypt(data, plain_key, nonce)
721
+
722
+ # Clear plain key from memory
723
+ plain_key = b"\x00" * len(plain_key)
724
+
725
+ return EncryptedData(
726
+ ciphertext=ciphertext,
727
+ encrypted_key=encrypted_key,
728
+ key_id=key_id,
729
+ algorithm=self._algorithm.value,
730
+ nonce=nonce,
731
+ metadata=metadata or {},
732
+ )
733
+
734
+ def decrypt(self, encrypted: EncryptedData) -> bytes:
735
+ """Decrypt data.
736
+
737
+ Args:
738
+ encrypted: Encrypted data.
739
+
740
+ Returns:
741
+ Decrypted data.
742
+ """
743
+ # Decrypt data key
744
+ plain_key = self._provider.decrypt_data_key(
745
+ encrypted.encrypted_key,
746
+ encrypted.key_id,
747
+ )
748
+
749
+ # Decrypt data
750
+ try:
751
+ return self._encryptor.decrypt(encrypted.ciphertext, plain_key)
752
+ finally:
753
+ # Clear plain key from memory
754
+ plain_key = b"\x00" * len(plain_key)
755
+
756
+ def encrypt_file(
757
+ self,
758
+ input_path: str | Path,
759
+ output_path: str | Path,
760
+ **kwargs: Any,
761
+ ) -> None:
762
+ """Encrypt a file.
763
+
764
+ Args:
765
+ input_path: Input file path.
766
+ output_path: Output file path.
767
+ **kwargs: Additional arguments for encrypt().
768
+ """
769
+ data = Path(input_path).read_bytes()
770
+ encrypted = self.encrypt(data, **kwargs)
771
+ Path(output_path).write_bytes(encrypted.to_bytes())
772
+
773
+ def decrypt_file(
774
+ self,
775
+ input_path: str | Path,
776
+ output_path: str | Path,
777
+ ) -> None:
778
+ """Decrypt a file.
779
+
780
+ Args:
781
+ input_path: Encrypted file path.
782
+ output_path: Output file path.
783
+ """
784
+ data = Path(input_path).read_bytes()
785
+ encrypted = EncryptedData.from_bytes(data)
786
+ decrypted = self.decrypt(encrypted)
787
+ Path(output_path).write_bytes(decrypted)
788
+
789
+
790
+ # =============================================================================
791
+ # Field-Level Encryption
792
+ # =============================================================================
793
+
794
+
795
+ @dataclass
796
+ class FieldEncryptionPolicy:
797
+ """Encryption policy for a field/column.
798
+
799
+ Attributes:
800
+ algorithm: Encryption algorithm.
801
+ key_id: Key ID to use.
802
+ format_preserving: Use format-preserving encryption.
803
+ deterministic: Use deterministic encryption (for searching).
804
+ mask_format: Masking format for display.
805
+ """
806
+
807
+ algorithm: str = "aes_gcm" # aes_gcm, chacha20, format_preserving
808
+ key_id: str = ""
809
+ format_preserving: bool = False
810
+ deterministic: bool = False
811
+ mask_format: str = "" # e.g., "***-**-{last4}" for SSN
812
+
813
+
814
+ class FieldLevelEncryption:
815
+ """Field-level encryption for sensitive columns.
816
+
817
+ Encrypts individual fields/columns based on policies.
818
+ Supports format-preserving encryption for data that must
819
+ maintain its format (e.g., credit card numbers).
820
+
821
+ Example:
822
+ >>> fle = FieldLevelEncryption(
823
+ ... provider=AwsKmsProvider("alias/my-key"),
824
+ ... policies={
825
+ ... "ssn": FieldEncryptionPolicy(format_preserving=True),
826
+ ... "email": FieldEncryptionPolicy(algorithm="aes_gcm"),
827
+ ... },
828
+ ... )
829
+ >>>
830
+ >>> # Encrypt field
831
+ >>> encrypted_ssn = fle.encrypt_field("ssn", "123-45-6789")
832
+ >>>
833
+ >>> # Decrypt field
834
+ >>> ssn = fle.decrypt_field("ssn", encrypted_ssn)
835
+ """
836
+
837
+ def __init__(
838
+ self,
839
+ provider: KeyProvider,
840
+ policies: dict[str, FieldEncryptionPolicy] | None = None,
841
+ *,
842
+ default_key_id: str = "",
843
+ ) -> None:
844
+ """Initialize field-level encryption.
845
+
846
+ Args:
847
+ provider: Key provider.
848
+ policies: Field encryption policies.
849
+ default_key_id: Default key ID.
850
+ """
851
+ self._provider = provider
852
+ self._policies = policies or {}
853
+ self._default_key_id = default_key_id
854
+ self._encryptor = AesGcmEncryptor()
855
+
856
+ def add_policy(self, field_name: str, policy: FieldEncryptionPolicy) -> None:
857
+ """Add encryption policy for a field.
858
+
859
+ Args:
860
+ field_name: Field name.
861
+ policy: Encryption policy.
862
+ """
863
+ self._policies[field_name] = policy
864
+
865
+ def encrypt_field(self, field_name: str, value: str) -> str:
866
+ """Encrypt a field value.
867
+
868
+ Args:
869
+ field_name: Field name.
870
+ value: Value to encrypt.
871
+
872
+ Returns:
873
+ Encrypted value (base64 encoded).
874
+ """
875
+ policy = self._policies.get(field_name, FieldEncryptionPolicy())
876
+ key_id = policy.key_id or self._default_key_id
877
+
878
+ if policy.format_preserving:
879
+ return self._format_preserving_encrypt(value, key_id)
880
+
881
+ # Get key
882
+ key = self._provider.get_key(key_id)
883
+
884
+ if policy.deterministic:
885
+ # Use fixed nonce derived from value hash (for searching)
886
+ nonce = hashlib.sha256(value.encode()).digest()[:12]
887
+ else:
888
+ nonce = generate_nonce(EncryptionAlgorithm.AES_256_GCM)
889
+
890
+ # Encrypt
891
+ ciphertext = self._encryptor.encrypt(value.encode("utf-8"), key, nonce)
892
+
893
+ # Encode as base64
894
+ return base64.b64encode(nonce + ciphertext).decode("utf-8")
895
+
896
+ def decrypt_field(self, field_name: str, encrypted_value: str) -> str:
897
+ """Decrypt a field value.
898
+
899
+ Args:
900
+ field_name: Field name.
901
+ encrypted_value: Encrypted value (base64 encoded).
902
+
903
+ Returns:
904
+ Decrypted value.
905
+ """
906
+ policy = self._policies.get(field_name, FieldEncryptionPolicy())
907
+ key_id = policy.key_id or self._default_key_id
908
+
909
+ if policy.format_preserving:
910
+ return self._format_preserving_decrypt(encrypted_value, key_id)
911
+
912
+ # Decode base64
913
+ data = base64.b64decode(encrypted_value)
914
+ nonce = data[:12]
915
+ ciphertext = data[12:]
916
+
917
+ # Get key
918
+ key = self._provider.get_key(key_id)
919
+
920
+ # Decrypt
921
+ plaintext = self._encryptor.decrypt(ciphertext, key)
922
+ return plaintext.decode("utf-8")
923
+
924
+ def _format_preserving_encrypt(self, value: str, key_id: str) -> str:
925
+ """Format-preserving encryption (simplified).
926
+
927
+ This is a simplified FPE implementation. For production,
928
+ use a proper FPE library like python-ff3.
929
+ """
930
+ # Get key
931
+ key = self._provider.get_key(key_id)
932
+
933
+ # Simple FPE: XOR with key-derived stream
934
+ key_stream = hashlib.sha256(key + value.encode()).digest()
935
+ result = []
936
+
937
+ for i, char in enumerate(value):
938
+ if char.isdigit():
939
+ offset = key_stream[i % 32] % 10
940
+ new_digit = (int(char) + offset) % 10
941
+ result.append(str(new_digit))
942
+ elif char.isalpha():
943
+ offset = key_stream[i % 32] % 26
944
+ if char.isupper():
945
+ new_char = chr((ord(char) - ord("A") + offset) % 26 + ord("A"))
946
+ else:
947
+ new_char = chr((ord(char) - ord("a") + offset) % 26 + ord("a"))
948
+ result.append(new_char)
949
+ else:
950
+ result.append(char)
951
+
952
+ return "".join(result)
953
+
954
+ def _format_preserving_decrypt(self, value: str, key_id: str) -> str:
955
+ """Format-preserving decryption."""
956
+ key = self._provider.get_key(key_id)
957
+
958
+ # Reverse the FPE
959
+ key_stream = hashlib.sha256(key + value.encode()).digest()
960
+ result = []
961
+
962
+ for i, char in enumerate(value):
963
+ if char.isdigit():
964
+ offset = key_stream[i % 32] % 10
965
+ new_digit = (int(char) - offset) % 10
966
+ result.append(str(new_digit))
967
+ elif char.isalpha():
968
+ offset = key_stream[i % 32] % 26
969
+ if char.isupper():
970
+ new_char = chr((ord(char) - ord("A") - offset) % 26 + ord("A"))
971
+ else:
972
+ new_char = chr((ord(char) - ord("a") - offset) % 26 + ord("a"))
973
+ result.append(new_char)
974
+ else:
975
+ result.append(char)
976
+
977
+ return "".join(result)
978
+
979
+ def mask_field(self, field_name: str, value: str) -> str:
980
+ """Mask a field value for display.
981
+
982
+ Args:
983
+ field_name: Field name.
984
+ value: Value to mask.
985
+
986
+ Returns:
987
+ Masked value.
988
+ """
989
+ policy = self._policies.get(field_name, FieldEncryptionPolicy())
990
+
991
+ if policy.mask_format:
992
+ # Apply mask format
993
+ if "{last4}" in policy.mask_format:
994
+ last4 = value[-4:] if len(value) >= 4 else value
995
+ return policy.mask_format.replace("{last4}", last4)
996
+ return policy.mask_format
997
+
998
+ # Default masking
999
+ if len(value) <= 4:
1000
+ return "*" * len(value)
1001
+ return value[:2] + "*" * (len(value) - 4) + value[-2:]
1002
+
1003
+
1004
+ # =============================================================================
1005
+ # Encryption Configuration
1006
+ # =============================================================================
1007
+
1008
+
1009
+ @dataclass
1010
+ class EnterpriseEncryptionConfig:
1011
+ """Enterprise encryption configuration.
1012
+
1013
+ Example:
1014
+ >>> config = EnterpriseEncryptionConfig(
1015
+ ... provider="aws_kms",
1016
+ ... key_id="alias/truthound-data-key",
1017
+ ... region="us-east-1",
1018
+ ... )
1019
+ """
1020
+
1021
+ enabled: bool = True
1022
+ provider: str = "local" # local, vault, aws_kms, gcp_kms, azure_keyvault
1023
+
1024
+ # Provider-specific settings
1025
+ key_id: str = ""
1026
+
1027
+ # Vault settings
1028
+ vault_url: str = ""
1029
+ vault_token: str = ""
1030
+ vault_mount_point: str = "transit"
1031
+
1032
+ # AWS settings
1033
+ aws_region: str = ""
1034
+
1035
+ # GCP settings
1036
+ gcp_project_id: str = ""
1037
+ gcp_location: str = "global"
1038
+ gcp_key_ring: str = "truthound"
1039
+
1040
+ # Azure settings
1041
+ azure_vault_url: str = ""
1042
+
1043
+ # Local settings
1044
+ local_key_file: str = ".truthound_keys"
1045
+ local_master_password: str = ""
1046
+
1047
+ # Field-level encryption
1048
+ field_policies: dict[str, dict[str, Any]] = field(default_factory=dict)
1049
+
1050
+
1051
+ # =============================================================================
1052
+ # Global Encryption
1053
+ # =============================================================================
1054
+
1055
+ _global_provider: KeyProvider | None = None
1056
+ _global_encryptor: AtRestEncryption | None = None
1057
+ _lock = threading.Lock()
1058
+
1059
+
1060
+ def configure_encryption(
1061
+ *,
1062
+ provider: str = "local",
1063
+ key_id: str = "",
1064
+ vault_url: str = "",
1065
+ aws_region: str = "",
1066
+ gcp_project_id: str = "",
1067
+ azure_vault_url: str = "",
1068
+ **kwargs: Any,
1069
+ ) -> AtRestEncryption:
1070
+ """Configure global encryption.
1071
+
1072
+ Args:
1073
+ provider: Key provider type.
1074
+ key_id: Default key ID.
1075
+ vault_url: Vault URL (if using Vault).
1076
+ aws_region: AWS region (if using AWS KMS).
1077
+ gcp_project_id: GCP project ID (if using GCP KMS).
1078
+ azure_vault_url: Azure Key Vault URL.
1079
+ **kwargs: Additional provider configuration.
1080
+
1081
+ Returns:
1082
+ Configured AtRestEncryption instance.
1083
+ """
1084
+ global _global_provider, _global_encryptor
1085
+
1086
+ with _lock:
1087
+ # Create provider
1088
+ if provider == "vault":
1089
+ _global_provider = VaultKeyProvider(
1090
+ vault_url,
1091
+ token=kwargs.get("vault_token"),
1092
+ mount_point=kwargs.get("vault_mount_point", "transit"),
1093
+ )
1094
+ elif provider == "aws_kms":
1095
+ _global_provider = AwsKmsProvider(
1096
+ key_id,
1097
+ region=aws_region,
1098
+ )
1099
+ elif provider == "gcp_kms":
1100
+ _global_provider = GcpKmsProvider(
1101
+ key_id,
1102
+ project_id=gcp_project_id,
1103
+ location=kwargs.get("gcp_location", "global"),
1104
+ key_ring=kwargs.get("gcp_key_ring", "truthound"),
1105
+ )
1106
+ elif provider == "azure_keyvault":
1107
+ _global_provider = AzureKeyVaultProvider(
1108
+ azure_vault_url,
1109
+ key_id,
1110
+ )
1111
+ else: # local
1112
+ _global_provider = LocalKeyProvider(
1113
+ key_file=kwargs.get("local_key_file", ".truthound_keys"),
1114
+ master_password=kwargs.get("local_master_password"),
1115
+ )
1116
+
1117
+ _global_encryptor = AtRestEncryption(_global_provider, key_id)
1118
+ return _global_encryptor
1119
+
1120
+
1121
+ def get_encryptor() -> AtRestEncryption:
1122
+ """Get the global encryptor.
1123
+
1124
+ Returns:
1125
+ AtRestEncryption instance.
1126
+ """
1127
+ global _global_encryptor
1128
+
1129
+ with _lock:
1130
+ if _global_encryptor is None:
1131
+ _global_encryptor = configure_encryption(provider="local", key_id="default")
1132
+ return _global_encryptor