interlockd 0.1.0__tar.gz → 0.1.2__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 (253) hide show
  1. interlockd-0.1.2/.github/ISSUE_TEMPLATE/agent.yml +20 -0
  2. interlockd-0.1.2/.github/ISSUE_TEMPLATE/bug.yml +29 -0
  3. interlockd-0.1.2/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. interlockd-0.1.2/.github/ISSUE_TEMPLATE/decision.yml +34 -0
  5. interlockd-0.1.2/.github/dependabot.yml +9 -0
  6. interlockd-0.1.2/.github/pull_request_template.md +10 -0
  7. {interlockd-0.1.0 → interlockd-0.1.2}/.github/workflows/ci.yml +18 -6
  8. interlockd-0.1.2/.github/workflows/release.yml +95 -0
  9. interlockd-0.1.2/CHANGELOG.md +58 -0
  10. interlockd-0.1.2/CODE_OF_CONDUCT.md +10 -0
  11. {interlockd-0.1.0 → interlockd-0.1.2}/CONTRIBUTING.md +7 -1
  12. interlockd-0.1.2/NOTICE +7 -0
  13. interlockd-0.1.0/README.md → interlockd-0.1.2/PKG-INFO +64 -9
  14. interlockd-0.1.0/PKG-INFO → interlockd-0.1.2/README.md +20 -39
  15. {interlockd-0.1.0 → interlockd-0.1.2}/SECURITY.md +11 -0
  16. {interlockd-0.1.0 → interlockd-0.1.2}/action.yml +5 -4
  17. interlockd-0.1.2/benchmarks/agentdojo_template.py +49 -0
  18. interlockd-0.1.2/benchmarks/injecagent.py +89 -0
  19. interlockd-0.1.2/benchmarks/nl2bash.py +45 -0
  20. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/_corpus.yaml +1 -1
  21. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/_corpus.yaml +1 -1
  22. interlockd-0.1.2/demo/demo.sh +23 -0
  23. interlockd-0.1.2/demo/demo.tape +8 -0
  24. {interlockd-0.1.0 → interlockd-0.1.2}/docs/ADAPTERS.md +9 -3
  25. interlockd-0.1.2/docs/BENCHMARKS.md +85 -0
  26. interlockd-0.1.2/docs/GETTING-STARTED.md +119 -0
  27. interlockd-0.1.2/docs/PERFORMANCE.md +33 -0
  28. {interlockd-0.1.0 → interlockd-0.1.2}/docs/THREAT-MODEL.md +3 -2
  29. {interlockd-0.1.0 → interlockd-0.1.2}/docs/USER-GUIDE.md +78 -11
  30. {interlockd-0.1.0 → interlockd-0.1.2}/docs/mandate-example.cedar +4 -0
  31. interlockd-0.1.2/examples/agents/README.md +17 -0
  32. interlockd-0.1.2/examples/agents/claude_agent_sdk_agent.py +18 -0
  33. interlockd-0.1.2/examples/agents/crewai_crew.py +21 -0
  34. interlockd-0.1.2/examples/agents/google_adk_agent.py +15 -0
  35. interlockd-0.1.2/examples/agents/langgraph_agent.py +19 -0
  36. interlockd-0.1.2/examples/agents/openai_agents.py +17 -0
  37. interlockd-0.1.2/examples/agents/semantic_kernel_agent.py +24 -0
  38. interlockd-0.1.2/examples/agents/strands_agent.py +17 -0
  39. interlockd-0.1.2/examples/github-workflow.yml +16 -0
  40. interlockd-0.1.2/examples/policies/ci.yaml +11 -0
  41. interlockd-0.1.2/examples/policies/mcp-locked-down.yaml +15 -0
  42. interlockd-0.1.2/examples/policies/strict-team.yaml +29 -0
  43. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/__init__.py +1 -1
  44. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/api.py +14 -1
  45. interlockd-0.1.2/interlockd/bench.py +113 -0
  46. interlockd-0.1.2/interlockd/capture.py +183 -0
  47. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/cli.py +224 -9
  48. interlockd-0.1.2/interlockd/control.py +69 -0
  49. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/shell.jsonl +3 -1
  50. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/describe.py +7 -0
  51. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/engine.py +6 -1
  52. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/mandate.py +1 -1
  53. interlockd-0.1.2/interlockd/det/oauth.py +126 -0
  54. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/packs.py +6 -1
  55. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/insights.py +111 -24
  56. interlockd-0.1.2/interlockd/integrations/claude_agent_sdk.py +84 -0
  57. interlockd-0.1.2/interlockd/integrations/crewai.py +79 -0
  58. interlockd-0.1.2/interlockd/integrations/google_adk.py +62 -0
  59. interlockd-0.1.2/interlockd/integrations/semantic_kernel.py +61 -0
  60. interlockd-0.1.2/interlockd/judge_setup.py +120 -0
  61. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/judges/local_probe.py +22 -8
  62. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/mcp_http.py +121 -6
  63. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/normalize/claude_code.py +1 -1
  64. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/onboard.py +2 -2
  65. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/pipeline.py +39 -6
  66. interlockd-0.1.2/interlockd/plugins.py +129 -0
  67. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/policy/__init__.py +8 -0
  68. interlockd-0.1.2/interlockd/policy/managed.py +131 -0
  69. interlockd-0.1.2/interlockd/policy/workspace.py +268 -0
  70. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/policy_lint.py +1 -1
  71. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/shims/claude-code/interlock-hook.sh +1 -1
  72. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/shims/kiro/interlock-hook.mjs +1 -1
  73. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/shims/kiro/interlock-hook.sh +1 -1
  74. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/store/spool.py +35 -10
  75. interlockd-0.1.2/interlockd/watch.py +78 -0
  76. interlockd-0.1.2/packaging/homebrew/Formula/interlock.rb +57 -0
  77. {interlockd-0.1.0 → interlockd-0.1.2}/packaging/homebrew/README.md +5 -1
  78. {interlockd-0.1.0 → interlockd-0.1.2}/pyproject.toml +14 -3
  79. interlockd-0.1.2/sdk/js/README.md +48 -0
  80. interlockd-0.1.2/sdk/js/package.json +14 -0
  81. interlockd-0.1.2/sdk/js/src/index.d.ts +38 -0
  82. interlockd-0.1.2/sdk/js/src/index.js +159 -0
  83. {interlockd-0.1.0 → interlockd-0.1.2}/tests/conftest.py +7 -0
  84. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_api.py +4 -1
  85. interlockd-0.1.2/tests/test_bench.py +17 -0
  86. interlockd-0.1.2/tests/test_capture.py +59 -0
  87. interlockd-0.1.2/tests/test_claude_agent_sdk.py +77 -0
  88. interlockd-0.1.2/tests/test_control.py +64 -0
  89. interlockd-0.1.2/tests/test_crewai.py +63 -0
  90. interlockd-0.1.2/tests/test_examples.py +27 -0
  91. interlockd-0.1.2/tests/test_google_adk.py +92 -0
  92. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_insights.py +9 -0
  93. interlockd-0.1.2/tests/test_js_sdk.py +106 -0
  94. interlockd-0.1.2/tests/test_judge_setup.py +33 -0
  95. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_local_probe.py +26 -0
  96. interlockd-0.1.2/tests/test_managed_policy.py +129 -0
  97. interlockd-0.1.2/tests/test_mandate.py +32 -0
  98. interlockd-0.1.2/tests/test_mcp_oauth.py +184 -0
  99. interlockd-0.1.2/tests/test_plugins.py +108 -0
  100. interlockd-0.1.2/tests/test_repo_policy.py +126 -0
  101. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_sandbox.py +26 -0
  102. interlockd-0.1.2/tests/test_semantic_kernel.py +72 -0
  103. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_spool.py +11 -0
  104. interlockd-0.1.2/tests/test_version.py +36 -0
  105. interlockd-0.1.2/tests/test_watch.py +56 -0
  106. interlockd-0.1.0/.github/workflows/release.yml +0 -54
  107. interlockd-0.1.0/CHANGELOG.md +0 -28
  108. {interlockd-0.1.0 → interlockd-0.1.2}/.gitignore +0 -0
  109. {interlockd-0.1.0 → interlockd-0.1.2}/LICENSE +0 -0
  110. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/README.md +0 -0
  111. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/antigravity/PAYLOADS.md +0 -0
  112. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/claude-code/PAYLOADS.md +0 -0
  113. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/claude-code/README.md +0 -0
  114. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/codex/PAYLOADS.md +0 -0
  115. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/codex/README.md +0 -0
  116. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/copilot-cli/PAYLOADS.md +0 -0
  117. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/cursor/PAYLOADS.md +0 -0
  118. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/gemini-cli/PAYLOADS.md +0 -0
  119. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/kiro/PAYLOADS.md +0 -0
  120. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/opencode/PAYLOADS.md +0 -0
  121. {interlockd-0.1.0 → interlockd-0.1.2}/adapters/windsurf/PAYLOADS.md +0 -0
  122. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/README.md +0 -0
  123. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/exfil_and_secrets.jsonl +0 -0
  124. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/git_and_history.jsonl +0 -0
  125. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/injection_persistence_supplychain.jsonl +0 -0
  126. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/judge_confirmation.jsonl +0 -0
  127. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/judge_exfil.jsonl +0 -0
  128. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/judge_injection.jsonl +0 -0
  129. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/judge_intent.jsonl +0 -0
  130. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/archetypes/slow_hijack.jsonl +0 -0
  131. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/external/README.md +0 -0
  132. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/external/samples/agentdojo.sample.json +0 -0
  133. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/external/samples/agentdrift.sample.json +0 -0
  134. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/external/samples/injecagent.sample.json +0 -0
  135. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/ogr-seed-v0/README.md +0 -0
  136. {interlockd-0.1.0 → interlockd-0.1.2}/corpus/ogr-seed-v0/seed-v0.jsonl +0 -0
  137. {interlockd-0.1.0 → interlockd-0.1.2}/docs/policy-example.yaml +0 -0
  138. {interlockd-0.1.0 → interlockd-0.1.2}/docs/reports/M3-judge-calibration.md +0 -0
  139. {interlockd-0.1.0 → interlockd-0.1.2}/docs/reports/ogr-seed-v0-results.jsonl +0 -0
  140. {interlockd-0.1.0 → interlockd-0.1.2}/docs/reports/ogr-seed-v0-results.md +0 -0
  141. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/__main__.py +0 -0
  142. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/aibom.py +0 -0
  143. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/auth.py +0 -0
  144. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/calibration.py +0 -0
  145. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/compose.py +0 -0
  146. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/config.py +0 -0
  147. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/corpus.py +0 -0
  148. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/_corpus.yaml +0 -0
  149. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/egress.jsonl +0 -0
  150. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/fileops.jsonl +0 -0
  151. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/git.jsonl +0 -0
  152. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/packages.jsonl +0 -0
  153. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/persistence.jsonl +0 -0
  154. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/data/packs/secrets.jsonl +0 -0
  155. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/__init__.py +0 -0
  156. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/flow.py +0 -0
  157. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/mcp.py +0 -0
  158. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/memory.py +0 -0
  159. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/det/shellparse.py +0 -0
  160. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/egress.py +0 -0
  161. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/gateway.py +0 -0
  162. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/ids.py +0 -0
  163. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/integrations/__init__.py +0 -0
  164. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/integrations/client.py +0 -0
  165. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/integrations/langchain.py +0 -0
  166. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/integrations/openai_agents.py +0 -0
  167. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/integrations/strands.py +0 -0
  168. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/judges/__init__.py +0 -0
  169. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/judges/guardrails.py +0 -0
  170. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/judges/projector.py +0 -0
  171. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/judges/strands_decider.py +0 -0
  172. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/ledger.py +0 -0
  173. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/mcp_proxy.py +0 -0
  174. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/mcp_server.py +0 -0
  175. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/models.py +0 -0
  176. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/normalize/__init__.py +0 -0
  177. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/normalize/cursor.py +0 -0
  178. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/normalize/gemini_cli.py +0 -0
  179. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/normalize/kiro.py +0 -0
  180. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/normalize/more.py +0 -0
  181. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/ogr.py +0 -0
  182. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/otel.py +0 -0
  183. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/paths.py +0 -0
  184. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/render.py +0 -0
  185. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/sandbox.py +0 -0
  186. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/scan.py +0 -0
  187. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/schema/verdict.schema.json +0 -0
  188. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/selftest.py +0 -0
  189. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/service.py +0 -0
  190. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/shims/opencode/interlock.js +0 -0
  191. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/store/__init__.py +0 -0
  192. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/store/drainer.py +0 -0
  193. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/store/dynamodb.py +0 -0
  194. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/store/memory.py +0 -0
  195. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/testkit.py +0 -0
  196. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/tiers.py +0 -0
  197. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/transcript/__init__.py +0 -0
  198. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/transcript/claude_code.py +0 -0
  199. {interlockd-0.1.0 → interlockd-0.1.2}/interlockd/transcript/kiro.py +0 -0
  200. {interlockd-0.1.0 → interlockd-0.1.2}/packaging/homebrew/make_formula.py +0 -0
  201. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fake_mcp_server.py +0 -0
  202. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/claude-code/post-bash-inject.json +0 -0
  203. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/claude-code/post-read-inject.json +0 -0
  204. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/claude-code/pre-bash-block.json +0 -0
  205. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/claude-code/pre-bash-inject.json +0 -0
  206. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/codex/post-bash-benign.json +0 -0
  207. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/codex/post-bash-inject.json +0 -0
  208. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/codex/pre-bash-benign.json +0 -0
  209. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/codex/pre-bash-block.json +0 -0
  210. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/copilot-cli/post-bash-inject.json +0 -0
  211. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/copilot-cli/pre-bash-benign.json +0 -0
  212. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/copilot-cli/pre-bash-block.json +0 -0
  213. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/cursor/post-read-benign.json +0 -0
  214. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/cursor/post-shell-benign.json +0 -0
  215. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/cursor/pre-read-benign.json +0 -0
  216. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/cursor/pre-shell-benign.json +0 -0
  217. {interlockd-0.1.0 → interlockd-0.1.2}/tests/fixtures/cursor/pre-shell-block.json +0 -0
  218. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_aibom.py +0 -0
  219. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_audit_chain.py +0 -0
  220. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_calibration.py +0 -0
  221. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_claude_code.py +0 -0
  222. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_corpus.py +0 -0
  223. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_cursor_gemini.py +0 -0
  224. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_dynamodb.py +0 -0
  225. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_egress.py +0 -0
  226. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_flow.py +0 -0
  227. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_gateway.py +0 -0
  228. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_homebrew_formula.py +0 -0
  229. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_interlock_test.py +0 -0
  230. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_judge.py +0 -0
  231. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_langchain.py +0 -0
  232. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_mcp_http.py +0 -0
  233. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_mcp_policy.py +0 -0
  234. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_mcp_proxy.py +0 -0
  235. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_mcp_server.py +0 -0
  236. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_memory.py +0 -0
  237. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_more_adapters.py +0 -0
  238. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_normalize_kiro.py +0 -0
  239. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_ogr.py +0 -0
  240. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_onboard.py +0 -0
  241. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_openai_agents.py +0 -0
  242. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_opencode.py +0 -0
  243. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_otel.py +0 -0
  244. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_packs.py +0 -0
  245. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_pipeline.py +0 -0
  246. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_policy_lint.py +0 -0
  247. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_scan.py +0 -0
  248. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_selftest.py +0 -0
  249. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_shim_sh.py +0 -0
  250. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_strands.py +0 -0
  251. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_tiers.py +0 -0
  252. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_transcript.py +0 -0
  253. {interlockd-0.1.0 → interlockd-0.1.2}/tests/test_unix_socket.py +0 -0
