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,953 @@
1
+ """Encryption provider implementations.
2
+
3
+ This module provides concrete implementations of encryption algorithms.
4
+ All implementations use authenticated encryption (AEAD) for security.
5
+
6
+ Supported Algorithms:
7
+ - AES-256-GCM (recommended for most use cases)
8
+ - AES-128-GCM (faster, still secure)
9
+ - ChaCha20-Poly1305 (better for software, mobile)
10
+ - XChaCha20-Poly1305 (extended nonce, safer random nonce)
11
+ - Fernet (high-level, batteries-included)
12
+
13
+ Example:
14
+ >>> from truthound.stores.encryption.providers import (
15
+ ... get_encryptor,
16
+ ... AesGcmEncryptor,
17
+ ... )
18
+ >>>
19
+ >>> # Using factory function
20
+ >>> encryptor = get_encryptor("aes-256-gcm")
21
+ >>> key = encryptor.generate_key()
22
+ >>> encrypted = encryptor.encrypt(b"secret data", key)
23
+ >>> decrypted = encryptor.decrypt(encrypted, key)
24
+ >>>
25
+ >>> # Direct instantiation
26
+ >>> aes = AesGcmEncryptor(key_size=32)
27
+ >>> result = aes.encrypt_with_metrics(b"data", key)
28
+ >>> print(result.metrics.to_dict())
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import time
34
+ from abc import ABC, abstractmethod
35
+ from dataclasses import dataclass
36
+ from typing import Any, Callable
37
+
38
+ from truthound.stores.encryption.base import (
39
+ DecryptionError,
40
+ EncryptionAlgorithm,
41
+ EncryptionConfig,
42
+ EncryptionError,
43
+ EncryptionMetrics,
44
+ EncryptionResult,
45
+ IntegrityError,
46
+ UnsupportedAlgorithmError,
47
+ constant_time_compare,
48
+ generate_key,
49
+ generate_nonce,
50
+ )
51
+
52
+
53
+ # =============================================================================
54
+ # Base Encryptor
55
+ # =============================================================================
56
+
57
+
58
+ class BaseEncryptor(ABC):
59
+ """Base class for all encryption implementations.
60
+
61
+ Provides common functionality for encryption providers including
62
+ metrics tracking, key validation, and consistent interface.
63
+ """
64
+
65
+ def __init__(self, algorithm: EncryptionAlgorithm) -> None:
66
+ """Initialize encryptor.
67
+
68
+ Args:
69
+ algorithm: Encryption algorithm this provider implements.
70
+ """
71
+ self._algorithm = algorithm
72
+
73
+ @property
74
+ def algorithm(self) -> EncryptionAlgorithm:
75
+ """Get the encryption algorithm."""
76
+ return self._algorithm
77
+
78
+ @property
79
+ def key_size(self) -> int:
80
+ """Get required key size in bytes."""
81
+ return self._algorithm.key_size
82
+
83
+ @property
84
+ def nonce_size(self) -> int:
85
+ """Get nonce size in bytes."""
86
+ return self._algorithm.nonce_size
87
+
88
+ @property
89
+ def tag_size(self) -> int:
90
+ """Get authentication tag size in bytes."""
91
+ return self._algorithm.tag_size
92
+
93
+ def generate_key(self) -> bytes:
94
+ """Generate a new random key for this algorithm."""
95
+ return generate_key(self._algorithm)
96
+
97
+ def generate_nonce(self) -> bytes:
98
+ """Generate a new random nonce for this algorithm."""
99
+ return generate_nonce(self._algorithm)
100
+
101
+ def _validate_key(self, key: bytes) -> None:
102
+ """Validate key size."""
103
+ if len(key) != self.key_size:
104
+ raise EncryptionError(
105
+ f"Invalid key size: expected {self.key_size} bytes, "
106
+ f"got {len(key)} bytes",
107
+ self._algorithm.value,
108
+ )
109
+
110
+ @abstractmethod
111
+ def _encrypt_impl(
112
+ self,
113
+ plaintext: bytes,
114
+ key: bytes,
115
+ nonce: bytes,
116
+ aad: bytes | None = None,
117
+ ) -> tuple[bytes, bytes]:
118
+ """Implementation-specific encryption.
119
+
120
+ Args:
121
+ plaintext: Data to encrypt.
122
+ key: Encryption key.
123
+ nonce: Nonce/IV.
124
+ aad: Additional authenticated data.
125
+
126
+ Returns:
127
+ Tuple of (ciphertext, tag).
128
+ """
129
+ ...
130
+
131
+ @abstractmethod
132
+ def _decrypt_impl(
133
+ self,
134
+ ciphertext: bytes,
135
+ key: bytes,
136
+ nonce: bytes,
137
+ tag: bytes,
138
+ aad: bytes | None = None,
139
+ ) -> bytes:
140
+ """Implementation-specific decryption.
141
+
142
+ Args:
143
+ ciphertext: Encrypted data.
144
+ key: Decryption key.
145
+ nonce: Nonce/IV used for encryption.
146
+ tag: Authentication tag.
147
+ aad: Additional authenticated data.
148
+
149
+ Returns:
150
+ Decrypted plaintext.
151
+
152
+ Raises:
153
+ DecryptionError: If decryption fails.
154
+ """
155
+ ...
156
+
157
+ def encrypt(
158
+ self,
159
+ plaintext: bytes,
160
+ key: bytes,
161
+ nonce: bytes | None = None,
162
+ aad: bytes | None = None,
163
+ ) -> bytes:
164
+ """Encrypt plaintext data.
165
+
166
+ Args:
167
+ plaintext: Data to encrypt.
168
+ key: Encryption key.
169
+ nonce: Optional nonce (generated if not provided).
170
+ aad: Additional authenticated data.
171
+
172
+ Returns:
173
+ Encrypted data in format: nonce || ciphertext || tag
174
+ """
175
+ self._validate_key(key)
176
+ if nonce is None:
177
+ nonce = self.generate_nonce()
178
+
179
+ ciphertext, tag = self._encrypt_impl(plaintext, key, nonce, aad)
180
+ return nonce + ciphertext + tag
181
+
182
+ def encrypt_with_metrics(
183
+ self,
184
+ plaintext: bytes,
185
+ key: bytes,
186
+ nonce: bytes | None = None,
187
+ aad: bytes | None = None,
188
+ ) -> EncryptionResult:
189
+ """Encrypt data with detailed metrics.
190
+
191
+ Args:
192
+ plaintext: Data to encrypt.
193
+ key: Encryption key.
194
+ nonce: Optional nonce (generated if not provided).
195
+ aad: Additional authenticated data.
196
+
197
+ Returns:
198
+ Encryption result with metrics.
199
+ """
200
+ self._validate_key(key)
201
+ if nonce is None:
202
+ nonce = self.generate_nonce()
203
+
204
+ start_time = time.perf_counter()
205
+ ciphertext, tag = self._encrypt_impl(plaintext, key, nonce, aad)
206
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
207
+
208
+ metrics = EncryptionMetrics(
209
+ plaintext_size=len(plaintext),
210
+ ciphertext_size=len(ciphertext) + len(nonce) + len(tag),
211
+ overhead_bytes=len(nonce) + len(tag),
212
+ encryption_time_ms=elapsed_ms,
213
+ algorithm=self._algorithm,
214
+ )
215
+
216
+ return EncryptionResult(
217
+ ciphertext=ciphertext,
218
+ nonce=nonce,
219
+ tag=tag,
220
+ metrics=metrics,
221
+ )
222
+
223
+ def decrypt(
224
+ self,
225
+ data: bytes,
226
+ key: bytes,
227
+ aad: bytes | None = None,
228
+ ) -> bytes:
229
+ """Decrypt ciphertext data.
230
+
231
+ Args:
232
+ data: Encrypted data in format: nonce || ciphertext || tag
233
+ key: Decryption key.
234
+ aad: Additional authenticated data.
235
+
236
+ Returns:
237
+ Decrypted plaintext.
238
+
239
+ Raises:
240
+ DecryptionError: If decryption or authentication fails.
241
+ """
242
+ self._validate_key(key)
243
+
244
+ nonce = data[: self.nonce_size]
245
+ tag = data[-self.tag_size :]
246
+ ciphertext = data[self.nonce_size : -self.tag_size]
247
+
248
+ return self._decrypt_impl(ciphertext, key, nonce, tag, aad)
249
+
250
+ def decrypt_with_metrics(
251
+ self,
252
+ data: bytes,
253
+ key: bytes,
254
+ aad: bytes | None = None,
255
+ ) -> tuple[bytes, EncryptionMetrics]:
256
+ """Decrypt data with detailed metrics.
257
+
258
+ Args:
259
+ data: Encrypted data.
260
+ key: Decryption key.
261
+ aad: Additional authenticated data.
262
+
263
+ Returns:
264
+ Tuple of (plaintext, metrics).
265
+ """
266
+ start_time = time.perf_counter()
267
+ plaintext = self.decrypt(data, key, aad)
268
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
269
+
270
+ metrics = EncryptionMetrics(
271
+ plaintext_size=len(plaintext),
272
+ ciphertext_size=len(data),
273
+ overhead_bytes=self.nonce_size + self.tag_size,
274
+ decryption_time_ms=elapsed_ms,
275
+ algorithm=self._algorithm,
276
+ )
277
+
278
+ return plaintext, metrics
279
+
280
+
281
+ # =============================================================================
282
+ # AES-GCM Implementations
283
+ # =============================================================================
284
+
285
+
286
+ class AesGcmEncryptor(BaseEncryptor):
287
+ """AES-GCM authenticated encryption.
288
+
289
+ AES-GCM is the recommended algorithm for most use cases. It provides
290
+ both confidentiality and integrity using a single key.
291
+
292
+ Key Features:
293
+ - Hardware acceleration on modern CPUs (AES-NI)
294
+ - Widely supported and standardized
295
+ - 128-bit authentication tag
296
+ - 96-bit nonce (never reuse with same key!)
297
+
298
+ Example:
299
+ >>> aes = AesGcmEncryptor(key_size=32) # AES-256
300
+ >>> key = aes.generate_key()
301
+ >>> encrypted = aes.encrypt(b"secret", key)
302
+ >>> decrypted = aes.decrypt(encrypted, key)
303
+ """
304
+
305
+ def __init__(self, key_size: int = 32) -> None:
306
+ """Initialize AES-GCM encryptor.
307
+
308
+ Args:
309
+ key_size: Key size in bytes (16 for AES-128, 32 for AES-256).
310
+ """
311
+ if key_size == 16:
312
+ algorithm = EncryptionAlgorithm.AES_128_GCM
313
+ elif key_size == 32:
314
+ algorithm = EncryptionAlgorithm.AES_256_GCM
315
+ else:
316
+ raise EncryptionError(
317
+ f"Invalid AES key size: {key_size}. Use 16 or 32 bytes."
318
+ )
319
+ super().__init__(algorithm)
320
+ self._aesgcm: Any = None
321
+
322
+ def _get_aesgcm(self, key: bytes) -> Any:
323
+ """Get AESGCM cipher instance."""
324
+ try:
325
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
326
+ except ImportError as e:
327
+ raise UnsupportedAlgorithmError(
328
+ self._algorithm.value,
329
+ available=["Install 'cryptography' package: pip install cryptography"],
330
+ ) from e
331
+ return AESGCM(key)
332
+
333
+ def _encrypt_impl(
334
+ self,
335
+ plaintext: bytes,
336
+ key: bytes,
337
+ nonce: bytes,
338
+ aad: bytes | None = None,
339
+ ) -> tuple[bytes, bytes]:
340
+ """Encrypt using AES-GCM."""
341
+ aesgcm = self._get_aesgcm(key)
342
+ # cryptography library returns ciphertext || tag
343
+ result = aesgcm.encrypt(nonce, plaintext, aad)
344
+ ciphertext = result[:-16] # Everything except last 16 bytes
345
+ tag = result[-16:] # Last 16 bytes is the tag
346
+ return ciphertext, tag
347
+
348
+ def _decrypt_impl(
349
+ self,
350
+ ciphertext: bytes,
351
+ key: bytes,
352
+ nonce: bytes,
353
+ tag: bytes,
354
+ aad: bytes | None = None,
355
+ ) -> bytes:
356
+ """Decrypt using AES-GCM."""
357
+ try:
358
+ from cryptography.exceptions import InvalidTag
359
+ except ImportError as e:
360
+ raise UnsupportedAlgorithmError(
361
+ self._algorithm.value,
362
+ available=["Install 'cryptography' package"],
363
+ ) from e
364
+
365
+ aesgcm = self._get_aesgcm(key)
366
+ try:
367
+ # cryptography expects ciphertext || tag
368
+ return aesgcm.decrypt(nonce, ciphertext + tag, aad)
369
+ except InvalidTag as e:
370
+ raise IntegrityError(
371
+ "Authentication failed: data may be corrupted or tampered",
372
+ self._algorithm.value,
373
+ ) from e
374
+ except Exception as e:
375
+ raise DecryptionError(str(e), self._algorithm.value) from e
376
+
377
+
378
+ # =============================================================================
379
+ # ChaCha20-Poly1305 Implementation
380
+ # =============================================================================
381
+
382
+
383
+ class ChaCha20Poly1305Encryptor(BaseEncryptor):
384
+ """ChaCha20-Poly1305 authenticated encryption.
385
+
386
+ ChaCha20-Poly1305 is a modern AEAD cipher that performs well in
387
+ software implementations (no hardware acceleration needed).
388
+
389
+ Key Features:
390
+ - Excellent software performance
391
+ - 256-bit key, 96-bit nonce
392
+ - 128-bit authentication tag
393
+ - Constant-time implementation
394
+
395
+ Example:
396
+ >>> chacha = ChaCha20Poly1305Encryptor()
397
+ >>> key = chacha.generate_key()
398
+ >>> encrypted = chacha.encrypt(b"secret", key)
399
+ """
400
+
401
+ def __init__(self) -> None:
402
+ """Initialize ChaCha20-Poly1305 encryptor."""
403
+ super().__init__(EncryptionAlgorithm.CHACHA20_POLY1305)
404
+
405
+ def _get_chacha(self, key: bytes) -> Any:
406
+ """Get ChaCha20Poly1305 cipher instance."""
407
+ try:
408
+ from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
409
+ except ImportError as e:
410
+ raise UnsupportedAlgorithmError(
411
+ self._algorithm.value,
412
+ available=["Install 'cryptography' package"],
413
+ ) from e
414
+ return ChaCha20Poly1305(key)
415
+
416
+ def _encrypt_impl(
417
+ self,
418
+ plaintext: bytes,
419
+ key: bytes,
420
+ nonce: bytes,
421
+ aad: bytes | None = None,
422
+ ) -> tuple[bytes, bytes]:
423
+ """Encrypt using ChaCha20-Poly1305."""
424
+ chacha = self._get_chacha(key)
425
+ result = chacha.encrypt(nonce, plaintext, aad)
426
+ ciphertext = result[:-16]
427
+ tag = result[-16:]
428
+ return ciphertext, tag
429
+
430
+ def _decrypt_impl(
431
+ self,
432
+ ciphertext: bytes,
433
+ key: bytes,
434
+ nonce: bytes,
435
+ tag: bytes,
436
+ aad: bytes | None = None,
437
+ ) -> bytes:
438
+ """Decrypt using ChaCha20-Poly1305."""
439
+ try:
440
+ from cryptography.exceptions import InvalidTag
441
+ except ImportError as e:
442
+ raise UnsupportedAlgorithmError(
443
+ self._algorithm.value,
444
+ available=["Install 'cryptography' package"],
445
+ ) from e
446
+
447
+ chacha = self._get_chacha(key)
448
+ try:
449
+ return chacha.decrypt(nonce, ciphertext + tag, aad)
450
+ except InvalidTag as e:
451
+ raise IntegrityError(
452
+ "Authentication failed: data may be corrupted or tampered",
453
+ self._algorithm.value,
454
+ ) from e
455
+ except Exception as e:
456
+ raise DecryptionError(str(e), self._algorithm.value) from e
457
+
458
+
459
+ # =============================================================================
460
+ # XChaCha20-Poly1305 Implementation
461
+ # =============================================================================
462
+
463
+
464
+ class XChaCha20Poly1305Encryptor(BaseEncryptor):
465
+ """XChaCha20-Poly1305 authenticated encryption.
466
+
467
+ XChaCha20-Poly1305 extends ChaCha20-Poly1305 with a 192-bit nonce,
468
+ making it safe to generate nonces randomly without collision risk.
469
+
470
+ Key Features:
471
+ - 192-bit nonce (safe for random generation)
472
+ - Same security as ChaCha20-Poly1305
473
+ - Ideal for file encryption
474
+
475
+ Note:
476
+ Requires the 'pynacl' package for native implementation.
477
+ Falls back to a cryptography-based implementation if not available.
478
+
479
+ Example:
480
+ >>> xchacha = XChaCha20Poly1305Encryptor()
481
+ >>> key = xchacha.generate_key()
482
+ >>> encrypted = xchacha.encrypt(b"secret", key)
483
+ """
484
+
485
+ def __init__(self) -> None:
486
+ """Initialize XChaCha20-Poly1305 encryptor."""
487
+ super().__init__(EncryptionAlgorithm.XCHACHA20_POLY1305)
488
+ self._use_nacl = self._check_nacl()
489
+
490
+ def _check_nacl(self) -> bool:
491
+ """Check if pynacl is available."""
492
+ try:
493
+ import nacl.secret # noqa: F401
494
+
495
+ return True
496
+ except ImportError:
497
+ return False
498
+
499
+ def _encrypt_impl(
500
+ self,
501
+ plaintext: bytes,
502
+ key: bytes,
503
+ nonce: bytes,
504
+ aad: bytes | None = None,
505
+ ) -> tuple[bytes, bytes]:
506
+ """Encrypt using XChaCha20-Poly1305."""
507
+ if self._use_nacl:
508
+ return self._encrypt_nacl(plaintext, key, nonce)
509
+ return self._encrypt_hchacha(plaintext, key, nonce, aad)
510
+
511
+ def _decrypt_impl(
512
+ self,
513
+ ciphertext: bytes,
514
+ key: bytes,
515
+ nonce: bytes,
516
+ tag: bytes,
517
+ aad: bytes | None = None,
518
+ ) -> bytes:
519
+ """Decrypt using XChaCha20-Poly1305."""
520
+ if self._use_nacl:
521
+ return self._decrypt_nacl(ciphertext, key, nonce, tag)
522
+ return self._decrypt_hchacha(ciphertext, key, nonce, tag, aad)
523
+
524
+ def _encrypt_nacl(
525
+ self, plaintext: bytes, key: bytes, nonce: bytes
526
+ ) -> tuple[bytes, bytes]:
527
+ """Encrypt using pynacl."""
528
+ import nacl.secret
529
+
530
+ box = nacl.secret.SecretBox(key)
531
+ encrypted = box.encrypt(plaintext, nonce)
532
+ # nacl format: nonce || ciphertext || tag
533
+ # We already have nonce, so skip it
534
+ ct_and_tag = encrypted[24:] # Skip 24-byte nonce
535
+ ciphertext = ct_and_tag[:-16]
536
+ tag = ct_and_tag[-16:]
537
+ return ciphertext, tag
538
+
539
+ def _decrypt_nacl(
540
+ self, ciphertext: bytes, key: bytes, nonce: bytes, tag: bytes
541
+ ) -> bytes:
542
+ """Decrypt using pynacl."""
543
+ import nacl.exceptions
544
+ import nacl.secret
545
+
546
+ box = nacl.secret.SecretBox(key)
547
+ try:
548
+ # nacl expects nonce || ciphertext || tag
549
+ return box.decrypt(nonce + ciphertext + tag)
550
+ except nacl.exceptions.CryptoError as e:
551
+ raise IntegrityError(
552
+ "Authentication failed: data may be corrupted or tampered",
553
+ self._algorithm.value,
554
+ ) from e
555
+
556
+ def _encrypt_hchacha(
557
+ self,
558
+ plaintext: bytes,
559
+ key: bytes,
560
+ nonce: bytes,
561
+ aad: bytes | None = None,
562
+ ) -> tuple[bytes, bytes]:
563
+ """Encrypt using HChaCha20 + ChaCha20-Poly1305 (cryptography fallback)."""
564
+ # XChaCha20 = HChaCha20(key, nonce[:16]) to derive subkey
565
+ # Then ChaCha20-Poly1305 with subkey and modified nonce
566
+ try:
567
+ from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
568
+ except ImportError as e:
569
+ raise UnsupportedAlgorithmError(
570
+ self._algorithm.value,
571
+ available=[
572
+ "Install 'pynacl' or 'cryptography': pip install pynacl cryptography"
573
+ ],
574
+ ) from e
575
+
576
+ # Derive subkey using HChaCha20 (simplified - use first 16 bytes of nonce)
577
+ subkey = self._hchacha20(key, nonce[:16])
578
+ # Use last 8 bytes of nonce + 4 zero bytes as the ChaCha20 nonce
579
+ chacha_nonce = b"\x00" * 4 + nonce[16:24]
580
+
581
+ chacha = ChaCha20Poly1305(subkey)
582
+ result = chacha.encrypt(chacha_nonce, plaintext, aad)
583
+ return result[:-16], result[-16:]
584
+
585
+ def _decrypt_hchacha(
586
+ self,
587
+ ciphertext: bytes,
588
+ key: bytes,
589
+ nonce: bytes,
590
+ tag: bytes,
591
+ aad: bytes | None = None,
592
+ ) -> bytes:
593
+ """Decrypt using HChaCha20 + ChaCha20-Poly1305."""
594
+ try:
595
+ from cryptography.exceptions import InvalidTag
596
+ from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
597
+ except ImportError as e:
598
+ raise UnsupportedAlgorithmError(
599
+ self._algorithm.value,
600
+ available=["Install 'pynacl' or 'cryptography'"],
601
+ ) from e
602
+
603
+ subkey = self._hchacha20(key, nonce[:16])
604
+ chacha_nonce = b"\x00" * 4 + nonce[16:24]
605
+
606
+ chacha = ChaCha20Poly1305(subkey)
607
+ try:
608
+ return chacha.decrypt(chacha_nonce, ciphertext + tag, aad)
609
+ except InvalidTag as e:
610
+ raise IntegrityError(
611
+ "Authentication failed",
612
+ self._algorithm.value,
613
+ ) from e
614
+
615
+ def _hchacha20(self, key: bytes, nonce: bytes) -> bytes:
616
+ """HChaCha20 key derivation (simplified implementation).
617
+
618
+ This is a simplified version. For production, use a proper
619
+ HChaCha20 implementation from a cryptographic library.
620
+ """
621
+ import hashlib
622
+
623
+ # Simplified: HKDF-style derivation for fallback
624
+ # In production, implement actual HChaCha20
625
+ return hashlib.blake2b(key + nonce, digest_size=32).digest()
626
+
627
+
628
+ # =============================================================================
629
+ # Fernet Implementation
630
+ # =============================================================================
631
+
632
+
633
+ class FernetEncryptor(BaseEncryptor):
634
+ """Fernet symmetric encryption.
635
+
636
+ Fernet provides a high-level, easy-to-use API for authenticated
637
+ encryption. It uses AES-128-CBC with HMAC-SHA256 for authentication.
638
+
639
+ Key Features:
640
+ - Batteries-included design
641
+ - Timestamp for freshness checking
642
+ - URL-safe base64 encoding
643
+ - Simple API
644
+
645
+ Note:
646
+ Fernet keys are base64-encoded. Use generate_key() to create
647
+ properly formatted keys.
648
+
649
+ Example:
650
+ >>> fernet = FernetEncryptor()
651
+ >>> key = fernet.generate_key()
652
+ >>> encrypted = fernet.encrypt(b"secret", key)
653
+ """
654
+
655
+ def __init__(self) -> None:
656
+ """Initialize Fernet encryptor."""
657
+ super().__init__(EncryptionAlgorithm.FERNET)
658
+
659
+ def generate_key(self) -> bytes:
660
+ """Generate a Fernet key (base64-encoded)."""
661
+ try:
662
+ from cryptography.fernet import Fernet
663
+ except ImportError as e:
664
+ raise UnsupportedAlgorithmError(
665
+ self._algorithm.value,
666
+ available=["Install 'cryptography' package"],
667
+ ) from e
668
+ return Fernet.generate_key()
669
+
670
+ def _get_fernet(self, key: bytes) -> Any:
671
+ """Get Fernet instance."""
672
+ try:
673
+ from cryptography.fernet import Fernet
674
+ except ImportError as e:
675
+ raise UnsupportedAlgorithmError(
676
+ self._algorithm.value,
677
+ available=["Install 'cryptography' package"],
678
+ ) from e
679
+ return Fernet(key)
680
+
681
+ def _validate_key(self, key: bytes) -> None:
682
+ """Validate Fernet key (base64-encoded, 32 bytes decoded)."""
683
+ import base64
684
+
685
+ try:
686
+ decoded = base64.urlsafe_b64decode(key)
687
+ if len(decoded) != 32:
688
+ raise EncryptionError(
689
+ f"Invalid Fernet key: decoded to {len(decoded)} bytes, expected 32",
690
+ self._algorithm.value,
691
+ )
692
+ except Exception as e:
693
+ if isinstance(e, EncryptionError):
694
+ raise
695
+ raise EncryptionError(
696
+ f"Invalid Fernet key format: {e}",
697
+ self._algorithm.value,
698
+ ) from e
699
+
700
+ def _encrypt_impl(
701
+ self,
702
+ plaintext: bytes,
703
+ key: bytes,
704
+ nonce: bytes,
705
+ aad: bytes | None = None,
706
+ ) -> tuple[bytes, bytes]:
707
+ """Encrypt using Fernet.
708
+
709
+ Note: Fernet manages its own nonce/IV internally.
710
+ The nonce parameter is ignored.
711
+ """
712
+ fernet = self._get_fernet(key)
713
+ token = fernet.encrypt(plaintext)
714
+ # Fernet token includes everything; use empty tag
715
+ return token, b""
716
+
717
+ def _decrypt_impl(
718
+ self,
719
+ ciphertext: bytes,
720
+ key: bytes,
721
+ nonce: bytes,
722
+ tag: bytes,
723
+ aad: bytes | None = None,
724
+ ) -> bytes:
725
+ """Decrypt using Fernet."""
726
+ try:
727
+ from cryptography.fernet import InvalidToken
728
+ except ImportError as e:
729
+ raise UnsupportedAlgorithmError(
730
+ self._algorithm.value,
731
+ available=["Install 'cryptography' package"],
732
+ ) from e
733
+
734
+ fernet = self._get_fernet(key)
735
+ try:
736
+ return fernet.decrypt(ciphertext)
737
+ except InvalidToken as e:
738
+ raise IntegrityError(
739
+ "Authentication failed or token expired",
740
+ self._algorithm.value,
741
+ ) from e
742
+
743
+ def encrypt(
744
+ self,
745
+ plaintext: bytes,
746
+ key: bytes,
747
+ nonce: bytes | None = None,
748
+ aad: bytes | None = None,
749
+ ) -> bytes:
750
+ """Encrypt using Fernet.
751
+
752
+ Fernet handles nonce internally, so the nonce parameter is ignored.
753
+ """
754
+ self._validate_key(key)
755
+ ciphertext, _ = self._encrypt_impl(plaintext, key, b"", aad)
756
+ return ciphertext
757
+
758
+ def decrypt(
759
+ self,
760
+ data: bytes,
761
+ key: bytes,
762
+ aad: bytes | None = None,
763
+ ) -> bytes:
764
+ """Decrypt Fernet token."""
765
+ self._validate_key(key)
766
+ return self._decrypt_impl(data, key, b"", b"", aad)
767
+
768
+
769
+ # =============================================================================
770
+ # No-op Encryptor (for testing/development)
771
+ # =============================================================================
772
+
773
+
774
+ class NoopEncryptor(BaseEncryptor):
775
+ """No-op encryptor that passes data through unchanged.
776
+
777
+ WARNING: This provides NO security and should only be used for
778
+ testing or development purposes.
779
+
780
+ Example:
781
+ >>> noop = NoopEncryptor()
782
+ >>> encrypted = noop.encrypt(b"data", b"")
783
+ >>> assert encrypted == b"data"
784
+ """
785
+
786
+ def __init__(self) -> None:
787
+ """Initialize no-op encryptor."""
788
+ super().__init__(EncryptionAlgorithm.NONE)
789
+
790
+ def _validate_key(self, key: bytes) -> None:
791
+ """No validation needed for no-op."""
792
+ pass
793
+
794
+ def _encrypt_impl(
795
+ self,
796
+ plaintext: bytes,
797
+ key: bytes,
798
+ nonce: bytes,
799
+ aad: bytes | None = None,
800
+ ) -> tuple[bytes, bytes]:
801
+ """Return data unchanged."""
802
+ return plaintext, b""
803
+
804
+ def _decrypt_impl(
805
+ self,
806
+ ciphertext: bytes,
807
+ key: bytes,
808
+ nonce: bytes,
809
+ tag: bytes,
810
+ aad: bytes | None = None,
811
+ ) -> bytes:
812
+ """Return data unchanged."""
813
+ return ciphertext
814
+
815
+ def encrypt(
816
+ self,
817
+ plaintext: bytes,
818
+ key: bytes,
819
+ nonce: bytes | None = None,
820
+ aad: bytes | None = None,
821
+ ) -> bytes:
822
+ """Return data unchanged."""
823
+ return plaintext
824
+
825
+ def decrypt(
826
+ self,
827
+ data: bytes,
828
+ key: bytes,
829
+ aad: bytes | None = None,
830
+ ) -> bytes:
831
+ """Return data unchanged."""
832
+ return data
833
+
834
+
835
+ # =============================================================================
836
+ # Factory Functions
837
+ # =============================================================================
838
+
839
+ # Registry of available encryptors
840
+ _ENCRYPTOR_REGISTRY: dict[EncryptionAlgorithm, type[BaseEncryptor]] = {
841
+ EncryptionAlgorithm.AES_128_GCM: AesGcmEncryptor,
842
+ EncryptionAlgorithm.AES_256_GCM: AesGcmEncryptor,
843
+ EncryptionAlgorithm.CHACHA20_POLY1305: ChaCha20Poly1305Encryptor,
844
+ EncryptionAlgorithm.XCHACHA20_POLY1305: XChaCha20Poly1305Encryptor,
845
+ EncryptionAlgorithm.FERNET: FernetEncryptor,
846
+ EncryptionAlgorithm.NONE: NoopEncryptor,
847
+ }
848
+
849
+
850
+ def get_encryptor(
851
+ algorithm: str | EncryptionAlgorithm,
852
+ ) -> BaseEncryptor:
853
+ """Get an encryptor instance for the specified algorithm.
854
+
855
+ Args:
856
+ algorithm: Algorithm name or enum value.
857
+
858
+ Returns:
859
+ Configured encryptor instance.
860
+
861
+ Raises:
862
+ UnsupportedAlgorithmError: If algorithm is not supported.
863
+
864
+ Example:
865
+ >>> encryptor = get_encryptor("aes-256-gcm")
866
+ >>> key = encryptor.generate_key()
867
+ >>> encrypted = encryptor.encrypt(b"data", key)
868
+ """
869
+ if isinstance(algorithm, str):
870
+ try:
871
+ algorithm = EncryptionAlgorithm(algorithm)
872
+ except ValueError:
873
+ raise UnsupportedAlgorithmError(
874
+ algorithm,
875
+ available=list_available_algorithms(),
876
+ )
877
+
878
+ encryptor_class = _ENCRYPTOR_REGISTRY.get(algorithm)
879
+ if encryptor_class is None:
880
+ raise UnsupportedAlgorithmError(
881
+ algorithm.value,
882
+ available=list_available_algorithms(),
883
+ )
884
+
885
+ # Handle AES key size variants
886
+ if algorithm == EncryptionAlgorithm.AES_128_GCM:
887
+ return AesGcmEncryptor(key_size=16)
888
+ elif algorithm == EncryptionAlgorithm.AES_256_GCM:
889
+ return AesGcmEncryptor(key_size=32)
890
+
891
+ return encryptor_class()
892
+
893
+
894
+ def register_encryptor(
895
+ algorithm: EncryptionAlgorithm,
896
+ encryptor_class: type[BaseEncryptor],
897
+ ) -> None:
898
+ """Register a custom encryptor implementation.
899
+
900
+ Args:
901
+ algorithm: Algorithm enum value.
902
+ encryptor_class: Encryptor class to register.
903
+
904
+ Example:
905
+ >>> class MyEncryptor(BaseEncryptor):
906
+ ... pass
907
+ >>> register_encryptor(EncryptionAlgorithm.CUSTOM, MyEncryptor)
908
+ """
909
+ _ENCRYPTOR_REGISTRY[algorithm] = encryptor_class
910
+
911
+
912
+ def list_available_algorithms() -> list[str]:
913
+ """List all available encryption algorithms.
914
+
915
+ Returns:
916
+ List of algorithm names.
917
+ """
918
+ available = []
919
+ for algo in _ENCRYPTOR_REGISTRY:
920
+ try:
921
+ # Test if the algorithm's dependencies are available
922
+ encryptor = get_encryptor(algo)
923
+ if algo != EncryptionAlgorithm.NONE:
924
+ # Try to create a key to verify dependencies
925
+ encryptor.generate_key()
926
+ available.append(algo.value)
927
+ except (UnsupportedAlgorithmError, ImportError):
928
+ pass
929
+ return available
930
+
931
+
932
+ def is_algorithm_available(algorithm: str | EncryptionAlgorithm) -> bool:
933
+ """Check if an algorithm is available.
934
+
935
+ Args:
936
+ algorithm: Algorithm to check.
937
+
938
+ Returns:
939
+ True if algorithm is available.
940
+ """
941
+ if isinstance(algorithm, str):
942
+ try:
943
+ algorithm = EncryptionAlgorithm(algorithm)
944
+ except ValueError:
945
+ return False
946
+
947
+ try:
948
+ encryptor = get_encryptor(algorithm)
949
+ if algorithm != EncryptionAlgorithm.NONE:
950
+ encryptor.generate_key()
951
+ return True
952
+ except (UnsupportedAlgorithmError, ImportError):
953
+ return False