llm-redact-proxy 1.0.0__tar.gz

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 (312) hide show
  1. llm_redact_proxy-1.0.0/.claude/settings.json +7 -0
  2. llm_redact_proxy-1.0.0/.claude-plugin/marketplace.json +14 -0
  3. llm_redact_proxy-1.0.0/.dockerignore +12 -0
  4. llm_redact_proxy-1.0.0/.github/ISSUE_TEMPLATE/bug.yml +50 -0
  5. llm_redact_proxy-1.0.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  6. llm_redact_proxy-1.0.0/.github/ISSUE_TEMPLATE/feature.yml +36 -0
  7. llm_redact_proxy-1.0.0/.github/dependabot.yml +12 -0
  8. llm_redact_proxy-1.0.0/.github/pull_request_template.md +15 -0
  9. llm_redact_proxy-1.0.0/.github/workflows/ci.yml +439 -0
  10. llm_redact_proxy-1.0.0/.github/workflows/live.yml +63 -0
  11. llm_redact_proxy-1.0.0/.github/workflows/release.yml +161 -0
  12. llm_redact_proxy-1.0.0/.github/workflows/scorecard.yml +51 -0
  13. llm_redact_proxy-1.0.0/.gitignore +227 -0
  14. llm_redact_proxy-1.0.0/CHANGELOG.md +81 -0
  15. llm_redact_proxy-1.0.0/CLAUDE.md +89 -0
  16. llm_redact_proxy-1.0.0/Dockerfile +51 -0
  17. llm_redact_proxy-1.0.0/Formula/llm-redact.rb +79 -0
  18. llm_redact_proxy-1.0.0/LICENSE +661 -0
  19. llm_redact_proxy-1.0.0/PKG-INFO +526 -0
  20. llm_redact_proxy-1.0.0/README.md +487 -0
  21. llm_redact_proxy-1.0.0/bench/fp_corpus/MANIFEST.toml +109 -0
  22. llm_redact_proxy-1.0.0/bench/fp_corpus/README.md +36 -0
  23. llm_redact_proxy-1.0.0/bench/fp_corpus/gutenberg_prose.txt +3384 -0
  24. llm_redact_proxy-1.0.0/bench/fp_corpus/rfc_excerpt.txt +227 -0
  25. llm_redact_proxy-1.0.0/bench/fp_corpus/sqlite_excerpt.c +2262 -0
  26. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_agentstack.env +15 -0
  27. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_app.log +23 -0
  28. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_bigint.log +11 -0
  29. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_code.py +95 -0
  30. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_config.yaml +67 -0
  31. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_eu_document.txt +49 -0
  32. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_k8s.yaml +81 -0
  33. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_natids_r10.log +28 -0
  34. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_netdebug.log +43 -0
  35. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_runbook.md +49 -0
  36. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_terraform.tf +46 -0
  37. llm_redact_proxy-1.0.0/bench/fp_corpus/synthetic_web3.ts +33 -0
  38. llm_redact_proxy-1.0.0/compose.yaml +33 -0
  39. llm_redact_proxy-1.0.0/config.example.toml +449 -0
  40. llm_redact_proxy-1.0.0/deploy/grafana-dashboard.json +178 -0
  41. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/.helmignore +8 -0
  42. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/Chart.yaml +23 -0
  43. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/NOTES.txt +71 -0
  44. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/_helpers.tpl +172 -0
  45. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/configmap.yaml +9 -0
  46. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/deployment.yaml +50 -0
  47. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/hpa.yaml +32 -0
  48. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/networkpolicy.yaml +31 -0
  49. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/pdb.yaml +13 -0
  50. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/service.yaml +17 -0
  51. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/serviceaccount.yaml +12 -0
  52. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/templates/servicemonitor.yaml +16 -0
  53. llm_redact_proxy-1.0.0/deploy/helm/llm-redact/values.yaml +166 -0
  54. llm_redact_proxy-1.0.0/deploy/k8s-sidecar.yaml +101 -0
  55. llm_redact_proxy-1.0.0/deploy/prometheus-alerts.yml +78 -0
  56. llm_redact_proxy-1.0.0/deploy/prometheus-scrape.yml +22 -0
  57. llm_redact_proxy-1.0.0/docs/CLA.md +61 -0
  58. llm_redact_proxy-1.0.0/docs/CONTRIBUTING.md +144 -0
  59. llm_redact_proxy-1.0.0/docs/README.md +83 -0
  60. llm_redact_proxy-1.0.0/docs/RELEASING.md +104 -0
  61. llm_redact_proxy-1.0.0/docs/SECURITY.md +60 -0
  62. llm_redact_proxy-1.0.0/docs/api-coverage.md +185 -0
  63. llm_redact_proxy-1.0.0/docs/assurance.md +198 -0
  64. llm_redact_proxy-1.0.0/docs/coding-standards.md +126 -0
  65. llm_redact_proxy-1.0.0/docs/compaction-relink.md +64 -0
  66. llm_redact_proxy-1.0.0/docs/dashboard.md +93 -0
  67. llm_redact_proxy-1.0.0/docs/dependencies.md +56 -0
  68. llm_redact_proxy-1.0.0/docs/deployment.md +207 -0
  69. llm_redact_proxy-1.0.0/docs/detection.md +129 -0
  70. llm_redact_proxy-1.0.0/docs/diagrams/architecture.mmd +36 -0
  71. llm_redact_proxy-1.0.0/docs/diagrams/architecture.png +0 -0
  72. llm_redact_proxy-1.0.0/docs/diagrams/security-gates.mmd +42 -0
  73. llm_redact_proxy-1.0.0/docs/diagrams/security-gates.png +0 -0
  74. llm_redact_proxy-1.0.0/docs/diagrams/sequence-chat.mmd +24 -0
  75. llm_redact_proxy-1.0.0/docs/diagrams/sequence-chat.png +0 -0
  76. llm_redact_proxy-1.0.0/docs/diagrams/sequence-streaming.mmd +22 -0
  77. llm_redact_proxy-1.0.0/docs/diagrams/sequence-streaming.png +0 -0
  78. llm_redact_proxy-1.0.0/docs/editions.md +91 -0
  79. llm_redact_proxy-1.0.0/docs/fips.md +68 -0
  80. llm_redact_proxy-1.0.0/docs/history-hygiene.md +53 -0
  81. llm_redact_proxy-1.0.0/docs/how-it-works.md +183 -0
  82. llm_redact_proxy-1.0.0/docs/ner-landscape.md +103 -0
  83. llm_redact_proxy-1.0.0/docs/observability.md +54 -0
  84. llm_redact_proxy-1.0.0/docs/plugins.md +221 -0
  85. llm_redact_proxy-1.0.0/docs/providers.md +137 -0
  86. llm_redact_proxy-1.0.0/docs/quickstart.md +97 -0
  87. llm_redact_proxy-1.0.0/docs/resilience.md +105 -0
  88. llm_redact_proxy-1.0.0/docs/screenshots/config-editor.png +0 -0
  89. llm_redact_proxy-1.0.0/docs/screenshots/dashboard.png +0 -0
  90. llm_redact_proxy-1.0.0/docs/screenshots/plugins/audit.svg +11 -0
  91. llm_redact_proxy-1.0.0/docs/screenshots/plugins/config-edit-allowlist.svg +36 -0
  92. llm_redact_proxy-1.0.0/docs/screenshots/plugins/config-edit-block.svg +33 -0
  93. llm_redact_proxy-1.0.0/docs/screenshots/plugins/config-edit-languages.svg +45 -0
  94. llm_redact_proxy-1.0.0/docs/screenshots/plugins/config-edit.svg +47 -0
  95. llm_redact_proxy-1.0.0/docs/screenshots/plugins/config-show.svg +70 -0
  96. llm_redact_proxy-1.0.0/docs/screenshots/plugins/doctor.svg +21 -0
  97. llm_redact_proxy-1.0.0/docs/screenshots/plugins/guide.svg +24 -0
  98. llm_redact_proxy-1.0.0/docs/screenshots/plugins/install.svg +15 -0
  99. llm_redact_proxy-1.0.0/docs/screenshots/plugins/preview.svg +13 -0
  100. llm_redact_proxy-1.0.0/docs/screenshots/plugins/recent.svg +40 -0
  101. llm_redact_proxy-1.0.0/docs/screenshots/plugins/sessions.svg +11 -0
  102. llm_redact_proxy-1.0.0/docs/screenshots/plugins/status.svg +26 -0
  103. llm_redact_proxy-1.0.0/docs/screenshots/plugins/users.svg +11 -0
  104. llm_redact_proxy-1.0.0/docs/screenshots/preview.png +0 -0
  105. llm_redact_proxy-1.0.0/docs/security-dataflows.md +73 -0
  106. llm_redact_proxy-1.0.0/docs/security-review-3.1.md +121 -0
  107. llm_redact_proxy-1.0.0/docs/security-testing.md +68 -0
  108. llm_redact_proxy-1.0.0/docs/threat-model.md +241 -0
  109. llm_redact_proxy-1.0.0/docs/troubleshooting.md +118 -0
  110. llm_redact_proxy-1.0.0/docs/versioning.md +49 -0
  111. llm_redact_proxy-1.0.0/examples/compose-config.toml +7 -0
  112. llm_redact_proxy-1.0.0/plugins/llm-redact/.claude-plugin/plugin.json +18 -0
  113. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/audit.md +23 -0
  114. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/config-edit.md +47 -0
  115. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/config-show.md +24 -0
  116. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/doctor.md +24 -0
  117. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/guide.md +25 -0
  118. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/preview.md +28 -0
  119. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/recent.md +28 -0
  120. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/sessions.md +25 -0
  121. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/status.md +35 -0
  122. llm_redact_proxy-1.0.0/plugins/llm-redact/commands/users.md +35 -0
  123. llm_redact_proxy-1.0.0/pyproject.toml +196 -0
  124. llm_redact_proxy-1.0.0/scripts/capture_plugin_screenshots.py +469 -0
  125. llm_redact_proxy-1.0.0/scripts/capture_screenshots.py +138 -0
  126. llm_redact_proxy-1.0.0/scripts/complexity_allowlist.py +13 -0
  127. llm_redact_proxy-1.0.0/scripts/complexity_gate.py +204 -0
  128. llm_redact_proxy-1.0.0/scripts/container_smoke.sh +72 -0
  129. llm_redact_proxy-1.0.0/scripts/dogfood_claude.py +261 -0
  130. llm_redact_proxy-1.0.0/scripts/fake_upstream.py +301 -0
  131. llm_redact_proxy-1.0.0/scripts/history_sweep.py +104 -0
  132. llm_redact_proxy-1.0.0/scripts/live_smoke.py +63 -0
  133. llm_redact_proxy-1.0.0/scripts/mutation_equivalents.py +905 -0
  134. llm_redact_proxy-1.0.0/scripts/mutation_gate.py +75 -0
  135. llm_redact_proxy-1.0.0/scripts/oracle_smoke.sh +43 -0
  136. llm_redact_proxy-1.0.0/scripts/plugin_e2e.py +75 -0
  137. llm_redact_proxy-1.0.0/scripts/render_diagrams.sh +32 -0
  138. llm_redact_proxy-1.0.0/scripts/render_plugins.py +39 -0
  139. llm_redact_proxy-1.0.0/scripts/render_user_guide.py +127 -0
  140. llm_redact_proxy-1.0.0/scripts/update_formula.py +47 -0
  141. llm_redact_proxy-1.0.0/src/llm_redact/__init__.py +3 -0
  142. llm_redact_proxy-1.0.0/src/llm_redact/__main__.py +4 -0
  143. llm_redact_proxy-1.0.0/src/llm_redact/audit.py +135 -0
  144. llm_redact_proxy-1.0.0/src/llm_redact/audit_s3.py +97 -0
  145. llm_redact_proxy-1.0.0/src/llm_redact/bench/__init__.py +6 -0
  146. llm_redact_proxy-1.0.0/src/llm_redact/bench/__main__.py +110 -0
  147. llm_redact_proxy-1.0.0/src/llm_redact/bench/corpus.py +968 -0
  148. llm_redact_proxy-1.0.0/src/llm_redact/bench/fp_scan.py +129 -0
  149. llm_redact_proxy-1.0.0/src/llm_redact/bench/latency.py +365 -0
  150. llm_redact_proxy-1.0.0/src/llm_redact/bench/metrics.py +124 -0
  151. llm_redact_proxy-1.0.0/src/llm_redact/cli.py +753 -0
  152. llm_redact_proxy-1.0.0/src/llm_redact/cloud_detect.py +92 -0
  153. llm_redact_proxy-1.0.0/src/llm_redact/completions.py +117 -0
  154. llm_redact_proxy-1.0.0/src/llm_redact/config.py +1023 -0
  155. llm_redact_proxy-1.0.0/src/llm_redact/config_write.py +314 -0
  156. llm_redact_proxy-1.0.0/src/llm_redact/dashboard.html +1218 -0
  157. llm_redact_proxy-1.0.0/src/llm_redact/detection/__init__.py +4 -0
  158. llm_redact_proxy-1.0.0/src/llm_redact/detection/base.py +24 -0
  159. llm_redact_proxy-1.0.0/src/llm_redact/detection/deny.py +57 -0
  160. llm_redact_proxy-1.0.0/src/llm_redact/detection/engine.py +345 -0
  161. llm_redact_proxy-1.0.0/src/llm_redact/detection/gliner_ner.py +73 -0
  162. llm_redact_proxy-1.0.0/src/llm_redact/detection/hf_ner.py +85 -0
  163. llm_redact_proxy-1.0.0/src/llm_redact/detection/ner.py +76 -0
  164. llm_redact_proxy-1.0.0/src/llm_redact/detection/presidio_ner.py +123 -0
  165. llm_redact_proxy-1.0.0/src/llm_redact/detection/regex_rules.py +1647 -0
  166. llm_redact_proxy-1.0.0/src/llm_redact/detection/stanza_ner.py +76 -0
  167. llm_redact_proxy-1.0.0/src/llm_redact/detection/validators.py +114 -0
  168. llm_redact_proxy-1.0.0/src/llm_redact/detection/wallet_checksums.py +264 -0
  169. llm_redact_proxy-1.0.0/src/llm_redact/doctor_cli.py +687 -0
  170. llm_redact_proxy-1.0.0/src/llm_redact/eventstream.py +216 -0
  171. llm_redact_proxy-1.0.0/src/llm_redact/fips.py +77 -0
  172. llm_redact_proxy-1.0.0/src/llm_redact/free_defaults.py +80 -0
  173. llm_redact_proxy-1.0.0/src/llm_redact/init_cli.py +147 -0
  174. llm_redact_proxy-1.0.0/src/llm_redact/jsonwalk.py +82 -0
  175. llm_redact_proxy-1.0.0/src/llm_redact/license_cli.py +94 -0
  176. llm_redact_proxy-1.0.0/src/llm_redact/licensing.py +133 -0
  177. llm_redact_proxy-1.0.0/src/llm_redact/log.py +47 -0
  178. llm_redact_proxy-1.0.0/src/llm_redact/metrics.py +154 -0
  179. llm_redact_proxy-1.0.0/src/llm_redact/multipart.py +116 -0
  180. llm_redact_proxy-1.0.0/src/llm_redact/ndjson.py +25 -0
  181. llm_redact_proxy-1.0.0/src/llm_redact/placeholders.py +96 -0
  182. llm_redact_proxy-1.0.0/src/llm_redact/plugin_api.py +92 -0
  183. llm_redact_proxy-1.0.0/src/llm_redact/plugin_assets.py +398 -0
  184. llm_redact_proxy-1.0.0/src/llm_redact/plugin_cli.py +298 -0
  185. llm_redact_proxy-1.0.0/src/llm_redact/providers/__init__.py +46 -0
  186. llm_redact_proxy-1.0.0/src/llm_redact/providers/anthropic.py +238 -0
  187. llm_redact_proxy-1.0.0/src/llm_redact/providers/azure_openai.py +83 -0
  188. llm_redact_proxy-1.0.0/src/llm_redact/providers/base.py +290 -0
  189. llm_redact_proxy-1.0.0/src/llm_redact/providers/bedrock.py +269 -0
  190. llm_redact_proxy-1.0.0/src/llm_redact/providers/claude_vertex.py +52 -0
  191. llm_redact_proxy-1.0.0/src/llm_redact/providers/cohere.py +204 -0
  192. llm_redact_proxy-1.0.0/src/llm_redact/providers/custom.py +105 -0
  193. llm_redact_proxy-1.0.0/src/llm_redact/providers/gemini.py +255 -0
  194. llm_redact_proxy-1.0.0/src/llm_redact/providers/ollama.py +129 -0
  195. llm_redact_proxy-1.0.0/src/llm_redact/providers/openai.py +371 -0
  196. llm_redact_proxy-1.0.0/src/llm_redact/providers/openai_responses.py +271 -0
  197. llm_redact_proxy-1.0.0/src/llm_redact/providers/vertex.py +50 -0
  198. llm_redact_proxy-1.0.0/src/llm_redact/proxy.py +2398 -0
  199. llm_redact_proxy-1.0.0/src/llm_redact/py.typed +0 -0
  200. llm_redact_proxy-1.0.0/src/llm_redact/realtime.py +861 -0
  201. llm_redact_proxy-1.0.0/src/llm_redact/redactor.py +124 -0
  202. llm_redact_proxy-1.0.0/src/llm_redact/registry.py +142 -0
  203. llm_redact_proxy-1.0.0/src/llm_redact/rehydrate.py +270 -0
  204. llm_redact_proxy-1.0.0/src/llm_redact/run_cli.py +215 -0
  205. llm_redact_proxy-1.0.0/src/llm_redact/service_cli.py +199 -0
  206. llm_redact_proxy-1.0.0/src/llm_redact/sessions.py +72 -0
  207. llm_redact_proxy-1.0.0/src/llm_redact/sse.py +100 -0
  208. llm_redact_proxy-1.0.0/src/llm_redact/user_guide.html +73 -0
  209. llm_redact_proxy-1.0.0/src/llm_redact/user_guide.md +124 -0
  210. llm_redact_proxy-1.0.0/src/llm_redact/users.py +162 -0
  211. llm_redact_proxy-1.0.0/src/llm_redact/vault.py +715 -0
  212. llm_redact_proxy-1.0.0/src/llm_redact/vault_cli.py +673 -0
  213. llm_redact_proxy-1.0.0/src/llm_redact/vault_crypto.py +104 -0
  214. llm_redact_proxy-1.0.0/src/llm_redact/vault_rdbms.py +891 -0
  215. llm_redact_proxy-1.0.0/tests/conftest.py +38 -0
  216. llm_redact_proxy-1.0.0/tests/fake_cipher.py +45 -0
  217. llm_redact_proxy-1.0.0/tests/license_fixtures.py +45 -0
  218. llm_redact_proxy-1.0.0/tests/test_api_coverage.py +121 -0
  219. llm_redact_proxy-1.0.0/tests/test_audit_required.py +241 -0
  220. llm_redact_proxy-1.0.0/tests/test_bench.py +76 -0
  221. llm_redact_proxy-1.0.0/tests/test_canary_leak.py +139 -0
  222. llm_redact_proxy-1.0.0/tests/test_cli_status.py +182 -0
  223. llm_redact_proxy-1.0.0/tests/test_cli_tools.py +228 -0
  224. llm_redact_proxy-1.0.0/tests/test_cli_vault.py +204 -0
  225. llm_redact_proxy-1.0.0/tests/test_cloud_detect.py +62 -0
  226. llm_redact_proxy-1.0.0/tests/test_codec_fuzz.py +105 -0
  227. llm_redact_proxy-1.0.0/tests/test_codec_mutation_kills.py +291 -0
  228. llm_redact_proxy-1.0.0/tests/test_complexity_gate.py +174 -0
  229. llm_redact_proxy-1.0.0/tests/test_config.py +177 -0
  230. llm_redact_proxy-1.0.0/tests/test_config_endpoint.py +561 -0
  231. llm_redact_proxy-1.0.0/tests/test_config_write.py +256 -0
  232. llm_redact_proxy-1.0.0/tests/test_coverage_gaps.py +148 -0
  233. llm_redact_proxy-1.0.0/tests/test_coverage_tail.py +196 -0
  234. llm_redact_proxy-1.0.0/tests/test_custom_providers.py +187 -0
  235. llm_redact_proxy-1.0.0/tests/test_custom_validators.py +134 -0
  236. llm_redact_proxy-1.0.0/tests/test_dashboard.py +243 -0
  237. llm_redact_proxy-1.0.0/tests/test_deny.py +217 -0
  238. llm_redact_proxy-1.0.0/tests/test_dependencies_doc.py +40 -0
  239. llm_redact_proxy-1.0.0/tests/test_deploy_assets.py +303 -0
  240. llm_redact_proxy-1.0.0/tests/test_detection_scope.py +354 -0
  241. llm_redact_proxy-1.0.0/tests/test_detectors.py +764 -0
  242. llm_redact_proxy-1.0.0/tests/test_docs_index.py +16 -0
  243. llm_redact_proxy-1.0.0/tests/test_doctor.py +396 -0
  244. llm_redact_proxy-1.0.0/tests/test_events_endpoint.py +143 -0
  245. llm_redact_proxy-1.0.0/tests/test_eventstream.py +180 -0
  246. llm_redact_proxy-1.0.0/tests/test_fips_posture.py +73 -0
  247. llm_redact_proxy-1.0.0/tests/test_free_alone.py +126 -0
  248. llm_redact_proxy-1.0.0/tests/test_gliner.py +127 -0
  249. llm_redact_proxy-1.0.0/tests/test_jsonwalk.py +73 -0
  250. llm_redact_proxy-1.0.0/tests/test_language_scope.py +214 -0
  251. llm_redact_proxy-1.0.0/tests/test_latency_bench.py +61 -0
  252. llm_redact_proxy-1.0.0/tests/test_licensing.py +86 -0
  253. llm_redact_proxy-1.0.0/tests/test_live.py +589 -0
  254. llm_redact_proxy-1.0.0/tests/test_log.py +65 -0
  255. llm_redact_proxy-1.0.0/tests/test_mcp_connector.py +148 -0
  256. llm_redact_proxy-1.0.0/tests/test_metrics.py +63 -0
  257. llm_redact_proxy-1.0.0/tests/test_modes.py +151 -0
  258. llm_redact_proxy-1.0.0/tests/test_multipart.py +111 -0
  259. llm_redact_proxy-1.0.0/tests/test_mutation_hardening.py +476 -0
  260. llm_redact_proxy-1.0.0/tests/test_ndjson.py +27 -0
  261. llm_redact_proxy-1.0.0/tests/test_ner.py +109 -0
  262. llm_redact_proxy-1.0.0/tests/test_ner_multibackend.py +171 -0
  263. llm_redact_proxy-1.0.0/tests/test_ner_stanza_hf.py +172 -0
  264. llm_redact_proxy-1.0.0/tests/test_no_gates.py +89 -0
  265. llm_redact_proxy-1.0.0/tests/test_no_op_forward.py +65 -0
  266. llm_redact_proxy-1.0.0/tests/test_open_core_free_coverage.py +288 -0
  267. llm_redact_proxy-1.0.0/tests/test_placeholders.py +101 -0
  268. llm_redact_proxy-1.0.0/tests/test_plugin_cli.py +247 -0
  269. llm_redact_proxy-1.0.0/tests/test_plugins.py +145 -0
  270. llm_redact_proxy-1.0.0/tests/test_prefilter.py +161 -0
  271. llm_redact_proxy-1.0.0/tests/test_presidio.py +217 -0
  272. llm_redact_proxy-1.0.0/tests/test_preview.py +119 -0
  273. llm_redact_proxy-1.0.0/tests/test_properties.py +595 -0
  274. llm_redact_proxy-1.0.0/tests/test_provider_anthropic.py +116 -0
  275. llm_redact_proxy-1.0.0/tests/test_provider_anthropic_batches.py +212 -0
  276. llm_redact_proxy-1.0.0/tests/test_provider_azure.py +132 -0
  277. llm_redact_proxy-1.0.0/tests/test_provider_cohere.py +192 -0
  278. llm_redact_proxy-1.0.0/tests/test_provider_gemini.py +288 -0
  279. llm_redact_proxy-1.0.0/tests/test_provider_ollama.py +277 -0
  280. llm_redact_proxy-1.0.0/tests/test_provider_openai.py +230 -0
  281. llm_redact_proxy-1.0.0/tests/test_provider_openai_files.py +280 -0
  282. llm_redact_proxy-1.0.0/tests/test_provider_openai_media.py +330 -0
  283. llm_redact_proxy-1.0.0/tests/test_provider_openai_responses.py +235 -0
  284. llm_redact_proxy-1.0.0/tests/test_proxy_integration.py +957 -0
  285. llm_redact_proxy-1.0.0/tests/test_realtime_gemini.py +185 -0
  286. llm_redact_proxy-1.0.0/tests/test_realtime_openai.py +318 -0
  287. llm_redact_proxy-1.0.0/tests/test_realtime_relay.py +218 -0
  288. llm_redact_proxy-1.0.0/tests/test_redactor.py +129 -0
  289. llm_redact_proxy-1.0.0/tests/test_registry.py +215 -0
  290. llm_redact_proxy-1.0.0/tests/test_rehydrate.py +214 -0
  291. llm_redact_proxy-1.0.0/tests/test_reload.py +172 -0
  292. llm_redact_proxy-1.0.0/tests/test_restart_only_docs.py +36 -0
  293. llm_redact_proxy-1.0.0/tests/test_run_cli.py +248 -0
  294. llm_redact_proxy-1.0.0/tests/test_screenshot_assets.py +45 -0
  295. llm_redact_proxy-1.0.0/tests/test_security_boundaries.py +510 -0
  296. llm_redact_proxy-1.0.0/tests/test_serve_check.py +118 -0
  297. llm_redact_proxy-1.0.0/tests/test_session_ttl.py +69 -0
  298. llm_redact_proxy-1.0.0/tests/test_sessions.py +52 -0
  299. llm_redact_proxy-1.0.0/tests/test_sessions_endpoint.py +164 -0
  300. llm_redact_proxy-1.0.0/tests/test_soak_concurrency.py +33 -0
  301. llm_redact_proxy-1.0.0/tests/test_sse.py +70 -0
  302. llm_redact_proxy-1.0.0/tests/test_stream_truncation.py +162 -0
  303. llm_redact_proxy-1.0.0/tests/test_tls.py +222 -0
  304. llm_redact_proxy-1.0.0/tests/test_upstream_faults.py +241 -0
  305. llm_redact_proxy-1.0.0/tests/test_user_guide.py +85 -0
  306. llm_redact_proxy-1.0.0/tests/test_vault.py +29 -0
  307. llm_redact_proxy-1.0.0/tests/test_vault_crypto_free.py +148 -0
  308. llm_redact_proxy-1.0.0/tests/test_vault_faults.py +131 -0
  309. llm_redact_proxy-1.0.0/tests/test_vault_rdbms.py +816 -0
  310. llm_redact_proxy-1.0.0/tests/test_vault_sqlite.py +276 -0
  311. llm_redact_proxy-1.0.0/tests/test_wallet_checksums.py +97 -0
  312. llm_redact_proxy-1.0.0/uv.lock +3434 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__github__pull_request_read"