@@ -0,0 +1,20 @@
1
+ name: Support for an agent or framework
2
+ description: Ask for (or offer to build) an adapter for a coding agent, agent framework, or MCP client
3
+ labels: [adapter]
4
+ body:
5
+ - type: input
6
+ id: agent
7
+ attributes: { label: Agent or framework, placeholder: "name, version, link" }
8
+ validations: { required: true }
9
+ - type: textarea
10
+ id: hooks
11
+ attributes:
12
+ label: Its extension point
13
+ description: Hooks, plugins, callbacks, or MCP only? Links to the docs help. Adapters are verified against payloads captured from the real tool, see docs/ADAPTERS.md.
14
+ - type: checkboxes
15
+ id: help
16
+ attributes:
17
+ label: Can you help?
18
+ options:
19
+ - label: I can capture real hook payloads from this tool
20
+ - label: I'd like to build the adapter
@@ -0,0 +1,29 @@
1
+ name: Bug
2
+ description: Something doesn't work as documented
3
+ labels: [bug]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Found a way past a rule? Please use **private vulnerability reporting** instead (see the link on the previous page).
9
+ Audit records contain tool arguments: remove anything secret before pasting.
10
+ - type: textarea
11
+ id: what
12
+ attributes: { label: "What happened, and what did you expect?" }
13
+ validations: { required: true }
14
+ - type: textarea
15
+ id: repro
16
+ attributes: { label: "Steps to reproduce" }
17
+ validations: { required: true }
18
+ - type: textarea
19
+ id: doctor
20
+ attributes:
21
+ label: Output of `interlock --version` and `interlock doctor`
22
+ render: text
23
+ validations: { required: true }
24
+ - type: input
25
+ id: agent
26
+ attributes: { label: Agent and version, placeholder: "Claude Code 2.x, Codex 0.x, Cursor CLI ..." }
27
+ - type: input
28
+ id: os
29
+ attributes: { label: OS, placeholder: "macOS 15 arm64, Ubuntu 24.04 ..." }
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Report a vulnerability or a bypass (private)
4
+ url: https://github.com/interlockd/interlock/security/advisories/new
5
+ about: A way past a default rule, an injected result that reaches the agent, or tampering doctor misses. Please don't open a public issue.
@@ -0,0 +1,34 @@
1
+ name: Wrong decision (false positive or false negative)
2
+ description: Interlock blocked or asked about something safe, or allowed something it should have stopped
3
+ labels: [decision]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ If an attack got through a **default** rule, please report it privately instead (Security → Report a vulnerability).
9
+ `interlock explain` shows the rule ids and reason for the latest stop. Remove secrets before pasting.
10
+ - type: dropdown
11
+ id: kind
12
+ attributes:
13
+ label: Kind
14
+ options: ["False positive: a safe action was blocked or asked", "False negative: a harmful action was allowed"]
15
+ validations: { required: true }
16
+ - type: textarea
17
+ id: action
18
+ attributes: { label: "The tool call (command or tool and arguments)", render: text }
19
+ validations: { required: true }
20
+ - type: textarea
21
+ id: explain
22
+ attributes: { label: "Output of `interlock explain` (for a stop)", render: text }
23
+ - type: input
24
+ id: expected
25
+ attributes: { label: "What should the decision be? (allow / ask / block)" }
26
+ validations: { required: true }
27
+ - type: textarea
28
+ id: why
29
+ attributes: { label: "Why? Context that makes it safe, or harmful" }
30
+ - type: dropdown
31
+ id: policy
32
+ attributes:
33
+ label: Policy
34
+ options: ["Default policy", "Custom policy (paste the relevant part below)"]
@@ -0,0 +1,9 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule: { interval: weekly }
6
+ - package-ecosystem: pip
7
+ directory: /
8
+ schedule: { interval: weekly }
9
+ open-pull-requests-limit: 5
@@ -0,0 +1,10 @@
1
+ ## What and why
2
+
3
+ ## How it was verified
4
+
5
+ - [ ] Tests (`pytest -q`, `interlock test`)
6
+ - [ ] Live against the real agent (which one, which version)
7
+
8
+ ## For a rule change
9
+
10
+ - [ ] Positive **and** hard-negative cases in `interlockd/data/packs/<pack>.jsonl`
@@ -15,8 +15,8 @@ jobs:
15
15
  matrix:
