Interlock puts guardrails on the few effects that matter — force-push main, delete files, publish a release. Those effects cannot happen without you. You add one ready-made file. The broker owns the effect. The coding agent still makes every other edit.
$ curl -fsSL https://raw.githubusercontent.com/operatorstack/interlock/main/install.sh | sh
> irm https://raw.githubusercontent.com/operatorstack/interlock/main/install.ps1 | iex
$ interlock init --template main-branch
Two are infrastructure you stand up and maintain. One is a file you already have. The coding agent proposes; the broker publishes.
Prompts, AGENTS.md files, and skills guide an agent. But they only advise the agent. A capable agent can ignore them, forget them, or find another path. You learn about the problem too late. Interlock controls the effect itself. The guardrail is not a suggestion.
You write the rule in a skill, an AGENTS.md file, or a system prompt. The text only advises the agent. The agent decides whether it obeys. It can ignore the rule, forget it during a long run, or find another path.
# Repository rules
Never force-push main.
Ask a human before you push main.
You write the same rule in a typed library. It compiles to one canonical policy. The broker owns the effect. So the agent cannot bypass it. The decision stays the same on every run.
// fluent builder → canonical IR
il.Policy("repository-policy.v1").
Actor("agent").
Branch("main", "repo://branch/main").
Deny("deny-force-push-main").By("agent").
To(il.ForcePush).On("main").
Because("force-pushing main is not allowed").Add().
Allow("push-main").By("agent").
To(il.Push).On("main").
Requiring(il.HumanApproval("release-main")).
Because("pushing main needs human approval").Add()
// @operatorstack/interlock
policy("repository-policy.v1")
.actor("agent")
.branch("main", "repo://branch/main")
.deny("deny-force-push-main").by("agent")
.to(ForcePush).on("main")
.because("force-pushing main is not allowed")
.allow("push-main").by("agent")
.to(Push).on("main")
.requiring(humanApproval("release-main"))
.because("pushing main needs human approval")
.emit();
# interlock
(Policy("repository-policy.v1")
.actor("agent")
.branch("main", "repo://branch/main")
.deny("deny-force-push-main").by("agent")
.to(FORCE_PUSH).on("main")
.because("force-pushing main is not allowed")
.allow("push-main").by("agent")
.to(PUSH).on("main")
.requiring(human_approval("release-main"))
.because("pushing main needs human approval")
.emit())
// interlock crate
Policy::new("repository-policy.v1")
.actor("agent")
.branch("main", "repo://branch/main")
.deny("deny-force-push-main").by("agent")
.to(ForcePush).on("main")
.because("force-pushing main is not allowed")
.allow("push-main").by("agent")
.to(Push).on("main")
.requiring(human_approval("release-main"))
.because("pushing main needs human approval")
.emit()?;
The agent proposes; the broker publishes. That's the difference between hoping the agent follows the rules and knowing it can't skip the ones that matter.
Interlock keeps its authority at the effect boundary. The broker owns push and publish. Any agent can propose a change. But no agent can reach that authority. You do not change tools. You do not install a plugin. You do not reconfigure your agent. This works even with no hook.
Send the decisions through Pitot. Pitot is our open action-transport protocol. It works with any agent. Pitot uses your agent's own pre-tool hook. A denial then appears in the session, such as the Blocked by Interlock line above. You add one line of configuration. You add no fork, no plugin, and no MCP server.
PreToolUse → pitot hook claude
Many tools use pre-tool hooks to control agents. For those tools, the hook is the guarantee. But an agent can bypass the hook. The hook also cannot see the writes that a shell command starts. For Interlock, the hook only shows the decision. The guarantee is the control of the effect.
Interlock does not exaggerate. This is what Interlock guarantees. This is also what it does not guarantee.
Interlock does not watch every keystroke or every file write. It controls the few effects that matter, such as push and publish. The agent cannot do these effects without the guardrail.
Interlock denies the effect if it finds a mismatch, such as wrong evidence or a changed policy. Your repository stays unchanged.
Interlock controls who can do an effect. It does not give cryptographic authenticity. It uses no MCP server.
"Interlock does not define whether work is correct, whether an artifact is safe to ship, or what 'released' means."
Laptop, coding agent, cloud sandbox, CI — the transport changes, the authority does not.
Install the CLI. Then pick a template. interlock init --template scaffolds a policy module. interlock compile builds the canonical IR. interlock doctor reports what your setup can build.
$ curl -fsSL https://raw.githubusercontent.com/operatorstack/interlock/main/install.sh | sh
> irm https://raw.githubusercontent.com/operatorstack/interlock/main/install.ps1 | iex
$ go install github.com/operatorstack/interlock/cmd/interlock@latest
$ interlock doctor