limes-guard 0.17.1__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 (244) hide show
  1. limes_guard-0.17.1/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  2. limes_guard-0.17.1/.github/ISSUE_TEMPLATE/feature_request.md +30 -0
  3. limes_guard-0.17.1/.github/PULL_REQUEST_TEMPLATE.md +20 -0
  4. limes_guard-0.17.1/.github/workflows/ci.yml +60 -0
  5. limes_guard-0.17.1/.github/workflows/release.yml +109 -0
  6. limes_guard-0.17.1/.gitignore +18 -0
  7. limes_guard-0.17.1/.gitleaks.toml +42 -0
  8. limes_guard-0.17.1/.pre-commit-config.yaml +28 -0
  9. limes_guard-0.17.1/.python-version +1 -0
  10. limes_guard-0.17.1/CHANGELOG.md +1180 -0
  11. limes_guard-0.17.1/CLA.md +139 -0
  12. limes_guard-0.17.1/CODE_OF_CONDUCT.md +129 -0
  13. limes_guard-0.17.1/CONTRIBUTING.md +99 -0
  14. limes_guard-0.17.1/LICENSE +201 -0
  15. limes_guard-0.17.1/Makefile +46 -0
  16. limes_guard-0.17.1/PKG-INFO +1397 -0
  17. limes_guard-0.17.1/README.md +1364 -0
  18. limes_guard-0.17.1/SECURITY.md +69 -0
  19. limes_guard-0.17.1/docs/audit-trail.md +282 -0
  20. limes_guard-0.17.1/docs/decisions/0001-record-architecture-decisions.md +37 -0
  21. limes_guard-0.17.1/docs/decisions/0002-a-verdict-carries-its-evidence.md +80 -0
  22. limes_guard-0.17.1/docs/decisions/0003-no-detector-without-eval-and-null-control.md +72 -0
  23. limes_guard-0.17.1/docs/decisions/0004-core-detectors-transports.md +72 -0
  24. limes_guard-0.17.1/docs/decisions/0005-mcp-proxy-transport.md +183 -0
  25. limes_guard-0.17.1/docs/decisions/0006-egress-redaction.md +142 -0
  26. limes_guard-0.17.1/docs/decisions/0007-mcp-streamable-http-transport.md +99 -0
  27. limes_guard-0.17.1/docs/decisions/0008-mask-styles.md +92 -0
  28. limes_guard-0.17.1/docs/decisions/0009-egress-corpus-synthetic-only.md +85 -0
  29. limes_guard-0.17.1/docs/decisions/0010-vendor-key-vectors-are-stored-assembled.md +85 -0
  30. limes_guard-0.17.1/docs/decisions/0011-a-crash-is-not-a-verdict.md +114 -0
  31. limes_guard-0.17.1/docs/decisions/0012-the-egress-leg-scans-for-injection.md +89 -0
  32. limes_guard-0.17.1/docs/decisions/0013-a-classifier-layer-enters-by-the-same-gate.md +67 -0
  33. limes_guard-0.17.1/docs/decisions/0014-the-package-names-its-own-version.md +116 -0
  34. limes_guard-0.17.1/docs/decisions/0015-a-ratchet-reports-its-own-blind-spot.md +102 -0
  35. limes_guard-0.17.1/docs/decisions/0016-apache-2-throughout.md +70 -0
  36. limes_guard-0.17.1/docs/decisions/0017-an-adversary-we-did-not-write.md +148 -0
  37. limes_guard-0.17.1/docs/decisions/0018-the-leg-selects-its-detectors.md +113 -0
  38. limes_guard-0.17.1/docs/decisions/0019-one-override-family-for-both-legs.md +74 -0
  39. limes_guard-0.17.1/docs/decisions/0020-a-comparison-on-ground-nobody-here-owns.md +258 -0
  40. limes_guard-0.17.1/docs/decisions/0021-the-outbound-leg-meets-an-adversary-too.md +416 -0
  41. limes_guard-0.17.1/docs/decisions/0022-an-unprefixed-secret-is-caught-by-its-name-or-not-at-all.md +225 -0
  42. limes_guard-0.17.1/docs/decisions/0023-a-scan-is-bounded-in-time-not-only-in-size.md +106 -0
  43. limes_guard-0.17.1/docs/decisions/0024-the-relay-owns-every-tool-call-and-its-own-annotation.md +97 -0
  44. limes_guard-0.17.1/docs/decisions/0025-the-chain-seals-the-disposition-and-the-witness-names-its-build.md +74 -0
  45. limes_guard-0.17.1/docs/decisions/0026-a-blind-spot-names-its-cause.md +118 -0
  46. limes_guard-0.17.1/docs/decisions/0027-a-witness-names-its-leg.md +76 -0
  47. limes_guard-0.17.1/docs/decisions/0028-the-guard-says-what-it-cannot-read.md +169 -0
  48. limes_guard-0.17.1/docs/decisions/0029-a-declared-limit-names-its-family.md +157 -0
  49. limes_guard-0.17.1/docs/decisions/0030-the-outbound-leg-meets-its-competitors.md +251 -0
  50. limes_guard-0.17.1/docs/decisions/0031-the-distribution-takes-the-name-the-index-allows.md +82 -0
  51. limes_guard-0.17.1/docs/design/mcp-proxy-v0.2.md +134 -0
  52. limes_guard-0.17.1/docs/guarding-an-mcp-server.md +157 -0
  53. limes_guard-0.17.1/docs/images/how-it-works.png +0 -0
  54. limes_guard-0.17.1/docs/images/how-it-works.svg +69 -0
  55. limes_guard-0.17.1/docs/measuring-detection.md +143 -0
  56. limes_guard-0.17.1/docs/threat-model.md +172 -0
  57. limes_guard-0.17.1/docs/writing-a-detector.md +161 -0
  58. limes_guard-0.17.1/eval/corpus/garak/PROVENANCE.md +116 -0
  59. limes_guard-0.17.1/eval/corpus/garak/latent_injection.json +6160 -0
  60. limes_guard-0.17.1/eval/corpus/garak/latent_jailbreak.json +1043 -0
  61. limes_guard-0.17.1/eval/corpus/garak/prompt_hijack.json +349 -0
  62. limes_guard-0.17.1/eval/corpus/gitleaks/PROVENANCE.md +124 -0
  63. limes_guard-0.17.1/eval/corpus/gitleaks/secret_formats.json +8979 -0
  64. limes_guard-0.17.1/eval/corpus/presidio/PROVENANCE.md +146 -0
  65. limes_guard-0.17.1/eval/corpus/presidio/pii_templates.json +417 -0
  66. limes_guard-0.17.1/eval/matrices/comparison/PROVENANCE.md +245 -0
  67. limes_guard-0.17.1/eval/matrices/comparison/deberta-injection-v2.results.json +4516 -0
  68. limes_guard-0.17.1/eval/matrices/comparison/deepset-injection.results.json +4516 -0
  69. limes_guard-0.17.1/eval/matrices/comparison/llm-guard.results.json +6007 -0
  70. limes_guard-0.17.1/eval/matrices/comparison/mcp-firewall.results.json +4511 -0
  71. limes_guard-0.17.1/eval/matrices/comparison/pii/mcp-firewall.results.json +2199 -0
  72. limes_guard-0.17.1/eval/matrices/comparison/pii/presidio.results.json +7506 -0
  73. limes_guard-0.17.1/eval/matrices/comparison/rebuff.results.json +1530 -0
  74. limes_guard-0.17.1/eval/matrices/comparison/secrets/detect-secrets.results.json +27391 -0
  75. limes_guard-0.17.1/eval/matrices/comparison/secrets/gitleaks.results.json +2318 -0
  76. limes_guard-0.17.1/eval/matrices/comparison/secrets/mcp-firewall.results.json +1095 -0
  77. limes_guard-0.17.1/eval/matrices/comparison.md +218 -0
  78. limes_guard-0.17.1/eval/matrices/comparison_pii.md +84 -0
  79. limes_guard-0.17.1/eval/matrices/comparison_secrets.md +113 -0
  80. limes_guard-0.17.1/eval/matrices/external_injection.md +74 -0
  81. limes_guard-0.17.1/eval/matrices/external_pii.md +54 -0
  82. limes_guard-0.17.1/eval/matrices/external_secrets.md +52 -0
  83. limes_guard-0.17.1/eval/matrices/injection.md +29 -0
  84. limes_guard-0.17.1/eval/matrices/injection_egress.md +56 -0
  85. limes_guard-0.17.1/eval/matrices/pii_egress.md +46 -0
  86. limes_guard-0.17.1/eval/matrices/secrets_egress.md +43 -0
  87. limes_guard-0.17.1/pyproject.toml +191 -0
  88. limes_guard-0.17.1/scripts/bench_competitors.py +1506 -0
  89. limes_guard-0.17.1/scripts/gate.sh +45 -0
  90. limes_guard-0.17.1/scripts/vendor_garak_corpus.py +438 -0
  91. limes_guard-0.17.1/scripts/vendor_gitleaks_corpus.py +397 -0
  92. limes_guard-0.17.1/scripts/vendor_presidio_corpus.py +279 -0
  93. limes_guard-0.17.1/src/limes/__init__.py +103 -0
  94. limes_guard-0.17.1/src/limes/baselines/__init__.py +12 -0
  95. limes_guard-0.17.1/src/limes/baselines/tessera_pii.py +125 -0
  96. limes_guard-0.17.1/src/limes/baselines/tessera_regex.py +48 -0
  97. limes_guard-0.17.1/src/limes/cli.py +300 -0
  98. limes_guard-0.17.1/src/limes/corpus/PROVENANCE.md +28 -0
  99. limes_guard-0.17.1/src/limes/corpus/benign/benign_inputs.json +10 -0
  100. limes_guard-0.17.1/src/limes/corpus/egress/_schema.json +104 -0
  101. limes_guard-0.17.1/src/limes/corpus/egress/injection_benign.json +147 -0
  102. limes_guard-0.17.1/src/limes/corpus/egress/injection_positive.json +135 -0
  103. limes_guard-0.17.1/src/limes/corpus/egress/pii_benign.json +189 -0
  104. limes_guard-0.17.1/src/limes/corpus/egress/pii_positive.json +287 -0
  105. limes_guard-0.17.1/src/limes/corpus/egress/secrets_benign.json +245 -0
  106. limes_guard-0.17.1/src/limes/corpus/egress/secrets_positive.json +247 -0
  107. limes_guard-0.17.1/src/limes/corpus/injection/01_prompt_injection.json +35 -0
  108. limes_guard-0.17.1/src/limes/corpus/injection/06_prompt_injection_virement.json +37 -0
  109. limes_guard-0.17.1/src/limes/corpus/injection/07_prompt_injection_roleplay.json +31 -0
  110. limes_guard-0.17.1/src/limes/corpus/injection/08_prompt_injection_beneficiaire.json +37 -0
  111. limes_guard-0.17.1/src/limes/corpus/injection/09_prompt_injection_multilingue.json +28 -0
  112. limes_guard-0.17.1/src/limes/corpus/injection/10_prompt_injection_motif.json +30 -0
  113. limes_guard-0.17.1/src/limes/corpus/injection/11_prompt_injection_base64.json +29 -0
  114. limes_guard-0.17.1/src/limes/corpus/injection/41_prompt_injection_rag_poison.json +45 -0
  115. limes_guard-0.17.1/src/limes/corpus/injection/42_prompt_injection_email_zeroclick.json +46 -0
  116. limes_guard-0.17.1/src/limes/corpus/injection/43_prompt_injection_binding_offer.json +44 -0
  117. limes_guard-0.17.1/src/limes/corpus/injection/44_prompt_injection_system_prompt_leak.json +46 -0
  118. limes_guard-0.17.1/src/limes/corpus/injection/_schema.json +125 -0
  119. limes_guard-0.17.1/src/limes/detector.py +145 -0
  120. limes_guard-0.17.1/src/limes/detectors/__init__.py +37 -0
  121. limes_guard-0.17.1/src/limes/detectors/checksums.py +192 -0
  122. limes_guard-0.17.1/src/limes/detectors/egress.yaml +388 -0
  123. limes_guard-0.17.1/src/limes/detectors/egress_policy.py +373 -0
  124. limes_guard-0.17.1/src/limes/detectors/egress_scan.py +154 -0
  125. limes_guard-0.17.1/src/limes/detectors/injection.py +93 -0
  126. limes_guard-0.17.1/src/limes/detectors/injection_egress.py +96 -0
  127. limes_guard-0.17.1/src/limes/detectors/pii_egress.py +86 -0
  128. limes_guard-0.17.1/src/limes/detectors/policy.yaml +175 -0
  129. limes_guard-0.17.1/src/limes/detectors/readability.py +285 -0
  130. limes_guard-0.17.1/src/limes/detectors/secrets_egress.py +104 -0
  131. limes_guard-0.17.1/src/limes/eval/__init__.py +10 -0
  132. limes_guard-0.17.1/src/limes/eval/comparison_harness.py +1482 -0
  133. limes_guard-0.17.1/src/limes/eval/corpus.py +66 -0
  134. limes_guard-0.17.1/src/limes/eval/egress_comparison_harness.py +1319 -0
  135. limes_guard-0.17.1/src/limes/eval/egress_corpus.py +267 -0
  136. limes_guard-0.17.1/src/limes/eval/egress_harness.py +503 -0
  137. limes_guard-0.17.1/src/limes/eval/external_corpus.py +229 -0
  138. limes_guard-0.17.1/src/limes/eval/external_egress_corpus.py +358 -0
  139. limes_guard-0.17.1/src/limes/eval/external_egress_harness.py +591 -0
  140. limes_guard-0.17.1/src/limes/eval/external_harness.py +442 -0
  141. limes_guard-0.17.1/src/limes/eval/harness.py +260 -0
  142. limes_guard-0.17.1/src/limes/eval/power.py +98 -0
  143. limes_guard-0.17.1/src/limes/guard.py +193 -0
  144. limes_guard-0.17.1/src/limes/policy.py +195 -0
  145. limes_guard-0.17.1/src/limes/py.typed +0 -0
  146. limes_guard-0.17.1/src/limes/record.py +166 -0
  147. limes_guard-0.17.1/src/limes/registry.py +28 -0
  148. limes_guard-0.17.1/src/limes/spans.py +57 -0
  149. limes_guard-0.17.1/src/limes/transports/__init__.py +25 -0
  150. limes_guard-0.17.1/src/limes/transports/in_process.py +355 -0
  151. limes_guard-0.17.1/src/limes/transports/mcp/__init__.py +81 -0
  152. limes_guard-0.17.1/src/limes/transports/mcp/__main__.py +12 -0
  153. limes_guard-0.17.1/src/limes/transports/mcp/bench.py +175 -0
  154. limes_guard-0.17.1/src/limes/transports/mcp/bench_http.py +214 -0
  155. limes_guard-0.17.1/src/limes/transports/mcp/bridge.py +1065 -0
  156. limes_guard-0.17.1/src/limes/transports/mcp/cli.py +263 -0
  157. limes_guard-0.17.1/src/limes/transports/mcp/config.py +276 -0
  158. limes_guard-0.17.1/src/limes/transports/mcp/decision.py +258 -0
  159. limes_guard-0.17.1/src/limes/transports/mcp/http.py +501 -0
  160. limes_guard-0.17.1/src/limes/transports/mcp/payload.py +305 -0
  161. limes_guard-0.17.1/src/limes/transports/mcp/sink.py +129 -0
  162. limes_guard-0.17.1/src/limes/transports/redaction.py +610 -0
  163. limes_guard-0.17.1/src/limes/verdict.py +300 -0
  164. limes_guard-0.17.1/tests/__init__.py +0 -0
  165. limes_guard-0.17.1/tests/conftest.py +21 -0
  166. limes_guard-0.17.1/tests/integration/__init__.py +0 -0
  167. limes_guard-0.17.1/tests/integration/egress/__init__.py +0 -0
  168. limes_guard-0.17.1/tests/integration/egress/guarded_http_proxy.py +32 -0
  169. limes_guard-0.17.1/tests/integration/egress/guarded_proxy.py +45 -0
  170. limes_guard-0.17.1/tests/integration/egress/test_pii_egress_e2e.py +324 -0
  171. limes_guard-0.17.1/tests/integration/mcp/__init__.py +0 -0
  172. limes_guard-0.17.1/tests/integration/mcp/http_journal_server.py +89 -0
  173. limes_guard-0.17.1/tests/integration/mcp/http_proxy.py +28 -0
  174. limes_guard-0.17.1/tests/integration/mcp/journal_server.py +81 -0
  175. limes_guard-0.17.1/tests/integration/mcp/poisoned_server.py +88 -0
  176. limes_guard-0.17.1/tests/integration/mcp/redacting_http_proxy.py +44 -0
  177. limes_guard-0.17.1/tests/integration/mcp/redacting_proxy.py +38 -0
  178. limes_guard-0.17.1/tests/integration/mcp/test_end_to_end.py +220 -0
  179. limes_guard-0.17.1/tests/integration/mcp/test_http_e2e.py +416 -0
  180. limes_guard-0.17.1/tests/integration/mcp/test_redaction_e2e.py +212 -0
  181. limes_guard-0.17.1/tests/integration/mcp/test_tools_list_poisoning_e2e.py +132 -0
  182. limes_guard-0.17.1/tests/integration/test_evidence_chain_replay.py +44 -0
  183. limes_guard-0.17.1/tests/unit/__init__.py +0 -0
  184. limes_guard-0.17.1/tests/unit/cli/__init__.py +0 -0
  185. limes_guard-0.17.1/tests/unit/cli/test_check.py +280 -0
  186. limes_guard-0.17.1/tests/unit/cli/test_leg_selects_detectors.py +74 -0
  187. limes_guard-0.17.1/tests/unit/comparison/test_comparison_harness.py +359 -0
  188. limes_guard-0.17.1/tests/unit/comparison/test_egress_comparison_harness.py +445 -0
  189. limes_guard-0.17.1/tests/unit/egress/__init__.py +0 -0
  190. limes_guard-0.17.1/tests/unit/egress/test_aws_secret_context_rule.py +264 -0
  191. limes_guard-0.17.1/tests/unit/egress/test_checksums.py +164 -0
  192. limes_guard-0.17.1/tests/unit/egress/test_corpus_is_synthetic.py +187 -0
  193. limes_guard-0.17.1/tests/unit/egress/test_egress_policy.py +185 -0
  194. limes_guard-0.17.1/tests/unit/egress/test_external_egress_corpus.py +153 -0
  195. limes_guard-0.17.1/tests/unit/egress/test_grader_integrity.py +112 -0
  196. limes_guard-0.17.1/tests/unit/egress/test_no_raw_secret_literal_in_corpus_file.py +94 -0
  197. limes_guard-0.17.1/tests/unit/egress/test_no_raw_value_in_records.py +120 -0
  198. limes_guard-0.17.1/tests/unit/egress/test_pii_detector.py +201 -0
  199. limes_guard-0.17.1/tests/unit/egress/test_precision_gates_are_load_bearing.py +203 -0
  200. limes_guard-0.17.1/tests/unit/egress/test_secrets_detector.py +218 -0
  201. limes_guard-0.17.1/tests/unit/external/test_external_corpus.py +143 -0
  202. limes_guard-0.17.1/tests/unit/external/test_external_harness.py +55 -0
  203. limes_guard-0.17.1/tests/unit/mcp/__init__.py +0 -0
  204. limes_guard-0.17.1/tests/unit/mcp/harness.py +258 -0
  205. limes_guard-0.17.1/tests/unit/mcp/test_cli.py +143 -0
  206. limes_guard-0.17.1/tests/unit/mcp/test_decision.py +107 -0
  207. limes_guard-0.17.1/tests/unit/mcp/test_http.py +104 -0
  208. limes_guard-0.17.1/tests/unit/mcp/test_outbound_leg_is_wired.py +74 -0
  209. limes_guard-0.17.1/tests/unit/mcp/test_payload.py +52 -0
  210. limes_guard-0.17.1/tests/unit/mcp/test_redaction.py +283 -0
  211. limes_guard-0.17.1/tests/unit/mcp/test_relay.py +787 -0
  212. limes_guard-0.17.1/tests/unit/mcp/test_sink.py +122 -0
  213. limes_guard-0.17.1/tests/unit/ratchets/__init__.py +0 -0
  214. limes_guard-0.17.1/tests/unit/ratchets/test_a_blind_spot_names_its_cause.py +239 -0
  215. limes_guard-0.17.1/tests/unit/ratchets/test_a_crash_is_not_a_verdict.py +54 -0
  216. limes_guard-0.17.1/tests/unit/ratchets/test_a_declared_limit_names_its_family.py +557 -0
  217. limes_guard-0.17.1/tests/unit/ratchets/test_a_witness_names_its_leg.py +137 -0
  218. limes_guard-0.17.1/tests/unit/ratchets/test_allow_needs_evidence_mypy.py +69 -0
  219. limes_guard-0.17.1/tests/unit/ratchets/test_disposition_is_sealed.py +80 -0
  220. limes_guard-0.17.1/tests/unit/ratchets/test_exceptions_frozen_at_zero.py +42 -0
  221. limes_guard-0.17.1/tests/unit/ratchets/test_guard_refuses_unknown.py +49 -0
  222. limes_guard-0.17.1/tests/unit/ratchets/test_inbound_scan_is_budgeted.py +117 -0
  223. limes_guard-0.17.1/tests/unit/ratchets/test_null_result_carries_power.py +43 -0
  224. limes_guard-0.17.1/tests/unit/ratchets/test_scan_cost_is_bounded.py +101 -0
  225. limes_guard-0.17.1/tests/unit/ratchets/test_span_digest_is_total.py +61 -0
  226. limes_guard-0.17.1/tests/unit/ratchets/test_the_guard_says_what_it_cannot_read.py +420 -0
  227. limes_guard-0.17.1/tests/unit/ratchets/test_witness_names_its_build.py +39 -0
  228. limes_guard-0.17.1/tests/unit/redaction/__init__.py +0 -0
  229. limes_guard-0.17.1/tests/unit/redaction/doubles.py +109 -0
  230. limes_guard-0.17.1/tests/unit/redaction/test_in_process.py +161 -0
  231. limes_guard-0.17.1/tests/unit/redaction/test_plan.py +147 -0
  232. limes_guard-0.17.1/tests/unit/redaction/test_policy.py +128 -0
  233. limes_guard-0.17.1/tests/unit/redaction/test_style.py +243 -0
  234. limes_guard-0.17.1/tests/unit/test_admission_rule.py +127 -0
  235. limes_guard-0.17.1/tests/unit/test_corpus_provenance.py +55 -0
  236. limes_guard-0.17.1/tests/unit/test_detector_protocol.py +70 -0
  237. limes_guard-0.17.1/tests/unit/test_frontier.py +953 -0
  238. limes_guard-0.17.1/tests/unit/test_hijack_family_names_its_templates.py +80 -0
  239. limes_guard-0.17.1/tests/unit/test_injection_detector.py +108 -0
  240. limes_guard-0.17.1/tests/unit/test_matrices_match_their_harnesses.py +120 -0
  241. limes_guard-0.17.1/tests/unit/test_power.py +69 -0
  242. limes_guard-0.17.1/tests/unit/test_record_chain.py +54 -0
  243. limes_guard-0.17.1/tests/unit/test_verdict.py +137 -0
  244. limes_guard-0.17.1/uv.lock +989 -0
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: Bug report
3
+ about: A defect in the limes engine, a detector, or a transport
4
+ title: "bug: "
5
+ labels: bug
6
+ ---
7
+
8
+ **Do not use this for security vulnerabilities.** See
9
+ [SECURITY.md](../../SECURITY.md) for private reporting.
10
+
11
+ ## What happened
12
+
13
+ A clear description of the bug.
14
+
15
+ ## Reproduce
16
+
17
+ Minimal steps or a code snippet. If it involves a detector verdict, include the
18
+ input and the `Verdict` / `Evidence` you observed.
19
+
20
+ ```python
21
+ # minimal repro
22
+ ```
23
+
24
+ ## Expected
25
+
26
+ What you expected instead (e.g. `Deny` with evidence, `CannotSay`, `Allow`).
27
+
28
+ ## Environment
29
+
30
+ - limes version / commit:
31
+ - Python version:
32
+ - OS:
33
+
34
+ ## Which layer (best guess)
35
+
36
+ - [ ] Core (verdict algebra, evidence chain)
37
+ - [ ] Detector / policy
38
+ - [ ] Transport
39
+ - [ ] Eval harness
40
+ - [ ] Docs
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose a new detector, policy, or transport (the core never grows)
4
+ title: "feat: "
5
+ labels: enhancement
6
+ ---
7
+
8
+ ## What and why
9
+
10
+ What capability is missing, and the problem it solves.
11
+
12
+ ## Which layer (ADR 0004)
13
+
14
+ A new capability is a plugin, never a change to the core.
15
+
16
+ - [ ] Detector (new detection capability)
17
+ - [ ] Policy (new rules for an existing detector)
18
+ - [ ] Transport (new integration surface — MCP, HTTP, …)
19
+ - [ ] Something that seems to need a core change → it needs a new ADR; say why
20
+
21
+ ## If this is a detector: the admission rule (ADR 0003)
22
+
23
+ A detector ships only with its **two numbers**. Sketch how you would measure it:
24
+
25
+ - **Positive corpus** — the attacks it catches:
26
+ - **Benign corpus** — legitimate traffic it must not kill:
27
+ - **Null control** — how it beats doing nothing, and how it avoids block-everything:
28
+
29
+ A detector without a benign corpus and a null control will not be admitted,
30
+ however easy it looks.
@@ -0,0 +1,20 @@
1
+ ## What this changes
2
+
3
+ A short description, and the motivation. Link any issue or ADR.
4
+
5
+ ## Checklist
6
+
7
+ - [ ] `make gate` is green locally (ruff + ruff format --check + mypy --strict + pytest).
8
+ - [ ] Commits follow **Conventional Commits** (`feat:`, `fix:`, `docs:`, …).
9
+ - [ ] Structural change? It references or adds an ADR under `docs/decisions/`.
10
+ - [ ] I have signed the [CLA](CLA.md) (required before a first merge).
11
+
12
+ ## If this adds or changes a detector (ADR 0003)
13
+
14
+ - [ ] Positive corpus added / updated.
15
+ - [ ] Benign corpus added / updated.
16
+ - [ ] Null control measured (vs unplugged **and** vs block-everything).
17
+ - [ ] Confusion matrix regenerated (`make eval`) and committed under `eval/matrices/`.
18
+ - [ ] The two numbers are in the PR description below.
19
+
20
+ **The two numbers:** attacks blocked = `…`, legitimate traffic killed = `…`.
@@ -0,0 +1,60 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ gate:
18
+ name: gate (ruff · mypy --strict · pytest) — py${{ matrix.python-version }}
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ # Every supported Python reports for itself. One red must not hide the others.
22
+ fail-fast: false
23
+ matrix:
24
+ # These are exactly the `Programming Language :: Python` classifiers in
25
+ # pyproject.toml, and `requires-python = ">=3.12"` is the claim they witness.
26
+ # A version claimed and never run is a promise with no witness.
27
+ python-version: ["3.12", "3.13", "3.14"]
28
+ env:
29
+ # uv reads this for both `uv sync` and the `uv run` calls inside make gate,
30
+ # so the whole gate runs on the matrix version rather than on .python-version.
31
+ UV_PYTHON: ${{ matrix.python-version }}
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ with:
35
+ # NOT the default depth 1. tests/unit/test_frontier.py compares today's
36
+ # bytes against the v0.1 commit, read out of git history — and a shallow
37
+ # checkout cannot produce it. With the default, that ratchet failed on
38
+ # every push from v0.1 to v0.8: 16 red runs, one green, on v0.1 itself
39
+ # where the comparison was trivially true. The gate was green locally the
40
+ # whole time, because a normal clone has the history.
41
+ fetch-depth: 0
42
+
43
+ - name: Install uv
44
+ uses: astral-sh/setup-uv@v5
45
+ with:
46
+ enable-cache: true
47
+
48
+ - name: Install the matrix Python
49
+ run: uv python install ${{ matrix.python-version }}
50
+
51
+ - name: Sync dependencies
52
+ run: uv sync
53
+
54
+ - name: Say which tree and which Python this gate judges
55
+ run: |
56
+ uv run python -VV
57
+ make tree
58
+
59
+ - name: Gate — ruff + ruff format --check + mypy --strict + pytest
60
+ run: make gate
@@ -0,0 +1,109 @@
1
+ name: release
2
+
3
+ # Publishing is deliberate: a tag, or a hand-pulled lever. Never a push to main.
4
+ on:
5
+ push:
6
+ tags: ["v*"]
7
+ workflow_dispatch:
8
+ inputs:
9
+ target:
10
+ description: "where to publish"
11
+ required: true
12
+ default: testpypi
13
+ type: choice
14
+ options: [testpypi, pypi]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ # The gate runs again here, on the exact tag, rather than trusting that the
21
+ # commit was green when it landed: a tag can be moved, and the thing published
22
+ # must be the thing judged (ADR 0026 — a witness interrogates the thing).
23
+ gate:
24
+ name: gate before publishing — py${{ matrix.python-version }}
25
+ runs-on: ubuntu-latest
26
+ strategy:
27
+ fail-fast: false
28
+ matrix:
29
+ python-version: ["3.12", "3.13", "3.14"]
30
+ env:
31
+ UV_PYTHON: ${{ matrix.python-version }}
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ with:
35
+ fetch-depth: 0 # the frontier ratchet reads v0.1's bytes from history
36
+ - uses: astral-sh/setup-uv@v5
37
+ with:
38
+ enable-cache: true
39
+ - run: uv python install ${{ matrix.python-version }}
40
+ - run: uv sync
41
+ - run: make gate
42
+
43
+ build:
44
+ name: build sdist + wheel
45
+ needs: gate
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ with:
50
+ fetch-depth: 0
51
+ - uses: astral-sh/setup-uv@v5
52
+
53
+ - name: Build
54
+ run: uv build
55
+
56
+ - name: The tag and the version must be the same number
57
+ if: startsWith(github.ref, 'refs/tags/v')
58
+ run: |
59
+ set -euo pipefail
60
+ tag="${GITHUB_REF_NAME#v}"
61
+ built="$(uv run --no-project --with tomli python -c \
62
+ 'import tomli;print(tomli.load(open("pyproject.toml","rb"))["project"]["version"])')"
63
+ echo "tag=$tag pyproject=$built"
64
+ # A tag that names a different version than the artifact it publishes is
65
+ # a release nobody can trace back: the sdist would claim one number and
66
+ # the git history another.
67
+ test "$tag" = "$built"
68
+
69
+ - name: Metadata must render on PyPI
70
+ run: uvx twine check --strict dist/*
71
+
72
+ - uses: actions/upload-artifact@v4
73
+ with:
74
+ name: dist
75
+ path: dist/
76
+
77
+ publish-testpypi:
78
+ name: publish to TestPyPI
79
+ needs: build
80
+ if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
81
+ runs-on: ubuntu-latest
82
+ environment: testpypi
83
+ permissions:
84
+ id-token: write # Trusted Publishing (OIDC): no token on any machine
85
+ steps:
86
+ - uses: actions/download-artifact@v4
87
+ with:
88
+ name: dist
89
+ path: dist/
90
+ - uses: pypa/gh-action-pypi-publish@release/v1
91
+ with:
92
+ repository-url: https://test.pypi.org/legacy/
93
+
94
+ publish-pypi:
95
+ name: publish to PyPI
96
+ needs: build
97
+ if: >-
98
+ startsWith(github.ref, 'refs/tags/v') ||
99
+ (github.event_name == 'workflow_dispatch' && inputs.target == 'pypi')
100
+ runs-on: ubuntu-latest
101
+ environment: pypi
102
+ permissions:
103
+ id-token: write # Trusted Publishing (OIDC): no token on any machine
104
+ steps:
105
+ - uses: actions/download-artifact@v4
106
+ with:
107
+ name: dist
108
+ path: dist/
109
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,18 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .venv/
6
+ dist/
7
+ build/
8
+
9
+ # Tooling caches
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .pytest_cache/
13
+
14
+ # OS
15
+ .DS_Store
16
+
17
+ # Eval scratch (the published matrices under eval/matrices/*.md ARE tracked)
18
+ *.tmp
@@ -0,0 +1,42 @@
1
+ # limes — secret-scanner configuration.
2
+ #
3
+ # limes is a package about credentials leaving a system, so its corpus and its
4
+ # tests necessarily CONTAIN credential-shaped strings: that is the material under
5
+ # test. A scanner reading them as leaks reports eleven findings on a repository
6
+ # with no secret in it, and the cost of that noise is not zero — a reader who
7
+ # learns to dismiss this scanner will dismiss it on the day it is right.
8
+ #
9
+ # So the exemptions are named one file at a time, with what each holds. They are
10
+ # NOT a wildcard over tests/: a real key pasted into a new test must still ring.
11
+ #
12
+ # ADR 0010 already removed the worst of it — vendor key vectors are stored as
13
+ # fragments the loader joins, so no contiguous credential literal sits in the
14
+ # corpus. What remains below is what could not be assembled away: PEM headers,
15
+ # a standard example JWT, and one obviously-fake key reused across four tests.
16
+ #
17
+ # Verify with: gitleaks detect --source . --redact -v
18
+
19
+ title = "limes"
20
+
21
+ [extend]
22
+ useDefault = true
23
+
24
+ [[allowlists]]
25
+ description = """
26
+ Synthetic detection material, verified: the base64 bodies decode to
27
+ 'EXAMPLE ONLY - not a key - synthetic' and 'PLACEHOLDER - synthetic - not a key',
28
+ the JWT is the canonical example.com token (sub 1234567890, name Example), and
29
+ the API key is the eight-character literal sk-live-AB12cd34. None is or ever was
30
+ live. Each file is the input of a test that must find exactly these strings.
31
+ """
32
+ paths = [
33
+ # The egress corpus: PEM headers and an example JWT the secrets detector must span.
34
+ '''^src/limes/corpus/egress/secrets_positive\.json$''',
35
+ # The secrets detector's own unit cases (the same PEM headers).
36
+ '''^tests/unit/egress/test_secrets_detector\.py$''',
37
+ # KEY = "sk-live-AB12cd34" — the payload the redaction tests mask and assert on.
38
+ '''^tests/unit/redaction/test_style\.py$''',
39
+ '''^tests/unit/mcp/test_redaction\.py$''',
40
+ '''^tests/integration/mcp/test_redaction_e2e\.py$''',
41
+ '''^tests/integration/mcp/test_http_e2e\.py$''',
42
+ ]
@@ -0,0 +1,28 @@
1
+ # limes pre-commit — the same checks make gate runs, at commit time.
2
+ repos:
3
+ - repo: local
4
+ hooks:
5
+ - id: ruff-check
6
+ name: ruff check
7
+ entry: uv run ruff check --fix
8
+ language: system
9
+ types: [python]
10
+ pass_filenames: false
11
+ - id: ruff-format
12
+ name: ruff format
13
+ entry: uv run ruff format
14
+ language: system
15
+ types: [python]
16
+ pass_filenames: false
17
+ - id: mypy
18
+ name: mypy --strict
19
+ entry: uv run mypy
20
+ language: system
21
+ types: [python]
22
+ pass_filenames: false
23
+ - id: pytest
24
+ name: pytest
25
+ entry: uv run pytest -q
26
+ language: system
27
+ pass_filenames: false
28
+ stages: [pre-push]
@@ -0,0 +1 @@
1
+ 3.12