16
16
  os: [ubuntu-latest, macos-latest]
17
17
  steps:
18
- - uses: actions/checkout@v4
19
- - uses: astral-sh/setup-uv@v5
18
+ - uses: actions/checkout@v7
19
+ - uses: astral-sh/setup-uv@v7
20
20
  - name: Install
21
21
  run: |
22
22
  uv venv --python 3.12
@@ -29,10 +29,22 @@ jobs:
29
29
  run: .venv/bin/interlock test
30
30
  - name: Evaluation corpus gate
31
31
  run: .venv/bin/interlock test --corpus corpus/archetypes
32
- - name: Framework integrations (Strands, LangChain) against a real daemon
32
+ - name: Linux sandbox (bubblewrap)
33
+ if: runner.os == 'Linux'
33
34
  run: |
34
- uv pip install --python .venv/bin/python strands-agents langchain-core openai-agents
35
- .venv/bin/pytest -q tests/test_strands.py tests/test_langchain.py tests/test_openai_agents.py
35
+ sudo apt-get install -y -qq bubblewrap
36
+ # Ubuntu 24.04 restricts unprivileged user namespaces through AppArmor; bubblewrap needs them
37
+ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
38
+ .venv/bin/pytest -q tests/test_sandbox.py -rs
39
+ - name: Framework integrations against a real daemon
40
+ run: |
41
+ uv pip install --python .venv/bin/python strands-agents langchain-core openai-agents google-adk claude-agent-sdk semantic-kernel
42
+ .venv/bin/pytest -q tests/test_strands.py tests/test_langchain.py tests/test_openai_agents.py tests/test_google_adk.py tests/test_claude_agent_sdk.py tests/test_semantic_kernel.py
43
+ - name: CrewAI integration (own environment)
44
+ run: |
45
+ uv venv /tmp/crewai --python 3.12
46
+ uv pip install --python /tmp/crewai/bin/python -e ".[dev]" crewai
47
+ /tmp/crewai/bin/pytest -q tests/test_crewai.py
36
48
  - name: Wheel smoke test
