interlockd 0.1.0__tar.gz → 0.1.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 (227) hide show
  1. interlockd-0.1.1/.github/ISSUE_TEMPLATE/agent.yml +20 -0
  2. interlockd-0.1.1/.github/ISSUE_TEMPLATE/bug.yml +29 -0
  3. interlockd-0.1.1/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. interlockd-0.1.1/.github/ISSUE_TEMPLATE/decision.yml +34 -0
  5. interlockd-0.1.1/.github/dependabot.yml +9 -0
  6. interlockd-0.1.1/.github/pull_request_template.md +10 -0
  7. {interlockd-0.1.0 → interlockd-0.1.1}/.github/workflows/ci.yml +18 -6
  8. {interlockd-0.1.0 → interlockd-0.1.1}/.github/workflows/release.yml +4 -4
  9. {interlockd-0.1.0 → interlockd-0.1.1}/CHANGELOG.md +15 -0
  10. {interlockd-0.1.0 → interlockd-0.1.1}/PKG-INFO +19 -9
  11. {interlockd-0.1.0 → interlockd-0.1.1}/README.md +8 -6
  12. {interlockd-0.1.0 → interlockd-0.1.1}/action.yml +3 -3
  13. interlockd-0.1.1/docs/GETTING-STARTED.md +117 -0
  14. interlockd-0.1.1/docs/PERFORMANCE.md +33 -0
  15. {interlockd-0.1.0 → interlockd-0.1.1}/docs/THREAT-MODEL.md +3 -2
  16. {interlockd-0.1.0 → interlockd-0.1.1}/docs/USER-GUIDE.md +31 -6
  17. interlockd-0.1.1/examples/agents/README.md +17 -0
  18. interlockd-0.1.1/examples/agents/claude_agent_sdk_agent.py +18 -0
  19. interlockd-0.1.1/examples/agents/crewai_crew.py +21 -0
  20. interlockd-0.1.1/examples/agents/google_adk_agent.py +15 -0
  21. interlockd-0.1.1/examples/agents/langgraph_agent.py +19 -0
  22. interlockd-0.1.1/examples/agents/openai_agents.py +17 -0
  23. interlockd-0.1.1/examples/agents/semantic_kernel_agent.py +24 -0
  24. interlockd-0.1.1/examples/agents/strands_agent.py +17 -0
  25. interlockd-0.1.1/examples/github-workflow.yml +16 -0
  26. interlockd-0.1.1/examples/policies/ci.yaml +11 -0
  27. interlockd-0.1.1/examples/policies/mcp-locked-down.yaml +15 -0
  28. interlockd-0.1.1/examples/policies/strict-team.yaml +29 -0
  29. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/__init__.py +1 -1
  30. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/api.py +1 -0
  31. interlockd-0.1.1/interlockd/bench.py +113 -0
  32. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/cli.py +69 -5
  33. interlockd-0.1.1/interlockd/control.py +69 -0
  34. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/describe.py +1 -0
  35. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/insights.py +111 -24
  36. interlockd-0.1.1/interlockd/integrations/claude_agent_sdk.py +84 -0
  37. interlockd-0.1.1/interlockd/integrations/crewai.py +79 -0
  38. interlockd-0.1.1/interlockd/integrations/google_adk.py +62 -0
  39. interlockd-0.1.1/interlockd/integrations/semantic_kernel.py +61 -0
  40. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/claude_code.py +1 -1
  41. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/onboard.py +2 -2
  42. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/pipeline.py +29 -6
  43. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/policy/__init__.py +1 -0
  44. interlockd-0.1.1/interlockd/policy/workspace.py +262 -0
  45. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/claude-code/interlock-hook.sh +1 -1
  46. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/kiro/interlock-hook.mjs +1 -1
  47. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/kiro/interlock-hook.sh +1 -1
  48. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/spool.py +26 -7
  49. interlockd-0.1.1/interlockd/watch.py +78 -0
  50. interlockd-0.1.1/packaging/homebrew/Formula/interlock.rb +57 -0
  51. {interlockd-0.1.0 → interlockd-0.1.1}/pyproject.toml +7 -3
  52. {interlockd-0.1.0 → interlockd-0.1.1}/tests/conftest.py +7 -0
  53. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_api.py +4 -1
  54. interlockd-0.1.1/tests/test_bench.py +17 -0
  55. interlockd-0.1.1/tests/test_claude_agent_sdk.py +77 -0
  56. interlockd-0.1.1/tests/test_control.py +64 -0
  57. interlockd-0.1.1/tests/test_crewai.py +63 -0
  58. interlockd-0.1.1/tests/test_examples.py +27 -0
  59. interlockd-0.1.1/tests/test_google_adk.py +92 -0
  60. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_insights.py +9 -0
  61. interlockd-0.1.1/tests/test_repo_policy.py +126 -0
  62. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_sandbox.py +26 -0
  63. interlockd-0.1.1/tests/test_semantic_kernel.py +72 -0
  64. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_spool.py +11 -0
  65. interlockd-0.1.1/tests/test_version.py +29 -0
  66. interlockd-0.1.1/tests/test_watch.py +56 -0
  67. {interlockd-0.1.0 → interlockd-0.1.1}/.gitignore +0 -0
  68. {interlockd-0.1.0 → interlockd-0.1.1}/CONTRIBUTING.md +0 -0
  69. {interlockd-0.1.0 → interlockd-0.1.1}/LICENSE +0 -0
  70. {interlockd-0.1.0 → interlockd-0.1.1}/SECURITY.md +0 -0
  71. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/README.md +0 -0
  72. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/antigravity/PAYLOADS.md +0 -0
  73. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/claude-code/PAYLOADS.md +0 -0
  74. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/claude-code/README.md +0 -0
  75. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/codex/PAYLOADS.md +0 -0
  76. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/codex/README.md +0 -0
  77. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/copilot-cli/PAYLOADS.md +0 -0
  78. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/cursor/PAYLOADS.md +0 -0
  79. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/gemini-cli/PAYLOADS.md +0 -0
  80. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/kiro/PAYLOADS.md +0 -0
  81. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/opencode/PAYLOADS.md +0 -0
  82. {interlockd-0.1.0 → interlockd-0.1.1}/adapters/windsurf/PAYLOADS.md +0 -0
  83. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/README.md +0 -0
  84. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/_corpus.yaml +0 -0
  85. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/_corpus.yaml +0 -0
  86. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/exfil_and_secrets.jsonl +0 -0
  87. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/git_and_history.jsonl +0 -0
  88. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/injection_persistence_supplychain.jsonl +0 -0
  89. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_confirmation.jsonl +0 -0
  90. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_exfil.jsonl +0 -0
  91. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_injection.jsonl +0 -0
  92. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_intent.jsonl +0 -0
  93. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/slow_hijack.jsonl +0 -0
  94. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/README.md +0 -0
  95. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/samples/agentdojo.sample.json +0 -0
  96. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/samples/agentdrift.sample.json +0 -0
  97. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/samples/injecagent.sample.json +0 -0
  98. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/ogr-seed-v0/README.md +0 -0
  99. {interlockd-0.1.0 → interlockd-0.1.1}/corpus/ogr-seed-v0/seed-v0.jsonl +0 -0
  100. {interlockd-0.1.0 → interlockd-0.1.1}/docs/ADAPTERS.md +0 -0
  101. {interlockd-0.1.0 → interlockd-0.1.1}/docs/mandate-example.cedar +0 -0
  102. {interlockd-0.1.0 → interlockd-0.1.1}/docs/policy-example.yaml +0 -0
  103. {interlockd-0.1.0 → interlockd-0.1.1}/docs/reports/M3-judge-calibration.md +0 -0
  104. {interlockd-0.1.0 → interlockd-0.1.1}/docs/reports/ogr-seed-v0-results.jsonl +0 -0
  105. {interlockd-0.1.0 → interlockd-0.1.1}/docs/reports/ogr-seed-v0-results.md +0 -0
  106. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/__main__.py +0 -0
  107. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/aibom.py +0 -0
  108. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/auth.py +0 -0
  109. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/calibration.py +0 -0
  110. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/compose.py +0 -0
  111. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/config.py +0 -0
  112. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/corpus.py +0 -0
  113. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/_corpus.yaml +0 -0
  114. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/egress.jsonl +0 -0
  115. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/fileops.jsonl +0 -0
  116. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/git.jsonl +0 -0
  117. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/packages.jsonl +0 -0
  118. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/persistence.jsonl +0 -0
  119. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/secrets.jsonl +0 -0
  120. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/shell.jsonl +0 -0
  121. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/__init__.py +0 -0
  122. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/engine.py +0 -0
  123. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/flow.py +0 -0
  124. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/mandate.py +0 -0
  125. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/mcp.py +0 -0
  126. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/memory.py +0 -0
  127. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/packs.py +0 -0
  128. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/shellparse.py +0 -0
  129. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/egress.py +0 -0
  130. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/gateway.py +0 -0
  131. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/ids.py +0 -0
  132. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/__init__.py +0 -0
  133. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/client.py +0 -0
  134. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/langchain.py +0 -0
  135. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/openai_agents.py +0 -0
  136. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/strands.py +0 -0
  137. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/__init__.py +0 -0
  138. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/guardrails.py +0 -0
  139. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/local_probe.py +0 -0
  140. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/projector.py +0 -0
  141. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/strands_decider.py +0 -0
  142. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/ledger.py +0 -0
  143. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/mcp_http.py +0 -0
  144. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/mcp_proxy.py +0 -0
  145. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/mcp_server.py +0 -0
  146. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/models.py +0 -0
  147. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/__init__.py +0 -0
  148. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/cursor.py +0 -0
  149. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/gemini_cli.py +0 -0
  150. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/kiro.py +0 -0
  151. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/more.py +0 -0
  152. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/ogr.py +0 -0
  153. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/otel.py +0 -0
  154. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/paths.py +0 -0
  155. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/policy_lint.py +0 -0
  156. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/render.py +0 -0
  157. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/sandbox.py +0 -0
  158. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/scan.py +0 -0
  159. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/schema/verdict.schema.json +0 -0
  160. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/selftest.py +0 -0
  161. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/service.py +0 -0
  162. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/opencode/interlock.js +0 -0
  163. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/__init__.py +0 -0
  164. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/drainer.py +0 -0
  165. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/dynamodb.py +0 -0
  166. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/memory.py +0 -0
  167. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/testkit.py +0 -0
  168. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/tiers.py +0 -0
  169. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/transcript/__init__.py +0 -0
  170. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/transcript/claude_code.py +0 -0
  171. {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/transcript/kiro.py +0 -0
  172. {interlockd-0.1.0 → interlockd-0.1.1}/packaging/homebrew/README.md +0 -0
  173. {interlockd-0.1.0 → interlockd-0.1.1}/packaging/homebrew/make_formula.py +0 -0
  174. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fake_mcp_server.py +0 -0
  175. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/post-bash-inject.json +0 -0
  176. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/post-read-inject.json +0 -0
  177. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/pre-bash-block.json +0 -0
  178. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/pre-bash-inject.json +0 -0
  179. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/post-bash-benign.json +0 -0
  180. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/post-bash-inject.json +0 -0
  181. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/pre-bash-benign.json +0 -0
  182. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/pre-bash-block.json +0 -0
  183. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/copilot-cli/post-bash-inject.json +0 -0
  184. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/copilot-cli/pre-bash-benign.json +0 -0
  185. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/copilot-cli/pre-bash-block.json +0 -0
  186. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/post-read-benign.json +0 -0
  187. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/post-shell-benign.json +0 -0
  188. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/pre-read-benign.json +0 -0
  189. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/pre-shell-benign.json +0 -0
  190. {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/pre-shell-block.json +0 -0
  191. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_aibom.py +0 -0
  192. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_audit_chain.py +0 -0
  193. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_calibration.py +0 -0
  194. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_claude_code.py +0 -0
  195. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_corpus.py +0 -0
  196. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_cursor_gemini.py +0 -0
  197. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_dynamodb.py +0 -0
  198. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_egress.py +0 -0
  199. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_flow.py +0 -0
  200. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_gateway.py +0 -0
  201. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_homebrew_formula.py +0 -0
  202. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_interlock_test.py +0 -0
  203. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_judge.py +0 -0
  204. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_langchain.py +0 -0
  205. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_local_probe.py +0 -0
  206. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_http.py +0 -0
  207. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_policy.py +0 -0
  208. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_proxy.py +0 -0
  209. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_server.py +0 -0
  210. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_memory.py +0 -0
  211. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_more_adapters.py +0 -0
  212. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_normalize_kiro.py +0 -0
  213. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_ogr.py +0 -0
  214. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_onboard.py +0 -0
  215. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_openai_agents.py +0 -0
  216. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_opencode.py +0 -0
  217. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_otel.py +0 -0
  218. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_packs.py +0 -0
  219. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_pipeline.py +0 -0
  220. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_policy_lint.py +0 -0
  221. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_scan.py +0 -0
  222. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_selftest.py +0 -0
  223. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_shim_sh.py +0 -0
  224. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_strands.py +0 -0
  225. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_tiers.py +0 -0
  226. {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_transcript.py +0 -0
  227. {interlockd-0.1.0 → interlockd-0.1.1}/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
@@ -16,8 +16,8 @@ jobs:
16
16
  build:
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
- - uses: actions/checkout@v4
20
- - uses: astral-sh/setup-uv@v5
19
+ - uses: actions/checkout@v7
20
+ - uses: astral-sh/setup-uv@v7
21
21
  - name: Tag matches package version
22
22
  run: |
23
23
  tag="${GITHUB_REF_NAME#v}"
@@ -35,7 +35,7 @@ jobs:
35
35
  run: |
36
36
  uv venv /tmp/w --python 3.12 && uv pip install --python /tmp/w/bin/python dist/*.whl
37
37
  cd /tmp && /tmp/w/bin/interlock --help >/dev/null && /tmp/w/bin/interlock test
38
- - uses: actions/upload-artifact@v4
38
+ - uses: actions/upload-artifact@v7
39
39
  with: { name: dist, path: dist/ }
40
40
 
41
41
  publish:
@@ -46,7 +46,7 @@ jobs:
46
46
  id-token: write # trusted publishing
47
47
  contents: write # GitHub release
48
48
  steps:
49
- - uses: actions/download-artifact@v4
49
+ - uses: actions/download-artifact@v8
50
50
  with: { name: dist, path: dist/ }
51
51
  - uses: pypa/gh-action-pypi-publish@release/v1
52
52
  - name: GitHub release
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 (2026-10-18)
4
+
5
+ - Google ADK and CrewAI integrations.
6
+ - Semantic Kernel integration (`guard_kernel`): a function-invocation filter, tested through a real Kernel.
7
+ - Claude Agent SDK integration (`guard_options`): in-process PreToolUse / PostToolUse hooks, verified live (a refused force-push, a withheld injected README).
8
+ - `interlock pause` / `resume` kill switch.
9
+ - Linux sandbox (bubblewrap) verified in CI.
10
+ - 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.
11
+ - 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.
12
+ - `interlock bench`: decision, probe, HTTP and hook-shim latency on your machine; see `docs/PERFORMANCE.md`.
13
+ - Faster audit writes: the hash chain's head is cached between appends instead of re-read; decision p50 4.2 ms -> 1.2 ms.
14
+ - `interlock doctor` reports when a newer release is on PyPI (`INTERLOCK_NO_UPDATE_CHECK=1` turns it off).
15
+ - Docs: the hooks apply `fail_mode.default` while the daemon is down (the per-category entries had been described as applying too).
16
+
3
17
  ## 0.1.0 (2026-10-17)
4
18
 
5
19
  First public release.
@@ -12,6 +26,7 @@ First public release.
12
26
  - `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.
13
27
  - Codex: injected tool results are withheld (`decision: block`, measured live).
14
28
  - 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.
29
+ - `interlock pause` / `resume`: a kill switch that refuses every agent action (all or named agents, optionally timed), in any mode.
15
30
  - `interlock egress-proxy`: decides every outbound host (metadata endpoints refused; `--strict` allowlist); `interlock sandbox --egress` makes it the agent's only way out.
16
31
  - `interlock sandbox -- <agent>`: OS-level sandbox (macOS seatbelt; Linux bubblewrap) refusing persistence and guard-rail writes, with optional secret-read and network limits.
17
32
  - 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`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: interlockd
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Interlock daemon: a pre/post-action interlock for coding agents
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -9,12 +9,18 @@ Requires-Dist: pydantic<3,>=2.9
9
9
  Requires-Dist: pyyaml<7,>=6.0
10
10
  Provides-Extra: aws
11
11
  Requires-Dist: boto3<2,>=1.35; extra == 'aws'
12
+ Provides-Extra: claude-agent-sdk
13
+ Requires-Dist: claude-agent-sdk>=0.2; extra == 'claude-agent-sdk'
14
+ Provides-Extra: crewai
15
+ Requires-Dist: crewai>=1.10; extra == 'crewai'
12
16
  Provides-Extra: dev
13
17
  Requires-Dist: boto3<2,>=1.35; extra == 'dev'
14
18
  Requires-Dist: cedarpy<5,>=4.7; extra == 'dev'
15
19
  Requires-Dist: moto[dynamodb]<6,>=5.0; extra == 'dev'
16
- Requires-Dist: pytest<9,>=8.3; extra == 'dev'
20
+ Requires-Dist: pytest<10,>=8.3; extra == 'dev'
17
21
  Requires-Dist: ruff<1,>=0.6; extra == 'dev'
22
+ Provides-Extra: google-adk
23
+ Requires-Dist: google-adk>=2.0; extra == 'google-adk'
18
24
  Provides-Extra: langchain
19
25
  Requires-Dist: langchain-core>=1.0; extra == 'langchain'
20
26
  Provides-Extra: mandate
@@ -23,7 +29,9 @@ Provides-Extra: openai-agents
23
29
  Requires-Dist: openai-agents>=0.20; extra == 'openai-agents'
24
30
  Provides-Extra: promptguard
25
31
  Requires-Dist: torch>=2.3; extra == 'promptguard'
26
- Requires-Dist: transformers<5,>=4.44; extra == 'promptguard'
32
+ Requires-Dist: transformers<6,>=4.44; extra == 'promptguard'
33
+ Provides-Extra: semantic-kernel
34
+ Requires-Dist: semantic-kernel>=1.30; extra == 'semantic-kernel'
27
35
  Provides-Extra: strands
28
36
  Requires-Dist: strands-agents>=1.40; extra == 'strands'
29
37
  Description-Content-Type: text/markdown
@@ -42,6 +50,8 @@ pipx install interlockd # or: uv tool install interlockd
42
50
  interlock init # finds your agents, installs hooks, starts the daemon (shadow mode)
43
51
  ```
44
52
 
53
+ 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.
54
+
45
55
  ```
46
56
  What the gate decides (enforce mode, nothing executed):
47
57
  block git push --force origin main force-push to a protected branch
@@ -61,14 +71,14 @@ What the gate decides (enforce mode, nothing executed):
61
71
  | Cursor (CLI) | ✅ verified live: blocks before execution |
62
72
  | GitHub Copilot CLI | ✅ verified live: blocks before execution, withholds injected tool results |
63
73
  | 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 |
74
+ | 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)` |
75
+ | Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio and remote (HTTP) MCP server, hooks or not |
76
+ | Cline, Copilot in VS Code, and more | hook adapters planned; meanwhile the MCP gateway and `interlock sandbox` cover them |
67
77
 
68
78
  ## How it works
69
79
 
70
80
  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.
81
+ 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
82
  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
83
  4. **Optional judge.** An open-weights decision model can answer "did the user actually ask for this?" for ambiguous actions. Off by default.
74
84
  5. **Everything recorded.** Every decision lands in a local append-only log with its reason.
@@ -98,7 +108,7 @@ Policy lives in `~/.interlock/policy.yaml` (user) and `<repo>/.interlock/policy.
98
108
 
99
109
  ## Honest limits
100
110
 
101
- The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
111
+ 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)).
102
112
 
103
113
 
104
114
  - 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,7 +118,7 @@ The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
108
118
  ## In CI
109
119
 
110
120
  ```yaml
111
- - uses: actions/checkout@v4
121
+ - uses: actions/checkout@v7
112
122
  - uses: interlockd/interlock@v0.1.0 # scan MCP/agent configs, test .interlock/policy.yaml
113
123
  with: { fail-on: high }
114
124
  - uses: github/codeql-action/upload-sarif@v3
@@ -12,6 +12,8 @@ pipx install interlockd # or: uv tool install interlockd
12
12
  interlock init # finds your agents, installs hooks, starts the daemon (shadow mode)
13
13
  ```
14
14
 
15
+ 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.
16
+
15
17
  ```
16
18
  What the gate decides (enforce mode, nothing executed):
17
19
  block git push --force origin main force-push to a protected branch
@@ -31,14 +33,14 @@ What the gate decides (enforce mode, nothing executed):
31
33
  | Cursor (CLI) | ✅ verified live: blocks before execution |
32
34
  | GitHub Copilot CLI | ✅ verified live: blocks before execution, withholds injected tool results |
33
35
  | 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 |
36
+ | 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)` |
37
+ | Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio and remote (HTTP) MCP server, hooks or not |
38
+ | Cline, Copilot in VS Code, and more | hook adapters planned; meanwhile the MCP gateway and `interlock sandbox` cover them |
37
39
 
38
40
  ## How it works
39
41
 
40
42
  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.
43
+ 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
44
  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
45
  4. **Optional judge.** An open-weights decision model can answer "did the user actually ask for this?" for ambiguous actions. Off by default.
44
46
  5. **Everything recorded.** Every decision lands in a local append-only log with its reason.
@@ -68,7 +70,7 @@ Policy lives in `~/.interlock/policy.yaml` (user) and `<repo>/.interlock/policy.
68
70
 
69
71
  ## Honest limits
70
72
 
71
- The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
73
+ 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)).
72
74
 
73
75
 
74
76
  - 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,7 +80,7 @@ The full threat model is in [docs/THREAT-MODEL.md](docs/THREAT-MODEL.md).
78
80
  ## In CI
79
81
 
80
82
  ```yaml
81
- - uses: actions/checkout@v4
83
+ - uses: actions/checkout@v7
82
84
  - uses: interlockd/interlock@v0.1.0 # scan MCP/agent configs, test .interlock/policy.yaml
83
85
  with: { fail-on: high }
84
86
  - uses: github/codeql-action/upload-sarif@v3
@@ -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
@@ -0,0 +1,117 @@
1
+ # Getting started
2
+
3
+ This guide takes about ten minutes. You install Interlock, watch what it would block, switch to enforcing, and then optionally add a team policy, MCP servers, your own agents, and CI. Every step can be undone with `interlock uninstall`.
4
+
5
+ ## 1. Install
6
+
7
+ ```sh
8
+ pipx install interlockd # or: uv tool install interlockd
9
+ interlock init
10
+ ```
11
+
12
+ `init` lists everything it will change and asks first. It then:
13
+
14
+ - finds the coding agents you have (Claude Code, Codex, Cursor, Copilot CLI, Gemini CLI, Windsurf, Antigravity, OpenCode, Kiro);
15
+ - adds a small hook to each one;
16
+ - creates `~/.interlock/`, holding a 0600 token, the audit log and a starter policy;
17
+ - starts the daemon as a background service (launchd on macOS, systemd on Linux).
18
+
19
+ Use `--harness claude-code,codex` to set up only some agents, and `--no-service` to skip the background service.
20
+
21
+ You start in **shadow** mode: every decision is recorded and nothing is blocked. Restart any agent that was already running so it loads the hook. Codex also asks you to review and trust the hook once with `/hooks`.
22
+
23
+ ## 2. Check it is working
24
+
25
+ ```sh
26
+ interlock status # daemon, mode, policy, which agents have hooks
27
+ interlock selftest # sends attack shapes through the live daemon in each agent's hook format; nothing runs
28
+ ```
29
+
30
+ ```
31
+ ✓ claude-code force-push to main block (should stop) git.force_push_protected_branch
32
+ ✓ claude-code pipe a download into a shell block (should stop) egress.unknown_host, shell.pipe_to_shell
33
+ ✓ claude-code upload .env to a paste site block (should stop) egress.unknown_host, egress.upload_sensitive_file
34
+ ✓ claude-code persistence in ~/.zshrc block (should stop) persistence.shell_rc, shell.pipe_to_shell
35
+ ✓ claude-code ordinary test run allow (should pass)
36
+ ```
37
+
38
+ ## 3. Work as usual, then look
39
+
40
+ Use your agent normally for a while. Then:
41
+
42
+ ```sh
43
+ interlock log -f # decisions as they happen
44
+ interlock report # what enforce mode would have blocked, and which rules fired
45
+ interlock explain # the latest stop in plain language: why, what the agent was told, how to override it
46
+ ```
47
+
48
+ ```
49
+ Interlock would have refused a Bash call from claude-code (shadow mode: nothing was enforced):
50
+ git push --force origin main
51
+
52
+ Why:
53
+ - git.force_push_protected_branch (critical): Force-pushing a protected branch rewrites shared history ...
54
+
55
+ If this was wrong, override the rule in ~/.interlock/policy.yaml:
56
+ packs:
57
+ overrides:
58
+ git.force_push_protected_branch: { action: ask } # or enabled: false
59
+ ```
60
+
61
+ If a rule is too noisy for you, override it in `~/.interlock/policy.yaml` and run `interlock restart`. `interlock policy lint` catches typos and warns about overrides that weaken critical rules.
62
+
63
+ ## 4. Enforce
64
+
65
+ ```sh
66
+ interlock enforce
67
+ ```
68
+
69
+ Refused calls now don't run. The agent receives the reason and is told not to retry in a disguised form. Tool results that carry injected instructions are withheld wherever the agent allows that. Claude Code, Codex and Copilot CLI allow it; [ADAPTERS.md](ADAPTERS.md) lists each agent. `interlock shadow` switches back. `interlock pause` refuses every agent action until `interlock resume`.
70
+
71
+ `interlock doctor` checks that protection is really on. It reports missing or edited hooks, agents you have installed that aren't protected, calls that ran while the daemon was down, and whether the audit log is intact.
72
+
73
+ ## 5. Optional: share a policy with your team
74
+
75
+ Commit a policy as `.interlock/policy.yaml` in a repository, starting from [examples/policies/strict-team.yaml](../examples/policies/strict-team.yaml). Everyone running Interlock gets it for calls made inside that repository, with no restart needed. A repository's policy can only make your own stricter, so a poisoned repository can't switch rules off. `interlock policy lint --workspace .` shows what applies.
76
+
77
+ ## 6. Optional: MCP servers
78
+
79
+ ```sh
80
+ interlock scan # audit MCP and agent configs: unpinned packages, inline secrets, broad roots, auto-approved tools
81
+ interlock gateway wrap # route every MCP server through Interlock (unwrap undoes it)
82
+ ```
83
+
84
+ The gateway pins each server's tool definitions, so a definition that changes after approval is caught. It also hides poisoned tools, gates each call with the same rules as the shell, and withholds injected results. It works with any MCP client, including ones without hooks. Restart your agents after wrapping.
85
+
86
+ ## 7. Optional: your own agents
87
+
88
+ Install the extra for your framework, for example `pip install 'interlockd[langchain]'`, then wrap the tools:
89
+
90
+ ```python
91
+ from interlockd.integrations.langchain import guard_tools
92
+
93
+ agent = create_react_agent(model, guard_tools([shell]))
94
+ ```
95
+
96
+ The Claude Agent SDK (`guard_options(options)`), Strands, the OpenAI Agents SDK, Google ADK and CrewAI work the same way. See [examples/agents/](../examples/agents/).
97
+
98
+ ## 8. Optional: stronger containment
99
+
100
+ Hooks only see what an agent reports. To limit an agent at the OS level as well:
101
+
102
+ ```sh
103
+ interlock egress-proxy & # decides every outbound host
104
+ interlock sandbox --egress http://127.0.0.1:7392 -- claude # rc files, ~/.ssh and Interlock's files can't be written; network only via the proxy
105
+ ```
106
+
107
+ ## 9. Optional: CI
108
+
109
+ `interlock scan --fail-on high` and `interlock test` both exit non-zero on a problem. [examples/github-workflow.yml](../examples/github-workflow.yml) runs them in GitHub Actions and uploads SARIF.
110
+
111
+ ## Removing it
112
+
113
+ ```sh
114
+ interlock uninstall # removes hooks and the service; keeps ~/.interlock (add --purge to delete it)
115
+ ```
116
+
117
+ Next: [USER-GUIDE.md](USER-GUIDE.md) for every command and policy option, [THREAT-MODEL.md](THREAT-MODEL.md) for what Interlock can and can't see, [PERFORMANCE.md](PERFORMANCE.md) for latency.
@@ -0,0 +1,33 @@
1
+ # Performance
2
+
3
+ Interlock sits in front of every tool call, so it has to be cheap. Measure it on your own machine with:
4
+
5
+ ```sh
6
+ interlock bench # add --json out.json to keep the numbers
7
+ ```
8
+
9
+ `bench` starts a private daemon in a temporary home (your audit log is not touched) and reports p50 / p95 / p99 in milliseconds for each path.
10
+
11
+ ## Results
12
+
13
+ Interlock 0.1.1 (unreleased), Apple M4 Pro, macOS, Python 3.12:
14
+
15
+ | Path | What it measures | n | p50 ms | p95 ms | p99 ms |
16
+ |---|---|---|---|---|---|
17
+ | decide | the decision pipeline alone, over the whole rule corpus (attack and benign cases, every pack), including the hash-chained audit write | 783 | 1.17 | 1.87 | 2.61 |
18
+ | probe 4 KB | a post-action decision on a 4 KB tool result: the local injection probe plus the audit write | 200 | 6.07 | 8.46 | 12.99 |
19
+ | http | one hook call to the daemon over loopback HTTP, as an agent's hook delivers it | 300 | 4.87 | 8.46 | 16.24 |
20
+ | shim (tcp) | the full hook path an agent pays: the POSIX `sh` shim, `curl`, the daemon, over TCP | 100 | 92.49 | 149.78 | 213.25 |
21
+ | shim (unix) | the same over the daemon's 0600 Unix socket | 100 | 93.67 | 215.43 | 352.82 |
22
+ | floor | `sh` + `cat` + `curl` to the health endpoint, no decision | 100 | 89.13 | 161.42 | 213.56 |
23
+
24
+ ## Reading the numbers
25
+
26
+ - **Decisions take about a millisecond.** The deterministic rules, the shell decomposer and the audit write together take 1.2 ms at p50. That is the "~2 ms per decision" figure, and p99 stays under 3 ms.
27
+ - **The hook shim mostly costs process spawn.** On this machine, starting `sh` and `curl` with no decision at all (the floor row) takes about as long as the full shim. Interlock adds the http row, about 5 ms. Process start-up varies a lot between machines. Endpoint security agents that inspect every `exec` make it much slower, so measure on yours.
28
+ - **Post-action is slower than pre-action** because the injection probe scans the whole result (up to 20,000 characters). An agent waits for this after the tool has already run, not before.
29
+ - **The optional model is not in these numbers.** With `judge` or a classifier enabled, ambiguous cases add the model's latency. The deterministic path still answers first, so clear cases never wait for the model.
30
+
31
+ ## Timeouts and fail mode
32
+
33
+ The shim gives the daemon 1.5 s before a tool runs (`INTERLOCK_PRE_TIMEOUT_MS`) and 2.5 s after (`INTERLOCK_POST_TIMEOUT_MS`). Both are hundreds of times the measured p99. A call that times out is handled by the fail mode. It fails open by default, and refuses the call if `fail_mode.default` is `closed`. Every such call is logged to `~/.interlock/hook.log`, and `interlock doctor` reports it.
@@ -39,11 +39,12 @@ Stated plainly, because a security tool that overstates its coverage is dangerou
39
39
  | Limit | Why | Mitigation |
40
40
  |---|---|---|
41
41
  | Only actions that pass through an enforcement point are gated | a process the agent already started, a second agent without hooks, or a tool the hook matcher doesn't cover is invisible to the hooks | install for every agent (`interlock init`); route MCP through the gateway; run the agent under `interlock sandbox`, which refuses persistence and guard-rail writes at the OS level whatever path they take; `doctor` and `scan` flag unprotected agents. `interlock sandbox --egress` confines the agent's network to localhost and routes it through `interlock egress-proxy` |
42
- | The agent runs as your user | it can technically edit hook files, the policy, or the shim | Interlock refuses writes to agent configs and its own files; `interlock doctor` detects removed hooks and modified shims; the audit log is hash-chained (`interlock audit verify`) |
43
- | Default fail mode is open for most categories | a stopped daemon should not stop all work | the high-risk categories (exfiltration, malicious commands, persistence, secret leaks, privilege escalation) fail closed; `doctor` reports calls that ran while the daemon was down |
42
+ | The agent runs as your user | it can technically edit hook files, the policy, or the shim | Interlock refuses writes to agent configs and its own files; the daemon re-checks its hooks every minute and records a removed hook or a modified shim in the hash-chained audit log (`interlock report` lists them), and `interlock doctor` reports them (`interlock audit verify` checks the chain) |
43
+ | Default fail mode is open | a stopped daemon should not stop all work; without the daemon a hook cannot tell a dangerous call from a safe one | set `fail_mode.default: closed` to refuse every call while the daemon is down; wrapped remote MCP servers are unreachable without it; `doctor` reports calls that ran while the daemon was down |
44
44
  | Shell analysis is static | commands assembled at runtime (strings built and executed later) can hide intent | constructs that build-and-execute (eval, decode-and-run, pipe to a shell) are themselves rules; ambiguous cases ask |
45
45
  | Injection detection is heuristic | a novel phrasing can score below the threshold | multiple detectors compose deny-wins (local heuristics, optional classifier, Bedrock Guardrails, the judge); toxic-flow detection catches the dangerous *combination* even when one step is missed |
46
46
  | Some agents can't replace a tool result | the hook contract doesn't allow it (Kiro, Windsurf, Antigravity, Cursor for non-MCP tools) | the detection is recorded and visible in `interlock log` / `report`; warnings are added where supported |
47
+ | A repository's own `.interlock/policy.yaml` is written by whoever commits to it | a poisoned repository could try to switch rules off | repository policies only tighten yours unless you list the repository in `trust.trusted_workspaces`; `policy lint --workspace` shows what was not applied |
47
48
  | Shadow mode enforces nothing | it is the default so you can see decisions first | `interlock report` shows what enforce would have done; `interlock enforce` |
48
49
  | Adapters built from documentation | contracts have differed from their docs before | adapters say "built, not verified live" until payloads are captured from the real tool |
49
50