Local guard layer for AI coding agents. Supported on Claude Code and Codex, with experimental Cursor, Copilot CLI, Gemini CLI, OpenCode, and Pi integrations; evaluates delivered tool hooks against deterministic rules; blocks the dangerous ones in milliseconds; gates new dependencies behind a fail-closed allowlist; keeps a local activity log you can grep.
Node 22+, macOS or Linux. No npm package yet. The 0.2 release goes to the waitlist first:
$ claude ❯ clean up the build artifacts before deploy I'll remove the build output now. → Bash: rm -rf / ⚠ BLOCKED by interlinked harness rule: builtin-rm-rf-root (critical) reason: Recursive deletion of root-level or wildcard paths is dangerous. hint: Be more specific about what to delete. ms: 1 Let me try the project's build output instead. → Bash: rm -rf ./dist ✓ allowed Removed 14 files.
Illustrative. The rule (builtin-rm-rf-root) and reason text are loaded verbatim from src/harness/rules/builtin-rules-processes.ts; the surrounding agent dialogue is synthesized to show the block-and-recover flow.
The receipt
The author dogfooded the harness on his own machine while writing it. Across every surviving rotation of the activity log, 3834 distinct guard_block events were captured; the rows below are the ones that survived a per-event audit against Claude Code's session transcripts to confirm the agent's actual tool_input.
Some early dramatic-looking categories (shutdown, root-level rm -rf, DROP TABLE) turned out to be false positives — the rules' substring matchers fired on commit-message bodies, echo arguments, and grep patterns. Those rules were tightened with command-start anchors and quote masking; the table reflects only blocks where the agent's resolved command actually matched the row's intent.
Two more deflations keep the numbers honest: 291 duplicate events from a since-fixed hook over-registration bug (one blocked call logged 3–4×) are collapsed to one, and 11 grep-accelerator "block-and-answer" events — the trigram index answering a search, not enforcement — are excluded entirely.
Data window 2026-05-14 → 2026-09-01, audited 2026-09-01. Two earlier log segments (2026-04-24 → 2026-05-14; a prior audit over that longer window verified 1175 blocks total) were lost and are excluded rather than estimated. Audit method: npm run docs:audit-receipts (re-runs the per-event transcript correlation locally).
| Count | Severity | What the agent tried |
|---|---|---|
| 580 | high | Edits that introduced a new TypeScript error — blocked before the write landed (tsc-diff-overlay) |
| 93 | high | Created a new source file with no companion test |
| 128 | high | Used shell redirect (cat > file.ts) to dodge the content-quality gate |
| 20 | critical | Wrote outside the repo root (e.g. /tmp/x.ts) |
| 23 | high | Empty catch {} blocks landing in code |
| 24 | critical | kill / pkill / killall at running processes — four aimed at the harness or session itself |
| 26 | high | Edited a file another agent held the reservation on (multi-agent lease enforced) |
| 10 | high | Destructive git: reset --hard, branch -D, stash drop |
| 2 | critical | Secrets detected in proposed write content — blocked before landing on disk |
| 5 | critical | Package installs not on the team allowlist (fail-closed supply-chain gate) |
Verified blocks
911
PreToolUse eval p50
3 ms
Distinct blocks logged
3834
Built-in rules
121
How it works
Every PreToolUse hook fires a JSON-RPC message at .interlinked/harness.sock. The harness evaluates rules, returns a decision, and (optionally) attaches warnings the agent will see on its next turn.
Local-only
No cloud, no telemetry, no LLM in the hot path. The socket is on your machine; the activity log stays in your repo.
Multi-runner
Same harness for Claude Code, Codex, Cursor, Copilot CLI, Gemini CLI, opencode, and Pi. One config, every agent.
Deterministic
Regex + AST + a handful of compilers (tsc, biome, gitleaks, semgrep). Replayable from the JSONL log.
Supply-chain gate
New dependencies are blocked until allowlisted — npm, pnpm, yarn, bun, pip, pipx, poetry, uv, cargo, gem, bundle, go, composer, dotnet, mvn. URL installs and registry overrides always blocked. Fail-closed even when the daemon is down.
Coverage-aware
Per-edit coverage, cyclomatic-complexity, and CRAP-score gates wired to real coverage data — an edit that drops a file below its ratchet is blocked before it lands.
Trajectory-aware
23 sequence detectors watch behavior across the session, not single calls — secret read → network call, download → execute, stale read → write, same failing command three times without observing.
# the hot path Claude / Codex / Cursor / Copilot / Gemini ├─→ interlinked-hook OpenCode / Pi ├─→ managed plugin / extension bridge │ ├─→ .interlinked/harness.sock │ │ └─→ guard eval (block / allow / warn) ............ 3 ms p50 │ │ └─→ post-edit quality + structural checks ........ ~0.1–3 s (compilers) │ │ │ └─→ .interlinked/activity.jsonl (append-only, ~0.1 ms) │ └─→ interlinked {status, activity, explain, doctor}
Why this and not …
| Capability | Interlinked | Husky / pre-commit | claude-code-action (CI) | No harness |
|---|---|---|---|---|
| Blocks tool calls before the agent runs them | ✓ PreToolUse hook | — | — (CI is post-action) | — |
| Runs locally, no network on hot path | ✓ | ✓ | ✗ runs in GitHub | — |
| Multi-agent runner support | ✓ 7 runners | — (git only) | — (Claude Code only) | — |
| Median rule-eval latency | 3 ms (p50, n=35k) | ~50–500 ms | 10s+ (CI startup) | — |
| Blocks unapproved package installs (supply chain) | ✓ fail-closed allowlist, 15 package managers | — | post-merge at best | — |
| Logs every PreToolUse / PostToolUse | ✓ JSONL, replayable | — | CI logs only | — |
| Catches dangerous shell commands (rm -rf, kubectl delete --all, …) | ✓ 121 rules | — (file lints, not commands) | depends on prompt | — |
| Compiles proposed file content before write lands | ✓ tsc / biome diff-overlay | post-commit only | CI only | — |
| Open-source, MIT | ✓ | ✓ | ✓ | — |
Husky and pre-commit are great at what they do — git-time linting on file content. They sit downstream of the moment the agent runs an arbitrary shell command. Interlinked sits at that moment.
FAQ
.interlinked/activity.jsonl in the repo it's instrumenting. Network happens only on explicit opt-in: server-backed commands you run (login, sync), allowlist admission screens (registry metadata + OSV advisories, human-invoked, never on the hook path), and — if you enable sponsor slots — a signed sponsor-feed fetch plus anonymous impression beacons (no accounts; interlinked sponsor disable removes it all).
git clone && npm link is a deliberate filter for serious users. A 0.2 release on npm is planned once the surface stabilizes.
balanced / lenient / strict) governs the second layer.
tsc, biome) take whatever the compiler takes; for a typical TS file that's a few hundred milliseconds, paid on code-file edits — reads and shell commands skip it. Latency budgets are enforced per event class.
.interlinked/guard-rules.json is committed and read by every machine that has the harness; .interlinked/guard-rules.local.json is gitignored and used for personal overrides. The /enforce skill also distills imperative prose from AGENTS.md / CLAUDE.md into the same JSON format.
Waitlist
Sponsors
One tasteful, clickable line under the harness status of every opted-in terminal. Free for friends’ personal projects right now. Every line and destination link is reviewed by a human and cryptographically signed before it renders anywhere — submitting puts you in the review queue, nothing more.
Submissions are sanitized, capped, and never served directly — terminals render only feeds signed offline after review. Clicks route through this domain, so an approved link can’t be silently swapped afterward.
Five minutes from git clone to your first guard_block in .interlinked/activity.jsonl.