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.
- interlockd-0.1.1/.github/ISSUE_TEMPLATE/agent.yml +20 -0
- interlockd-0.1.1/.github/ISSUE_TEMPLATE/bug.yml +29 -0
- interlockd-0.1.1/.github/ISSUE_TEMPLATE/config.yml +5 -0
- interlockd-0.1.1/.github/ISSUE_TEMPLATE/decision.yml +34 -0
- interlockd-0.1.1/.github/dependabot.yml +9 -0
- interlockd-0.1.1/.github/pull_request_template.md +10 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/.github/workflows/ci.yml +18 -6
- {interlockd-0.1.0 → interlockd-0.1.1}/.github/workflows/release.yml +4 -4
- {interlockd-0.1.0 → interlockd-0.1.1}/CHANGELOG.md +15 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/PKG-INFO +19 -9
- {interlockd-0.1.0 → interlockd-0.1.1}/README.md +8 -6
- {interlockd-0.1.0 → interlockd-0.1.1}/action.yml +3 -3
- interlockd-0.1.1/docs/GETTING-STARTED.md +117 -0
- interlockd-0.1.1/docs/PERFORMANCE.md +33 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/THREAT-MODEL.md +3 -2
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/USER-GUIDE.md +31 -6
- interlockd-0.1.1/examples/agents/README.md +17 -0
- interlockd-0.1.1/examples/agents/claude_agent_sdk_agent.py +18 -0
- interlockd-0.1.1/examples/agents/crewai_crew.py +21 -0
- interlockd-0.1.1/examples/agents/google_adk_agent.py +15 -0
- interlockd-0.1.1/examples/agents/langgraph_agent.py +19 -0
- interlockd-0.1.1/examples/agents/openai_agents.py +17 -0
- interlockd-0.1.1/examples/agents/semantic_kernel_agent.py +24 -0
- interlockd-0.1.1/examples/agents/strands_agent.py +17 -0
- interlockd-0.1.1/examples/github-workflow.yml +16 -0
- interlockd-0.1.1/examples/policies/ci.yaml +11 -0
- interlockd-0.1.1/examples/policies/mcp-locked-down.yaml +15 -0
- interlockd-0.1.1/examples/policies/strict-team.yaml +29 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/__init__.py +1 -1
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/api.py +1 -0
- interlockd-0.1.1/interlockd/bench.py +113 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/cli.py +69 -5
- interlockd-0.1.1/interlockd/control.py +69 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/describe.py +1 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/insights.py +111 -24
- interlockd-0.1.1/interlockd/integrations/claude_agent_sdk.py +84 -0
- interlockd-0.1.1/interlockd/integrations/crewai.py +79 -0
- interlockd-0.1.1/interlockd/integrations/google_adk.py +62 -0
- interlockd-0.1.1/interlockd/integrations/semantic_kernel.py +61 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/claude_code.py +1 -1
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/onboard.py +2 -2
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/pipeline.py +29 -6
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/policy/__init__.py +1 -0
- interlockd-0.1.1/interlockd/policy/workspace.py +262 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/claude-code/interlock-hook.sh +1 -1
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/kiro/interlock-hook.mjs +1 -1
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/kiro/interlock-hook.sh +1 -1
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/spool.py +26 -7
- interlockd-0.1.1/interlockd/watch.py +78 -0
- interlockd-0.1.1/packaging/homebrew/Formula/interlock.rb +57 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/pyproject.toml +7 -3
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/conftest.py +7 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_api.py +4 -1
- interlockd-0.1.1/tests/test_bench.py +17 -0
- interlockd-0.1.1/tests/test_claude_agent_sdk.py +77 -0
- interlockd-0.1.1/tests/test_control.py +64 -0
- interlockd-0.1.1/tests/test_crewai.py +63 -0
- interlockd-0.1.1/tests/test_examples.py +27 -0
- interlockd-0.1.1/tests/test_google_adk.py +92 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_insights.py +9 -0
- interlockd-0.1.1/tests/test_repo_policy.py +126 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_sandbox.py +26 -0
- interlockd-0.1.1/tests/test_semantic_kernel.py +72 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_spool.py +11 -0
- interlockd-0.1.1/tests/test_version.py +29 -0
- interlockd-0.1.1/tests/test_watch.py +56 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/.gitignore +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/CONTRIBUTING.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/LICENSE +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/SECURITY.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/README.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/antigravity/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/claude-code/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/claude-code/README.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/codex/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/codex/README.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/copilot-cli/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/cursor/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/gemini-cli/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/kiro/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/opencode/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/adapters/windsurf/PAYLOADS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/README.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/_corpus.yaml +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/_corpus.yaml +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/exfil_and_secrets.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/git_and_history.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/injection_persistence_supplychain.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_confirmation.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_exfil.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_injection.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/judge_intent.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/archetypes/slow_hijack.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/README.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/samples/agentdojo.sample.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/samples/agentdrift.sample.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/external/samples/injecagent.sample.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/ogr-seed-v0/README.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/corpus/ogr-seed-v0/seed-v0.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/ADAPTERS.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/mandate-example.cedar +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/policy-example.yaml +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/reports/M3-judge-calibration.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/reports/ogr-seed-v0-results.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/docs/reports/ogr-seed-v0-results.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/__main__.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/aibom.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/auth.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/calibration.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/compose.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/config.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/corpus.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/_corpus.yaml +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/egress.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/fileops.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/git.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/packages.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/persistence.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/secrets.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/data/packs/shell.jsonl +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/__init__.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/engine.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/flow.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/mandate.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/mcp.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/memory.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/packs.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/det/shellparse.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/egress.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/gateway.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/ids.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/__init__.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/client.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/langchain.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/openai_agents.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/integrations/strands.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/__init__.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/guardrails.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/local_probe.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/projector.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/judges/strands_decider.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/ledger.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/mcp_http.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/mcp_proxy.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/mcp_server.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/models.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/__init__.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/cursor.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/gemini_cli.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/kiro.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/normalize/more.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/ogr.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/otel.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/paths.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/policy_lint.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/render.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/sandbox.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/scan.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/schema/verdict.schema.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/selftest.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/service.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/shims/opencode/interlock.js +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/__init__.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/drainer.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/dynamodb.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/store/memory.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/testkit.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/tiers.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/transcript/__init__.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/transcript/claude_code.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/interlockd/transcript/kiro.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/packaging/homebrew/README.md +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/packaging/homebrew/make_formula.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fake_mcp_server.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/post-bash-inject.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/post-read-inject.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/pre-bash-block.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/claude-code/pre-bash-inject.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/post-bash-benign.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/post-bash-inject.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/pre-bash-benign.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/codex/pre-bash-block.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/copilot-cli/post-bash-inject.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/copilot-cli/pre-bash-benign.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/copilot-cli/pre-bash-block.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/post-read-benign.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/post-shell-benign.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/pre-read-benign.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/pre-shell-benign.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/fixtures/cursor/pre-shell-block.json +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_aibom.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_audit_chain.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_calibration.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_claude_code.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_corpus.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_cursor_gemini.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_dynamodb.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_egress.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_flow.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_gateway.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_homebrew_formula.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_interlock_test.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_judge.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_langchain.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_local_probe.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_http.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_policy.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_proxy.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_mcp_server.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_memory.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_more_adapters.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_normalize_kiro.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_ogr.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_onboard.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_openai_agents.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_opencode.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_otel.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_packs.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_pipeline.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_policy_lint.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_scan.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_selftest.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_shim_sh.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_strands.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_tiers.py +0 -0
- {interlockd-0.1.0 → interlockd-0.1.1}/tests/test_transcript.py +0 -0
- {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,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@
|
|
19
|
-
- uses: astral-sh/setup-uv@
|
|
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:
|
|
32
|
+
- name: Linux sandbox (bubblewrap)
|
|
33
|
+
if: runner.os == 'Linux'
|
|
33
34
|
run: |
|
|
34
|
-
|
|
35
|
-
|
|
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@
|
|
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@
|
|
20
|
-
- uses: astral-sh/setup-uv@
|
|
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@
|
|
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@
|
|
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.
|
|
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<
|
|
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<
|
|
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: `
|
|
65
|
-
| Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio MCP server, hooks or not |
|
|
66
|
-
|
|
|
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
|
|
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@
|
|
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: `
|
|
35
|
-
| Any MCP client | ✅ the MCP gateway (`interlock gateway wrap`) gates every stdio MCP server, hooks or not |
|
|
36
|
-
|
|
|
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
|
|
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@
|
|
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:
|
|
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@
|
|
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;
|
|
43
|
-
| Default fail mode is open
|
|
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
|
|