5
+ ]
6
+ }
7
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "llm-redact",
3
+ "owner": {
4
+ "name": "llm-redact",
5
+ "url": "https://github.com/asanderson/llm-redact"
6
+ },
7
+ "plugins": [
8
+ {
9
+ "name": "llm-redact",
10
+ "source": "./plugins/llm-redact",
11
+ "description": "llm-redact proxy control: status, live traffic, sessions, redaction preview, audit verification, and guarded config editing as slash commands"
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,12 @@
1
+ .git
2
+ .venv
3
+ .pytest_cache
4
+ .ruff_cache
5
+ .mypy_cache
6
+ __pycache__
7
+ dist
8
+ tests
9
+ bench-report
10
+ *.db
11
+ .github
12
+ .claude
@@ -0,0 +1,50 @@
1
+ name: Bug report
2
+ description: Something misbehaves — a redaction miss on a covered shape, a mangled response, a broken tool.
3
+ labels: [bug]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ **Never paste real secrets, vault contents, or raw request/response
9
+ bodies containing private values.** Reproduce with secret-shaped
10
+ fakes (`AKIAIOSFODNN7EXAMPLE`, `jane@corp.example`) — the detectors
11
+ treat them identically. Suspected security vulnerabilities go
12
+ through [private reporting](../../security/policy), not issues.
13
+ - type: input
14
+ id: version
15
+ attributes:
16
+ label: llm-redact version
17
+ description: "`llm-redact --version` (also note the install method: pip/uvx, container, source)"
18
+ placeholder: "1.4.0, via uvx"
19
+ validations:
20
+ required: true
21
+ - type: input
22
+ id: tool
23
+ attributes:
24
+ label: Agentic tool and provider
25
+ description: What talks through the proxy, and to which provider/endpoint?
26
+ placeholder: "Claude Code → Anthropic /v1/messages (streaming)"
27
+ validations:
28
+ required: true
29
+ - type: textarea
30
+ id: repro
31
+ attributes:
32
+ label: Reproduction
33
+ description: Smallest input/config that shows the problem. Redact your config's allowlist/deny values if they are themselves sensitive.
34
+ placeholder: |
35
+ 1. serve with the config below
36
+ 2. send this request (fake values)
37
+ 3. observe ...
38
+ validations:
39
+ required: true
40
+ - type: textarea
41
+ id: expected
42
+ attributes:
43
+ label: Expected vs actual
44
+ validations:
45
+ required: true
46
+ - type: textarea
47
+ id: logs
48
+ attributes:
49
+ label: Proxy log lines
50
+ description: Proxy logs carry only paths, statuses, and detection counts by design — safe to paste as-is.
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Report a security vulnerability
4
+ url: https://github.com/asanderson/llm-redact/security/policy
5
+ about: Suspected vulnerabilities go through GitHub private vulnerability reporting — never public issues. See docs/SECURITY.md for scope.
@@ -0,0 +1,36 @@
1
+ name: Feature request
2
+ description: A new capability, provider, or detection rule.
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: Problem
9
+ description: What can't you do today, or what leaks that shouldn't?
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: Proposed behavior
16
+ validations:
17
+ required: true
18
+ - type: markdown
19
+ attributes:
20
+ value: |
21
+ For **new detection rules**, note the bars up front (they are why
22
+ some obvious-seeming rules are deliberately absent — see the
23
+ "Known limitations" section of the README):
24
+ - vendor tokens need a distinctive **prefix** to anchor on;
25
+ - national ids need a **grouped display form plus a checksum**
26
+ (bare digit runs never fire — too collision-prone);
27
+ - the rule must pass the false-positive corpus gate.
28
+
29
+ For **new providers**: the proxy operates on raw bytes and must
30
+ forward unknown fields verbatim; a link to the provider's wire
31
+ format docs (streaming included) makes triage much faster.
32
+ - type: textarea
33
+ id: alternatives
34
+ attributes:
35
+ label: Alternatives considered
36
+ description: Custom rules (`[[detection.custom_rules]]`), deny strings, an existing provider's compat endpoint, ...
@@ -0,0 +1,12 @@
1
+ # Keep the SHA-pinned GitHub Actions current: dependabot updates the pinned
2
+ # SHA and its trailing version comment together. Python deps are handled by
3
+ # uv.lock plus the weekly gating pip-audit job, so only actions are watched.
4
+ version: 2
5
+ updates:
6
+ - package-ecosystem: github-actions
7
+ directory: /
8
+ schedule:
9
+ interval: weekly
10
+ groups:
11
+ actions:
12
+ patterns: ["*"]
@@ -0,0 +1,15 @@
1
+ <!-- Never include real secrets anywhere in a PR — use secret-shaped fakes
2
+ (vendors' canonical examples, alphabet runs, corp.example domains). -->
3
+
4
+ ## What & why
5
+
6
+ ## Checklist
7
+
8
+ - [ ] All gates pass locally: `ruff check` + `ruff format --check`,
9
+ `pytest`, `mypy`, `python -m llm_redact.bench --check`
10
+ - [ ] `CHANGELOG.md` entry under `[Unreleased]` (user-visible changes)
11
+ - [ ] Touched `rehydrate.py`/`sse.py`/adapter event handling → extended the
12
+ split-at-every-offset sweeps (not just single-case tests)
13
+ - [ ] New detection rule → generator in `bench/corpus.py`, fp-corpus gate
14
+ run, `MANIFEST.toml` updated with justification if counts changed
15
+ - [ ] No new runtime dependencies (extras are fine)
@@ -0,0 +1,439 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ # Weekly run so new dependency advisories (the audit job) surface on main
8
+ # without waiting for the next push.
9
+ schedule:
10
+ - cron: "17 6 * * 1"
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+ name: test (py${{ matrix.python }})
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ python: ["3.11", "3.12", "3.13", "3.14"]
27
+ steps:
28
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
29
+ with:
30
+ persist-credentials: false
31
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
32
+ with:
33
+ python-version: ${{ matrix.python }}
34
+ enable-cache: true
35
+ cache-dependency-glob: uv.lock
36
+ - run: uv sync --frozen
37
+ - run: uv run pytest
38
+ # The concurrency/soak checks are deselected from the default run; run
39
+ # them explicitly so the never-wrong-value-under-load invariants stay live.
40
+ - run: uv run pytest -m soak
41
+ - run: uv run ruff check .
42
+ - run: uv run ruff format --check .
43
+ - run: uv run mypy
44
+
45
+ test-macos:
46
+ # Both macOS architectures, one Python each: the platform-sensitive
47
+ # surface (kqueue event loop, APFS os.replace, explicit 0600/0700
48
+ # modes, sqlite WAL, SIGHUP) does not vary by Python minor, and macOS
49
+ # runners bill ~10x Linux. macos-15 is Apple silicon (arm64);
50
+ # macos-15-intel is GitHub's x86_64 runner (supported into 2027) —
51
+ # the wheel is pure-Python, so these jobs ARE the x86_64/arm64 macOS
52
+ # support verification.
53
+ name: test (macOS ${{ matrix.arch }}, py3.13)
54
+ strategy:
55
+ fail-fast: false
56
+ matrix:
57
+ include:
58
+ - runner: macos-15
59
+ arch: arm64
60
+ - runner: macos-15-intel
61
+ arch: x86_64
62
+ runs-on: ${{ matrix.runner }}
63
+ steps:
64
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
65
+ with:
66
+ persist-credentials: false
67
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
68
+ with:
69
+ python-version: "3.13"
70
+ enable-cache: true
71
+ cache-dependency-glob: uv.lock
72
+ - run: uv sync --frozen
73
+ - run: uv run pytest
74
+
75
+ test-windows:
76
+ # Phase 26: the supported Windows scope is the Free tier + agent
77
+ # plugins, and this job is its verification — the FULL suite runs, with
78
+ # only narrow, pinned platform gates (POSIX mode assertions become
79
+ # conditional; doctor/service take their win32 branches). PYTHONUTF8
80
+ # keeps the guillemet placeholders printable on a cp1252 console.
81
+ name: test (Windows, py3.13)
82
+ runs-on: windows-latest
83
+ env:
84
+ PYTHONUTF8: "1"
85
+ steps:
86
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
87
+ with:
88
+ persist-credentials: false
89
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
90
+ with:
91
+ python-version: "3.13"
92
+ enable-cache: true
93
+ cache-dependency-glob: uv.lock
94
+ - run: uv sync --frozen
95
+ - run: uv run pytest
96
+
97
+ plugins:
98
+ name: agent plugins (install e2e + validate)
99
+ runs-on: ubuntu-latest
100
+ steps:
101
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
102
+ with:
103
+ persist-credentials: false
104
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
105
+ with:
106
+ python-version: "3.13"
107
+ enable-cache: true
108
+ cache-dependency-glob: uv.lock
109
+ - run: uv sync --frozen
110
+ # The rendered plugin tree + marketplace must be in sync with
111
+ # plugin_assets.py (also pinned by test, but re-render here so a
112
+ # stale checked-in file fails loudly).
113
+ - run: uv run python scripts/render_plugins.py && git diff --exit-code plugins .claude-plugin
114
+ # Install into a throwaway HOME and prove the command↔CLI contract.
115
+ - run: uv run python scripts/plugin_e2e.py
116
+ # Structural validation of the Claude Code plugin manifest, when the
117
+ # CLI is available (best-effort — never blocks the job if absent).
118
+ - name: claude plugin validate (best-effort)
119
+ run: |
120
+ if command -v claude >/dev/null 2>&1; then
121
+ claude plugin validate --strict plugins/llm-redact || exit 1
122
+ else
123
+ echo "claude CLI not present on the runner; skipping structural validate"
124
+ fi
125
+
126
+ helm:
127
+ # The Helm chart (deploy/helm/llm-redact) rendered for real: `helm lint`
128
+ # plus the helm-gated tests in test_deploy_assets.py, which render both
129
+ # value presets (sidecar; standalone+autoscaling) and assert the
130
+ # never-wrong-value guardrail {{ fail }}s a per-pod-vault autoscaled render.
131
+ # helm is installed from the official release with a PINNED version +
132
+ # SHA256 checksum (no marketplace action in the supply chain); the base
133
+ # `test` job already runs the stdlib needle/appVersion tests, which skip
134
+ # the helm-gated ones when helm is absent.
135
+ name: helm chart (lint + render both modes)
136
+ runs-on: ubuntu-latest
137
+ env:
138
+ HELM_VERSION: v3.16.3
139
+ HELM_SHA256: f5355c79190951eed23c5432a3b920e071f4c00a64f75e077de0dd4cb7b294ea
140
+ steps:
141
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
142
+ with:
143
+ persist-credentials: false
144
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
145
+ with:
146
+ python-version: "3.13"
147
+ enable-cache: true
148
+ cache-dependency-glob: uv.lock
149
+ - run: uv sync --frozen
150
+ - name: install helm (pinned + checksum-verified)
151
+ run: |
152
+ set -euo pipefail
153
+ tarball="helm-${HELM_VERSION}-linux-amd64.tar.gz"
154
+ curl -fsSL "https://get.helm.sh/${tarball}" -o "$tarball"
155
+ echo "${HELM_SHA256} ${tarball}" | sha256sum -c -
156
+ tar xzf "$tarball"
157
+ sudo install -m 0755 linux-amd64/helm /usr/local/bin/helm
158
+ helm version
159
+ - name: helm lint
160
+ run: helm lint deploy/helm/llm-redact
161
+ - name: chart tests (renders both presets + guardrail-fires)
162
+ run: uv run pytest tests/test_deploy_assets.py -v
163
+ - name: guardrail fails an autoscaled per-pod-vault render
164
+ run: |
165
+ set -euo pipefail
166
+ if helm template rel deploy/helm/llm-redact \
167
+ --set mode=standalone --set autoscaling.enabled=true \
168
+ --set vault.backend=sqlite >/dev/null 2>err.txt; then
169
+ echo "FAIL: guardrail did not fire for standalone+sqlite+autoscaling"; exit 1
170
+ fi
171
+ grep -q "SHARED vault" err.txt
172
+
173
+ audit:
174
+ name: dependency audit (pip-audit)
175
+ runs-on: ubuntu-latest
176
+ steps:
177
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
178
+ with:
179
+ persist-credentials: false
180
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
181
+ with:
182
+ python-version: "3.13"
183
+ enable-cache: true
184
+ cache-dependency-glob: uv.lock
185
+ # Runtime closure only (no dev tools): what a `pip install
186
+ # llm-redact-proxy` user actually gets. Gating on purpose — the
187
+ # closure is tiny (httpx/starlette/uvicorn). If an advisory has no
188
+ # fix released yet, add `--ignore-vuln GHSA-...` with a comment and
189
+ # an issue link, and remove it when the fix ships.
190
+ - run: >
191
+ uv export --frozen --no-dev --no-emit-project --no-hashes
192
+ -o requirements-audit.txt
193
+ - run: uvx pip-audit --strict --no-deps -r requirements-audit.txt
194
+
195
+ dependency-review:
196
+ # Blocks a PR that introduces a dependency with a known vulnerability or a
197
+ # disallowed licence, using GitHub's Dependency Graph diff. The graph is
198
+ # available on public repos (and private repos with Advanced Security), so
199
+ # the job self-skips while this repo is private — it arms on go-public,
200
+ # the same switch as PyPI publishing and build attestations. pip-audit
201
+ # (the `audit` job) covers the runtime closure meanwhile.
202
+ name: dependency review
203
+ runs-on: ubuntu-latest
204
+ if: ${{ github.event_name == 'pull_request' && github.event.repository.private == false }}
205
+ steps:
206
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
207
+ with:
208
+ persist-credentials: false
209
+ - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
210
+ with:
211
+ fail-on-severity: high
212
+ # Copyleft licences would complicate the MIT distribution; flag them
213
+ # for a human decision rather than silently pulling them in.
214
+ deny-licenses: GPL-2.0, GPL-3.0, AGPL-3.0
215
+
216
+ ner:
217
+ name: ner extras (spaCy model + presidio)
218
+ runs-on: ubuntu-latest
219
+ steps:
220
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
221
+ with:
222
+ persist-credentials: false
223
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
224
+ with:
225
+ python-version: "3.13"
226
+ enable-cache: true
227
+ cache-dependency-glob: uv.lock
228
+ - run: uv sync --frozen --extra ner --extra presidio
229
+ # Direct wheel URL: `spacy download` shells out to pip, which does not
230
+ # exist in uv-managed venvs. One model serves both backends (the
231
+ # presidio detector pins en_core_web_sm too).
232
+ - run: uv pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl
233
+ # test_presidio.py includes the real-AnalyzerEngine smoke test, which
234
+ # only runs when the extra and the model are both present — i.e. here.
235
+ - run: uv run pytest tests/test_ner.py tests/test_presidio.py -v
236
+
237
+ # NOTE (R4 open-core split): the real-PostgreSQL+MySQL RDBMS-vault job moved
238
+ # to the private llm-redact-pro repo's CI (the server RDBMS vault is a Pro
239
+ # feature). The Free `test` job still runs test_vault_rdbms.py's fake-driver
240
+ # battery over stdlib sqlite3-as-dbapi, which proves the portable dialect SQL.
241
+
242
+ bench:
243
+ name: detection bench (report artifact)
244
+ runs-on: ubuntu-latest
245
+ steps:
246
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
247
+ with:
248
+ persist-credentials: false
249
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
250
+ with:
251
+ python-version: "3.13"
252
+ enable-cache: true
253
+ cache-dependency-glob: uv.lock
254
+ - run: uv sync --frozen
255
+ - run: uv run python -m llm_redact.bench --latency --out bench-report --check
256
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
257
+ with:
258
+ name: bench-report
259
+ path: bench-report/
260
+
261
+ complexity:
262
+ # Every function with cyclomatic complexity > 1 must be EXECUTED by the
263
+ # suite (scripts/complexity_gate.py). Report-only through 3.1.1; gated
264
+ # here from 3.1.2 — an uncovered branching function fails unless recorded
265
+ # in scripts/complexity_allowlist.py with a justification, and a stale
266
+ # allowlist entry fails too (same discipline as the mutation gate).
267
+ name: complexity coverage (assurance gate)
268
+ runs-on: ubuntu-latest
269
+ steps:
270
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
271
+ with:
272
+ persist-credentials: false
273
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
274
+ with:
275
+ python-version: "3.13"
276
+ enable-cache: true
277
+ cache-dependency-glob: uv.lock
278
+ - run: uv sync --frozen
279
+ - name: run the suite under coverage
280
+ run: uv run coverage run -m pytest -q
281
+ - name: gate on CC>1 execution (Free core)
282
+ run: uv run python scripts/complexity_gate.py --check
283
+ # The paid package's own complexity gate runs in the llm-redact-pro repo's
284
+ # CI (R4 physical split) — it is no longer in this tree.
285
+
286
+ # NOTE (R4 open-core split): the `free-alone` job (which hid the co-located
287
+ # src/llm_redact_pro to simulate a Free-only install) and the `enforcement-ext`
288
+ # job (which compiled the pro Cython enforcement core) both moved to the
289
+ # llm-redact-pro repo. This repo IS the Free-only install now — test_free_alone.py
290
+ # runs in the normal `test` job (the pro package is genuinely absent), and the
291
+ # keyless-boot / paid-config-fails-closed assertions ride there.
292
+
293
+ mutation:
294
+ # Assurance: mutation testing proves the suites KILL a weakened guard or a
295
+ # broken checksum, not just that they pass. Scoped to the load-bearing
296
+ # correctness core ([tool.mutmut] in pyproject). GATING via
297
+ # scripts/mutation_gate.py: a surviving mutant fails unless recorded in
298
+ # scripts/mutation_equivalents.py with a reviewed justification, and a
299
+ # stale allow-list entry fails too — nothing survives silently in either
300
+ # direction (see docs/assurance.md). Baseline: 968/1100 killed, 130
301
+ # reviewed equivalents.
302
+ name: mutation testing (assurance gate)
303
+ runs-on: ubuntu-latest
304
+ steps:
305
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
306
+ with:
307
+ persist-credentials: false
308
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
309
+ with:
310
+ python-version: "3.13"
311
+ enable-cache: true
312
+ cache-dependency-glob: uv.lock
313
+ - run: uv sync --frozen
314
+ # mutmut exits non-zero when ANY mutant survives — including the 130
315
+ # reviewed equivalents — so the run itself must not gate; the gate
316
+ # script right after it is the pass/fail arbiter.
317
+ - name: run mutation testing
318
+ run: uv run mutmut run || true
319
+ - name: gate against the equivalents allow-list
320
+ run: uv run python scripts/mutation_gate.py
321
+ - name: summarize survivors
322
+ if: always()
323
+ run: uv run mutmut results | tee mutmut-results.txt
324
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
325
+ if: always()
326
+ with:
327
+ name: mutation-results
328
+ path: mutmut-results.txt
329
+
330
+ history-hygiene:
331
+ # Non-gating: runs the production detectors over ALL git history and
332
+ # uploads the HISTORY-ONLY report. This is the go-public review signal —
333
+ # flipping the repo public exposes every past commit at once, so the
334
+ # report must be reviewed clean before that (see docs/history-hygiene.md).
335
+ # Not a hard gate: 17 phases carry many benign secret-SHAPED test fixtures.
336
+ name: history hygiene (report artifact)
337
+ runs-on: ubuntu-latest
338
+ steps:
339
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
340
+ with:
341
+ persist-credentials: false
342
+ fetch-depth: 0 # full history for the sweep
343
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
344
+ with:
345
+ python-version: "3.13"
346
+ enable-cache: true
347
+ cache-dependency-glob: uv.lock
348
+ - run: uv sync --frozen
349
+ - run: uv run python scripts/history_sweep.py | tee history-sweep.txt
350
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
351
+ if: always()
352
+ with:
353
+ name: history-sweep
354
+ path: history-sweep.txt
355
+
356
+ build:
357
+ name: build distribution
358
+ runs-on: ubuntu-latest
359
+ steps:
360
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
361
+ with:
362
+ persist-credentials: false
363
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
364
+ with:
365
+ python-version: "3.13"
366
+ - run: uv build
367
+ - run: uvx twine check dist/*
368
+ # twine cannot catch missing package data; assert the dashboard, the
369
+ # user guide, and the license text ship in both distributions.
370
+ - name: check package data
371
+ run: |
372
+ python -m zipfile -l dist/*.whl | grep 'llm_redact/dashboard.html'
373
+ tar -tzf dist/*.tar.gz | grep 'src/llm_redact/dashboard.html'
374
+ python -m zipfile -l dist/*.whl | grep 'llm_redact/user_guide.md'
375
+ python -m zipfile -l dist/*.whl | grep 'llm_redact/user_guide.html'
376
+ tar -tzf dist/*.tar.gz | grep 'src/llm_redact/user_guide.md'
377
+ tar -tzf dist/*.tar.gz | grep 'src/llm_redact/user_guide.html'
378
+ python -m zipfile -l dist/*.whl | grep 'licenses/LICENSE'
379
+ tar -tzf dist/*.tar.gz | grep -x '[^/]*/LICENSE'
380
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
381
+ with:
382
+ name: dist
383
+ path: dist/
384
+
385
+ reproducible-build:
386
+ # Supply-chain assurance: two builds from the same tree with a pinned
387
+ # SOURCE_DATE_EPOCH must be byte-identical — a divergence means embedded
388
+ # timestamps/ordering crept in and the provenance story weakens. Out-dirs
389
+ # live OUTSIDE the workspace: the sdist packs the working tree, so a
390
+ # first build left inside it would poison the second (found the hard way).
391
+ name: reproducible build
392
+ runs-on: ubuntu-latest
393
+ steps:
394
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
395
+ with:
396
+ persist-credentials: false
397
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
398
+ with:
399
+ python-version: "3.13"
400
+ - name: build twice with a pinned epoch and compare byte-for-byte
401
+ run: |
402
+ export SOURCE_DATE_EPOCH=1704067200
403
+ uv build --out-dir "$RUNNER_TEMP/build-a"
404
+ uv build --out-dir "$RUNNER_TEMP/build-b"
405
+ status=0
406
+ for f in "$RUNNER_TEMP"/build-a/*; do
407
+ name="$(basename "$f")"
408
+ if cmp -s "$f" "$RUNNER_TEMP/build-b/$name"; then
409
+ echo "IDENTICAL: $name"
410
+ else
411
+ echo "DIFFERS: $name"
412
+ status=1
413
+ fi
414
+ done
415
+ exit "$status"
416
+
417
+ container-docker:
418
+ name: container (docker)
419
+ runs-on: ubuntu-latest
420
+ needs: test
421
+ steps:
422
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
423
+ with:
424
+ persist-credentials: false
425
+ - run: docker build --target runtime -t llm-redact:ci .
426
+ - run: docker build --target fake-upstream -t llm-redact-fake:ci .
427
+ - run: ./scripts/container_smoke.sh docker llm-redact:ci llm-redact-fake:ci
428
+
429
+ container-podman:
430
+ name: container (podman, rootless)
431
+ runs-on: ubuntu-latest
432
+ needs: test
433
+ steps:
434
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
435
+ with:
436
+ persist-credentials: false
437
+ - run: podman build --target runtime -t llm-redact:ci .
438
+ - run: podman build --target fake-upstream -t llm-redact-fake:ci .
439
+ - run: ./scripts/container_smoke.sh podman llm-redact:ci llm-redact-fake:ci
@@ -0,0 +1,63 @@
1
+ name: Live API smoke
2
+
3
+ # Manual only: sends real provider requests and spends API credits.
4
+ # Requires repo secrets ANTHROPIC_API_KEY and/or OPENAI_API_KEY.
5
+ # Never runs on pushes or pull requests.
6
+ on:
7
+ workflow_dispatch:
8
+ inputs:
9
+ dogfood:
10
+ description: "also run scripts/dogfood_claude.py (real claude CLI through the proxy)"
11
+ type: boolean
12
+ default: false
13
+ # Enable a weekly drift check after the first successful manual run:
14
+ # schedule:
15
+ # - cron: "17 6 * * 1"
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ jobs:
21
+ live:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
25
+ with:
26
+ persist-credentials: false
27
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
28
+ with:
29
+ python-version: "3.13"
30
+ enable-cache: true
31
+ cache-dependency-glob: uv.lock
32
+ - run: uv sync --frozen
33
+ - run: uv run pytest -m live -rA tests/test_live.py
34
+ env:
35
+ LLM_REDACT_LIVE: "1"
36
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
37
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
38
+
39
+ dogfood:
40
+ # The full production loop: the real claude CLI driven through a real
41
+ # proxy subprocess. Opt-in via the dispatch input; needs ANTHROPIC_API_KEY.
42
+ if: inputs.dogfood
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
46
+ with:
47
+ persist-credentials: false
48
+ - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
49
+ with:
50
+ python-version: "3.13"
51
+ enable-cache: true
52
+ cache-dependency-glob: uv.lock
53
+ - uses: actions/setup-node@395ad3262231945d0d5e35de6a4a55fcec1502e4 # v6
54
+ with:
55
+ node-version: 22
56
+ # Pinned to the version the harness was verified against; bump
57
+ # deliberately. (Manual opt-in job, so no lockfile to anchor to.)
58
+ - run: npm install -g @anthropic-ai/claude-code@2.1.203
59
+ - run: uv sync --frozen
60
+ - run: uv run python scripts/dogfood_claude.py
61
+ env:
62
+ LLM_REDACT_DOGFOOD: "1"
63
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}