37
49
  run: |
38
50
  uv build --wheel -o dist
@@ -43,7 +55,7 @@ jobs:
43
55
  action:
44
56
  runs-on: ubuntu-latest
45
57
  steps:
46
- - uses: actions/checkout@v4
58
+ - uses: actions/checkout@v7
47
59
  - name: Plant a risky MCP config to prove the scan sees it
48
60
  run: |
49
61
  mkdir -p .vscode
@@ -0,0 +1,95 @@
1
+ name: release
2
+
3
+ # Publish to PyPI when a version tag is pushed (git tag v0.1.0 && git push origin v0.1.0).
4
+ # Uses PyPI trusted publishing: no API token is stored. One-time setup on pypi.org:
5
+ # project "interlockd" → Publishing → add GitHub publisher: owner interlockd, repo interlock,
6
+ # workflow release.yml, environment pypi.
7
+
8
+ on:
9
+ push:
10
+ tags: ["v*"]
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ contents: read
20
+ id-token: write # signs the build provenance (Sigstore)
21
+ attestations: write
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+ - uses: astral-sh/setup-uv@v7
25
+ - name: Tag matches package version
26
+ run: |
27
+ tag="${GITHUB_REF_NAME#v}"
28
+ pkg=$(python3 -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
29
+ mod=$(python3 -c "import re;print(re.search(r'__version__ = \"(.+)\"', open('interlockd/__init__.py').read()).group(1))")
30
+ echo "tag=$tag pyproject=$pkg module=$mod"
31
+ [ "$tag" = "$pkg" ] && [ "$tag" = "$mod" ]
32
+ - name: Test
33
+ run: |
34
+ uv venv --python 3.12 && uv pip install -e ".[dev]" --python .venv/bin/python
35
+ .venv/bin/ruff check . && .venv/bin/pytest -q && .venv/bin/interlock test
36
+ - name: Build
37
+ run: uv build
38
+ - name: Smoke-test the wheel
39
+ run: |
40
+ uv venv /tmp/w --python 3.12 && uv pip install --python /tmp/w/bin/python dist/*.whl
41
+ cd /tmp && /tmp/w/bin/interlock --help >/dev/null && /tmp/w/bin/interlock test
42
+ - name: Build provenance (verify with `gh attestation verify <file> --repo interlockd/interlock`)
43
+ uses: actions/attest-build-provenance@v2
44
+ with: { subject-path: "dist/*" }
45
+ - uses: actions/upload-artifact@v7
46
+ with: { name: dist, path: dist/ }
47
+
48
+ publish:
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ environment: pypi
52
+ permissions:
53
+ id-token: write # trusted publishing
54
+ contents: write # GitHub release
55
+ steps:
56
+ - uses: actions/download-artifact@v8
57
+ with: { name: dist, path: dist/ }
58
+ - uses: pypa/gh-action-pypi-publish@release/v1
59
+ - name: GitHub release
60
+ env: { GH_TOKEN: "${{ github.token }}" }
61
+ run: |
62
+ # a release drafted by hand on GitHub (e.g. to publish the Action to the Marketplace) keeps its notes
63
+ if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
64
+ gh release upload "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --clobber
65
+ else
66
+ gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "$GITHUB_REF_NAME" --generate-notes
67
+ fi
68
+
69
+ homebrew:
70
+ # Regenerates Formula/interlock.rb from the release on PyPI. With a HOMEBREW_TAP_TOKEN secret (a fine-grained token
71
+ # with contents: write on interlockd/homebrew-tap) it commits the formula there; without one it is kept as an artifact.
72
+ needs: publish
73
+ runs-on: ubuntu-latest
74
+ env: { TAP_TOKEN: "${{ secrets.HOMEBREW_TAP_TOKEN }}" }
75
+ steps:
76
+ - uses: actions/checkout@v7
77
+ - name: Generate the formula (waits for PyPI to serve the new version)
78
+ run: |
79
+ v="${GITHUB_REF_NAME#v}"
80
+ for i in 1 2 3 4 5 6 7 8 9 10; do
81
+ python3 packaging/homebrew/make_formula.py "$v" > interlock.rb && break
82
+ sleep 30
83
+ done
84
+ ruby -c interlock.rb
85
+ - uses: actions/upload-artifact@v7
86
+ with: { name: homebrew-formula, path: interlock.rb }
87
+ - name: Commit to interlockd/homebrew-tap
88
+ if: env.TAP_TOKEN != ''
89
+ run: |
90
+ git clone "https://x-access-token:${TAP_TOKEN}@github.com/interlockd/homebrew-tap.git" tap
91
+ mkdir -p tap/Formula && cp interlock.rb tap/Formula/interlock.rb
92
+ cd tap
93
+ git config user.name "interlock release"; git config user.email "release@users.noreply.github.com"
94
+ git add Formula/interlock.rb
95
+ git diff --cached --quiet || { git commit -m "interlock ${GITHUB_REF_NAME#v}" && git push origin HEAD:main; } # works on an empty tap too
@@ -0,0 +1,58 @@
1
+ # Changelog
2
+
3
+ ## 0.1.2 (2026-10-24)
4
+
5
+ - Managed policy: `<interlock home>/managed-policy.yaml` is applied last and can only tighten decisions (it can switch enforce on, never loosen anything). It is reloaded without a restart; a file that doesn't parse is ignored and reported by `interlock policy lint`. For device management and centrally managed machines.
6
+ - Plugins: installed packages that register under the `interlockd.plugins` entry-point group can add commands and run background work beside `interlock serve`. Plugins never sit in the decision path; a plugin that fails is skipped with a warning. `interlock plugins` lists them; `INTERLOCK_PLUGINS=0` turns them off.
7
+ - Platforms stated: macOS and Linux; on Windows, `interlock` explains how to use WSL2 instead of crashing on import. Homebrew: `brew install interlockd/tap/interlock`.
8
+ - `interlock judge install | status | remove`: run the optional judge model (Strands Decider 2B) locally in one step. The daemon starts it in the background instead of delaying startup.
9
+ - Contributing: pull requests are imported into the development repository with author credit (`CONTRIBUTING.md`); a code of conduct; `demo/demo.sh` (and a VHS tape) for a self-contained recording.
10
+ - `@interlockd/sdk` for JavaScript and TypeScript agents (`sdk/js`, no dependencies): `guardTools` for the Vercel AI SDK, Mastra and any `{ execute }` tool map, and `guardOptions` for the Claude Agent SDK for TypeScript. Both verified with the real packages.
11
+ - Injection detector, measured on public benchmarks (`docs/BENCHMARKS.md`, scripts in `benchmarks/`): catches AgentDojo's leaderboard template (0% -> 100%), InjecAgent's enhanced attacks (37% -> 97% on the harm set), typo'd "ignore your previous ..." and `###(system_message)` markers. False positives on real documentation fell from 2.2% to 0.25% ("bypass the X policy" and "silently skipped" no longer count).
12
+ - `shred <file>` is an irreversible delete (asked or recorded like `rm -rf`), no longer blocked as a disk wipe; `shred /dev/...` still is.
13
+ - Releases carry signed build provenance (`gh attestation verify`), and the release workflow regenerates the Homebrew formula and commits it to `interlockd/homebrew-tap` when a tap token is configured.
14
+ - `interlock capture`: record the real hook payloads an agent sends (with Interlock's replies), check that blocks held, and export redacted fixtures. `--scratch` sets up a verification project. Verified live with Claude Code.
15
+ - Cedar mandate: shell egress (`curl`, `wget`, ...) now binds to `net.egress` with its hosts, like fetch tools (it was reported unjudged). With `mandate.mode: enforce`, a mandate without a `net.egress` permit now refuses shell egress; `docs/mandate-example.cedar` shows a host allowlist. Enforce mode verified live through the hook shim.
16
+ - MCP OAuth checks in the HTTP gateway: a JWT issued for another service is never passed to an MCP server (audience binding), scopes in `mcp.oauth.deny_scopes` are refused, administrative scopes are flagged, and each server's authorization servers are pinned on first use so a change is refused until `interlock mcp-pins --accept`.
17
+
18
+ ## 0.1.1 (2026-10-18)
19
+
20
+ - Google ADK and CrewAI integrations.
21
+ - Semantic Kernel integration (`guard_kernel`): a function-invocation filter, tested through a real Kernel.
22
+ - Claude Agent SDK integration (`guard_options`): in-process PreToolUse / PostToolUse hooks, verified live (a refused force-push, a withheld injected README).
23
+ - `interlock pause` / `resume` kill switch.
24
+ - Linux sandbox (bubblewrap) verified in CI.
25
+ - Repository policies: a committed `<repo>/.interlock/policy.yaml` applies to every call inside the repository, re-read on change. It can only tighten your policy unless you list the repository in `trust.trusted_workspaces`; `interlock policy lint --workspace <repo>` shows what was not applied.
26
+ - Hook watch: the daemon re-checks the hooks it installed every minute and records a removed hook or a modified shim (and the repair) in the audit chain; `interlock report` lists them. Shims left by an upgrade are refreshed instead of flagged.
27
+ - `interlock bench`: decision, probe, HTTP and hook-shim latency on your machine; see `docs/PERFORMANCE.md`.
28
+ - Faster audit writes: the hash chain's head is cached between appends instead of re-read; decision p50 4.2 ms -> 1.2 ms.
29
+ - `interlock doctor` reports when a newer release is on PyPI (`INTERLOCK_NO_UPDATE_CHECK=1` turns it off).
30
+ - Docs: the hooks apply `fail_mode.default` while the daemon is down (the per-category entries had been described as applying too).
31
+
32
+ ## 0.1.0 (2026-10-17)
33
+
34
+ First public release.
35
+
36
+ - `interlock init` / `uninstall`: one-command setup for Claude Code, Codex, Kiro, Cursor, and GitHub Copilot CLI (verified live), plus Gemini CLI, Windsurf, Antigravity CLI, and OpenCode (built from their documented hooks and plugin API); background service (launchd, systemd user, or detached process); shadow mode by default.
37
+ - Pre-action gate: 48 deterministic rules in 6 packs (shell, git, egress, persistence, secrets, packages) with a shell decomposer.
38
+ - Post-action injection probe that runs locally (no cloud), withholding injected results in Claude Code.
39
+ - Custom agents: `interlockd.integrations.strands.InterlockHooks`, `interlockd.integrations.langchain.guard_tools`, and `interlockd.integrations.openai_agents.guard_agent_tools`.
40
+ - MCP gateway: `interlock gateway wrap | unwrap | status` routes stdio MCP servers through `interlock mcp-proxy` and remote (Streamable HTTP) servers through the daemon, which pins tool definitions (rug-pull detection), hides poisoned tools, gates tool calls with the same rules as the agent's own shell, and withholds injected results.
41
+ - `interlock mcp`: Interlock as an MCP server with read-only advisory tools (`check_action`, `explain_decision`, `recent_decisions`, `scan_mcp_servers`, `interlock_status`, `suggest_rule_override`); `interlock init --mcp` registers it.
42
+ - Codex: injected tool results are withheld (`decision: block`, measured live).
43
+ - Memory-poisoning protection: writes to agent instruction files (CLAUDE.md, AGENTS.md, ...) and MCP memory tools are recorded, and asked when the content reads as injected or the session took in untrusted content.
44
+ - `interlock pause` / `resume`: a kill switch that refuses every agent action (all or named agents, optionally timed), in any mode.
45
+ - `interlock egress-proxy`: decides every outbound host (metadata endpoints refused; `--strict` allowlist); `interlock sandbox --egress` makes it the agent's only way out.
46
+ - `interlock sandbox -- <agent>`: OS-level sandbox (macOS seatbelt; Linux bubblewrap) refusing persistence and guard-rail writes, with optional secret-read and network limits.
47
+ - Packaging: published on PyPI as `interlockd` (the command is still `interlock`) via a trusted-publishing release workflow, and a Homebrew formula generator for `interlockd/homebrew-tap`.
48
+ - `interlock replay`: re-decide recorded calls with your current policy and see what would change.
49
+ - `interlock policy lint`: unknown rule ids (a typo in an override silently does nothing), weakened critical rules, catch-all allows.
50
+ - `interlock selftest`: attack shapes through the live daemon in each agent's hook format; run by `init`.
51
+ - `interlock explain`: why a call was stopped, in plain language, with the override snippet.
52
+ - OpenTelemetry: one span per decision to any OTLP/HTTP collector (`otlp_endpoint` in config), and `interlock otel export` to backfill.
53
+ - `interlock aibom`: CycloneDX 1.6 AI bill of materials for the agent stack (agents, models, MCP servers with package URLs and pinning, remote endpoints).
54
+ - Hardening: the daemon also listens on a 0600 Unix socket that the hooks prefer; `interlock token --rotate`.
55
+ - Tamper-evident audit log: hash-chained records, `interlock audit verify` and `audit head`.
56
+ - `interlock scan`: static audit of MCP servers and agent settings across Claude Code, Claude Desktop, Codex, Cursor, VS Code, Kiro, Gemini CLI, and Windsurf.
57
+ - `interlock report`, `doctor`, `status`, `log`, `enforce` / `shadow`, `test`.
58
+ - Optional: Cedar mandate, open-weights judge, Amazon Bedrock Guardrails as an extra detector, DynamoDB audit store.
@@ -0,0 +1,10 @@
1
+ # Code of conduct
2
+
3
+ Interlock follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
4
+
5
+ In short: be respectful and constructive, assume good faith, and keep discussion about the work. Harassment, personal
6
+ attacks, and discriminatory language are not acceptable in issues, pull requests, discussions, or any other project
7
+ space.
8
+
9
+ To report a problem, email **conduct@interlockd.dev**. Reports are handled confidentially. The maintainers may remove content and restrict participation, as described in the Covenant's
10
+ enforcement guidelines.
@@ -17,11 +17,17 @@ CI runs all four on every pull request.
17
17
  ## What to contribute
18
18
 
19
19
  - **A rule.** Register it in `interlockd/det/packs.py`, implement the check, and add positive *and* negative cases to `interlockd/data/packs/<pack>.jsonl`. A rule without hard negatives will not be merged: false positives are what make people uninstall a security tool.
20
- - **An adapter for a new agent.** Read [docs/ADAPTERS.md](docs/ADAPTERS.md) first. A normalizer (`interlockd/normalize/`), a shim (`interlockd/shims/`), detection in `interlockd/onboard.py`, and **payloads captured from the real tool** as fixtures in `tests/fixtures/<agent>/`, plus a `adapters/<agent>/PAYLOADS.md` describing what you measured. Documented-but-unmeasured contracts have been wrong before.
20
+ - **An adapter for a new agent.** Read [docs/ADAPTERS.md](docs/ADAPTERS.md) first. A normalizer (`interlockd/normalize/`), a shim (`interlockd/shims/`), detection in `interlockd/onboard.py`, and **payloads captured from the real tool** (`interlock capture`, see docs/ADAPTERS.md §4) as fixtures in `tests/fixtures/<agent>/`, plus a `adapters/<agent>/PAYLOADS.md` describing what you measured. Documented-but-unmeasured contracts have been wrong before.
21
21
  - **Corpus cases.** Real attack shapes and real benign commands that a rule gets wrong.
22
22
 
23
23
  ## Pull requests
24
24
 
25
+ Open pull requests against `main` here as usual. This repository is published from the maintainers' development
26
+ repository, so a merged change reaches `main` through the next published commit, with you credited as author (and
27
+ `Co-authored-by` on the published commit); your PR is closed with a link to it rather than merged with the button.
28
+
25
29
  - Keep changes focused; include tests.
26
30
  - Describe what you verified and how (live against the tool, or tests only).
27
31
  - By contributing you agree your contribution is licensed under Apache-2.0.
32
+
33
+ By taking part you agree to the [code of conduct](CODE_OF_CONDUCT.md).
@@ -0,0 +1,7 @@
1
+ Interlock
2
+ Copyright 2026 the Interlock authors
3
+
4
+ This product is licensed under the Apache License, Version 2.0 (see LICENSE).
5
+
6
+ Benchmark scripts in benchmarks/ download third-party datasets at run time; those datasets are not part of this
7
+ repository and keep their own licenses: InjecAgent (MIT), AgentDojo (MIT), NL2Bash (GPL-3.0).
@@ -1,3 +1,47 @@
1
+ Metadata-Version: 2.5
2
+ Name: interlockd
3
+ Version: 0.1.2
4
+ Summary: Interlock daemon: a pre/post-action interlock for coding agents
5
+ License: Apache-2.0
6
+ License-File: LICENSE
7
+ License-File: NOTICE
8
+ Classifier: Environment :: Console
9
+ Classifier: Operating System :: MacOS
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Security
13
+ Requires-Python: >=3.12
14
+ Requires-Dist: pydantic<3,>=2.9
15
+ Requires-Dist: pyyaml<7,>=6.0
16
+ Provides-Extra: aws
17
+ Requires-Dist: boto3<2,>=1.35; extra == 'aws'
18
+ Provides-Extra: claude-agent-sdk
19
+ Requires-Dist: claude-agent-sdk>=0.2; extra == 'claude-agent-sdk'
20
+ Provides-Extra: crewai
21
+ Requires-Dist: crewai>=1.10; extra == 'crewai'
22
+ Provides-Extra: dev
23
+ Requires-Dist: boto3<2,>=1.35; extra == 'dev'
24
+ Requires-Dist: cedarpy<5,>=4.7; extra == 'dev'
25
+ Requires-Dist: moto[dynamodb]<6,>=5.0; extra == 'dev'
26
+ Requires-Dist: pytest<10,>=8.3; extra == 'dev'
27
+ Requires-Dist: ruff<1,>=0.6; extra == 'dev'
28
+ Provides-Extra: google-adk
29
+ Requires-Dist: google-adk>=2.0; extra == 'google-adk'
30
+ Provides-Extra: langchain
31
+ Requires-Dist: langchain-core>=1.0; extra == 'langchain'
32
+ Provides-Extra: mandate
33
+ Requires-Dist: cedarpy<5,>=4.7; extra == 'mandate'
34
+ Provides-Extra: openai-agents
35
+ Requires-Dist: openai-agents>=0.20; extra == 'openai-agents'
36
+ Provides-Extra: promptguard
37
+ Requires-Dist: torch>=2.3; extra == 'promptguard'
38
+ Requires-Dist: transformers<6,>=4.44; extra == 'promptguard'
39
+ Provides-Extra: semantic-kernel
40
+ Requires-Dist: semantic-kernel>=1.30; extra == 'semantic-kernel'
41
+ Provides-Extra: strands
42
+ Requires-Dist: strands-agents>=1.40; extra == 'strands'
43
+ Description-Content-Type: text/markdown
44
+
1
45
  # Interlock
2
46
 
3
47
  **The safety layer between your AI agent and your codebase.**
@@ -8,10 +52,15 @@ It runs locally. No account, no cloud, no model download required.
8
52
 
9
53
 
10
54
  ```sh
11
- pipx install interlockd # or: uv tool install interlockd
55
+ pipx install interlockd # or: uv tool install interlockd, or: brew install interlockd/tap/interlock
12
56
  interlock init # finds your agents, installs hooks, starts the daemon (shadow mode)
13
57
  ```
14
58
 
59
+ Runs on macOS and Linux (Python 3.12+). On Windows, use it inside WSL2 and run your agents from the WSL terminal;
60
+ native Windows support is planned.
61
+
62
+ New here? [docs/GETTING-STARTED.md](docs/GETTING-STARTED.md) walks through shadow mode, enforcing, MCP servers, your own agents and CI in about ten minutes.
63
+
15
64
  ```
16
65
  What the gate decides (enforce mode, nothing executed):
17
66
  block git push --force origin main force-push to a protected branch
@@ -31,14 +80,15 @@ What the gate decides (enforce mode, nothing executed):
31
80
  | Cursor (CLI) | ✅ verified live: blocks before execution |
32
81
  | GitHub Copilot CLI | ✅ verified live: blocks before execution, withholds injected tool results |
33
82
  | Gemini CLI, Windsurf, Antigravity CLI, OpenCode | built from their documented hooks/plugins; live verification in progress |
34
- | Your own agents (Strands, LangChain / LangGraph, OpenAI Agents SDK) | ✅ in-process: `Agent(hooks=[InterlockHooks()])`, `guard_tools(tools)`, `guard_agent_tools(agent)` |
35
- | Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio MCP server, hooks or not |
36
- | Windsurf, Cline, GitHub Copilot, and more | hook adapters planned |
83
+ | Your own agents (Claude Agent SDK, Strands, LangChain / LangGraph, OpenAI Agents SDK, Google ADK, CrewAI, Semantic Kernel) | ✅ in-process: `guard_options(options)`, `InterlockHooks()`, `guard_tools(tools)`, `guard_agent_tools(agent)`, `guard_agent(agent)`, `crewai.install()`, `guard_kernel(kernel)` |
84
+ | JavaScript / TypeScript agents (Vercel AI SDK, Claude Agent SDK for TS, Mastra) | ✅ `@interlockd/sdk`: `guardTools(tools)`, `guardOptions(options)` ([sdk/js](sdk/js)) |
85
+ | Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio and remote (HTTP) MCP server, hooks or not |
86
+ | Cline, Copilot in VS Code, and more | hook adapters planned; meanwhile the MCP gateway and `interlock sandbox` cover them |
37
87
 
38
88
  ## How it works
39
89
 
40
90
  1. **Hooks.** `interlock init` adds a small hook to each agent you have installed. The hook forwards each proposed tool call (and each tool result) to a local daemon.
41
- 2. **Deterministic rules first.** 48 rules in 6 packs (shell, git, egress, persistence, secrets, packages) decide in about 2 ms. The shell decomposer sees through chains, pipes, subshells, heredocs, interpreter one-liners and base64.
91
+ 2. **Deterministic rules first.** 48 rules in 6 packs (shell, git, egress, persistence, secrets, packages) decide in about a millisecond ([measured](docs/PERFORMANCE.md)). The shell decomposer sees through chains, pipes, subshells, heredocs, interpreter one-liners and base64.
42
92
  3. **Injection probe on results.** A local detector flags tool output that tries to instruct the agent (including text hidden in HTML comments or invisible Unicode) and withholds it where the agent supports that.
43
93
  4. **Optional judge.** An open-weights decision model can answer "did the user actually ask for this?" for ambiguous actions. Off by default.
44
94
  5. **Everything recorded.** Every decision lands in a local append-only log with its reason.
@@ -68,7 +118,7 @@ Policy lives in `~/.interlock/policy.yaml` (user) and `<repo>/.interlock/policy.
68
118
 
69
119
  ## Honest limits
70
120
 
71
- The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
121
+ The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md). Latency on your own machine: `interlock bench` ([docs/PERFORMANCE.md](docs/PERFORMANCE.md)). Detection and false-positive rates on public datasets (InjecAgent, AgentDojo, NL2Bash), including what it misses: [docs/BENCHMARKS.md](docs/BENCHMARKS.md).
72
122
 
73
123
 
74
124
  - Interlock sees what the agent's hooks show it. A process the agent starts outside the hook path, or a second unguarded agent, is not gated. Network and sandbox enforcement are on the roadmap.
@@ -78,8 +128,8 @@ The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
78
128
  ## In CI
79
129
 
80
130
  ```yaml
81
- - uses: actions/checkout@v4
82
- - uses: interlockd/interlock@v0.1.0 # scan MCP/agent configs, test .interlock/policy.yaml
131
+ - uses: actions/checkout@v7
132
+ - uses: interlockd/interlock@v0.1.2 # scan MCP/agent configs, test .interlock/policy.yaml
83
133
  with: { fail-on: high }
84
134
  - uses: github/codeql-action/upload-sarif@v3
85
135
  if: always()
@@ -92,4 +142,9 @@ Adapters for new agents, rules, and corpus cases are all welcome. See [CONTRIBUT
92
142
 
93
143
  ## License
94
144
 
95
- Apache-2.0.
145
+ Everything in this repository is Apache-2.0, and stays that way: we will not relicense it. That covers enforcement,
146
+ every rule and adapter, the injection detector, the MCP gateway, policy testing, and the local audit log, and it runs
147
+ fully offline with no license check.
148
+
149
+ A hosted service for teams (fleet-wide policy, dashboards, retention) may come later as a separate product. It will
150
+ not make anything here less safe or move it behind a paywall. See `NOTICE`.
@@ -1,33 +1,3 @@
1
- Metadata-Version: 2.5
2
- Name: interlockd
3
- Version: 0.1.0
4
- Summary: Interlock daemon: a pre/post-action interlock for coding agents
5
- License: Apache-2.0
6
- License-File: LICENSE
7
- Requires-Python: >=3.12
8
- Requires-Dist: pydantic<3,>=2.9
9
- Requires-Dist: pyyaml<7,>=6.0
10
- Provides-Extra: aws
11
- Requires-Dist: boto3<2,>=1.35; extra == 'aws'
12
- Provides-Extra: dev
13
- Requires-Dist: boto3<2,>=1.35; extra == 'dev'
14
- Requires-Dist: cedarpy<5,>=4.7; extra == 'dev'
15
- Requires-Dist: moto[dynamodb]<6,>=5.0; extra == 'dev'
16
- Requires-Dist: pytest<9,>=8.3; extra == 'dev'
17
- Requires-Dist: ruff<1,>=0.6; extra == 'dev'
18
- Provides-Extra: langchain
19
- Requires-Dist: langchain-core>=1.0; extra == 'langchain'
20
- Provides-Extra: mandate
21
- Requires-Dist: cedarpy<5,>=4.7; extra == 'mandate'
22
- Provides-Extra: openai-agents
23
- Requires-Dist: openai-agents>=0.20; extra == 'openai-agents'
24
- Provides-Extra: promptguard
25
- Requires-Dist: torch>=2.3; extra == 'promptguard'
26
- Requires-Dist: transformers<5,>=4.44; extra == 'promptguard'
27
- Provides-Extra: strands
28
- Requires-Dist: strands-agents>=1.40; extra == 'strands'
29
- Description-Content-Type: text/markdown
30
-
31
1
  # Interlock
32
2
 
33
3
  **The safety layer between your AI agent and your codebase.**
@@ -38,10 +8,15 @@ It runs locally. No account, no cloud, no model download required.
38
8
 
39
9
 
40
10
  ```sh
41
- pipx install interlockd # or: uv tool install interlockd
11
+ pipx install interlockd # or: uv tool install interlockd, or: brew install interlockd/tap/interlock
42
12
  interlock init # finds your agents, installs hooks, starts the daemon (shadow mode)
43
13
  ```
44
14
 
15
+ Runs on macOS and Linux (Python 3.12+). On Windows, use it inside WSL2 and run your agents from the WSL terminal;
16
+ native Windows support is planned.
17
+
18
+ New here? [docs/GETTING-STARTED.md](docs/GETTING-STARTED.md) walks through shadow mode, enforcing, MCP servers, your own agents and CI in about ten minutes.
19
+
45
20
  ```
46
21
  What the gate decides (enforce mode, nothing executed):
47
22
  block git push --force origin main force-push to a protected branch
@@ -61,14 +36,15 @@ What the gate decides (enforce mode, nothing executed):
61
36
  | Cursor (CLI) | ✅ verified live: blocks before execution |
62
37
  | GitHub Copilot CLI | ✅ verified live: blocks before execution, withholds injected tool results |
63
38
  | Gemini CLI, Windsurf, Antigravity CLI, OpenCode | built from their documented hooks/plugins; live verification in progress |
64
- | Your own agents (Strands, LangChain / LangGraph, OpenAI Agents SDK) | ✅ in-process: `Agent(hooks=[InterlockHooks()])`, `guard_tools(tools)`, `guard_agent_tools(agent)` |
65
- | Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio MCP server, hooks or not |
66
- | Windsurf, Cline, GitHub Copilot, and more | hook adapters planned |
39
+ | Your own agents (Claude Agent SDK, Strands, LangChain / LangGraph, OpenAI Agents SDK, Google ADK, CrewAI, Semantic Kernel) | ✅ in-process: `guard_options(options)`, `InterlockHooks()`, `guard_tools(tools)`, `guard_agent_tools(agent)`, `guard_agent(agent)`, `crewai.install()`, `guard_kernel(kernel)` |
40
+ | JavaScript / TypeScript agents (Vercel AI SDK, Claude Agent SDK for TS, Mastra) | ✅ `@interlockd/sdk`: `guardTools(tools)`, `guardOptions(options)` ([sdk/js](sdk/js)) |
41
+ | Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio and remote (HTTP) MCP server, hooks or not |
42
+ | Cline, Copilot in VS Code, and more | hook adapters planned; meanwhile the MCP gateway and `interlock sandbox` cover them |
67
43
 
68
44
  ## How it works
69
45
 
70
46
  1. **Hooks.** `interlock init` adds a small hook to each agent you have installed. The hook forwards each proposed tool call (and each tool result) to a local daemon.
71
- 2. **Deterministic rules first.** 48 rules in 6 packs (shell, git, egress, persistence, secrets, packages) decide in about 2 ms. The shell decomposer sees through chains, pipes, subshells, heredocs, interpreter one-liners and base64.
47
+ 2. **Deterministic rules first.** 48 rules in 6 packs (shell, git, egress, persistence, secrets, packages) decide in about a millisecond ([measured](docs/PERFORMANCE.md)). The shell decomposer sees through chains, pipes, subshells, heredocs, interpreter one-liners and base64.
72
48
  3. **Injection probe on results.** A local detector flags tool output that tries to instruct the agent (including text hidden in HTML comments or invisible Unicode) and withholds it where the agent supports that.
73
49
  4. **Optional judge.** An open-weights decision model can answer "did the user actually ask for this?" for ambiguous actions. Off by default.
74
50
  5. **Everything recorded.** Every decision lands in a local append-only log with its reason.
@@ -98,7 +74,7 @@ Policy lives in `~/.interlock/policy.yaml` (user) and `<repo>/.interlock/policy.
98
74
 
99
75
  ## Honest limits
100
76
 
101
- The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
77
+ The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md). Latency on your own machine: `interlock bench` ([docs/PERFORMANCE.md](docs/PERFORMANCE.md)). Detection and false-positive rates on public datasets (InjecAgent, AgentDojo, NL2Bash), including what it misses: [docs/BENCHMARKS.md](docs/BENCHMARKS.md).
102
78
 
103
79
 
104
80
  - Interlock sees what the agent's hooks show it. A process the agent starts outside the hook path, or a second unguarded agent, is not gated. Network and sandbox enforcement are on the roadmap.
@@ -108,8 +84,8 @@ The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
108
84
  ## In CI
109
85
 
110
86
  ```yaml
111
- - uses: actions/checkout@v4
112
- - uses: interlockd/interlock@v0.1.0 # scan MCP/agent configs, test .interlock/policy.yaml
87
+ - uses: actions/checkout@v7
88
+ - uses: interlockd/interlock@v0.1.2 # scan MCP/agent configs, test .interlock/policy.yaml
113
89
  with: { fail-on: high }
114
90
  - uses: github/codeql-action/upload-sarif@v3
115
91
  if: always()
@@ -122,4 +98,9 @@ Adapters for new agents, rules, and corpus cases are all welcome. See [CONTRIBUT
122
98
 
123
99
  ## License
124
100
 
125
- Apache-2.0.
101
+ Everything in this repository is Apache-2.0, and stays that way: we will not relicense it. That covers enforcement,
102
+ every rule and adapter, the injection detector, the MCP gateway, policy testing, and the local audit log, and it runs
103
+ fully offline with no license check.
104
+
105
+ A hosted service for teams (fleet-wide policy, dashboards, retention) may come later as a separate product. It will
106
+ not make anything here less safe or move it behind a paywall. See `NOTICE`.
@@ -21,3 +21,14 @@ The threat model (assets, adversaries, where Interlock sees, and its known limit
21
21
 
22
22
  - Actions that never pass through an agent's hooks (documented limit).
23
23
  - Findings that require an already-compromised user account.
24
+
25
+ ## Verifying a release
26
+
27
+ Releases are built by GitHub Actions from the tagged commit and published to PyPI with trusted publishing (no stored
28
+ token). From 0.1.2, every wheel and sdist also carries a signed build provenance attestation (Sigstore):
29
+
30
+ ```sh
31
+ pip download interlockd==<version> --no-deps -d /tmp/il && gh attestation verify /tmp/il/* --repo interlockd/interlock
32
+ ```
33
+
34
+ PyPI shows the same provenance on each file's page ("Verified details").
@@ -1,5 +1,5 @@
1
- name: Interlock
2
- description: Audit MCP servers, agent configs, and committed agent instruction files (AGENTS.md, CLAUDE.md), and test your agent security policy, in CI.
1
+ name: Interlock Agent Security
2
+ description: Scan MCP servers, agent configs and AGENTS.md / CLAUDE.md for risks, and test your agent security policy in CI.
3
3
  author: interlockd
4
4
  branding: { icon: shield, color: blue }
5
5
 
@@ -31,7 +31,7 @@ outputs:
31
31
  runs:
32
32
  using: composite
33
33
  steps:
34
- - uses: actions/setup-python@v5
34
+ - uses: actions/setup-python@v7
35
35
  with: { python-version: "3.12" }
36
36
  - name: Install Interlock
37
37
  shell: bash
@@ -45,7 +45,8 @@ runs:
45
45
  run: |
46
46
  # an empty HOME keeps the scan to this repository's own configs
47
47
  export HOME="$(mktemp -d)"
48
- interlock scan --project "$GITHUB_WORKSPACE" --fail-on "${{ inputs.fail-on }}" --sarif "$GITHUB_WORKSPACE/${{ inputs.sarif }}"
48
+ args=(scan --project "$GITHUB_WORKSPACE" --fail-on "${{ inputs.fail-on }}" --sarif "$GITHUB_WORKSPACE/${{ inputs.sarif }}")
49
+ interlock "${args[@]}"
49
50
  - name: Test the policy
50
51
  if: inputs.test-policy == 'true'
51
52
  shell: bash