<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Foreguard · Preview what your AI agent is about to do</title>
<meta name="description" content="Foreguard is a dry-run trust layer for AI agents. It sits between an MCP host and its tool server, previewing every mutating tool call before it runs and forcing human approval when untrusted data is driving the action." />
<link rel="canonical" href="https://foreguard.nlj.dev/" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Foreguard · Preview what your AI agent is about to do" />
<meta property="og:description" content="A dry-run trust layer for AI agents. Preview mutating tool calls before they run, and catch prompt injection at the tool boundary." />
<meta property="og:url" content="https://foreguard.nlj.dev/" />
<meta name="twitter:card" content="summary" />
<style>
:root{
--bg:#0a0a0b; --panel:#0f0f11; --line:#1e1e22; --line-2:#2a2a30;
--fg:#e8e8ea; --muted:#9a9aa4; --faint:#6a6a74;
--green:#4ade80; --red:#f87171; --amber:#fbbf24; --accent:#7dd3fc;
--mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
--sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,sans-serif;
}
*{box-sizing:border-box}
body{
margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);
line-height:1.65;-webkit-font-smoothing:antialiased;
}
.wrap{max-width:860px;margin:0 auto;padding:0 24px}
a{color:var(--accent)}
header{border-bottom:1px solid var(--line);padding:20px 0}
.brand{font-family:var(--mono);font-size:15px;letter-spacing:-.01em}
.brand b{font-weight:600}
.brand span{color:var(--faint)}
.hero{padding:64px 0 40px}
.tag{
display:inline-block;font-family:var(--mono);font-size:11.5px;letter-spacing:.06em;
text-transform:uppercase;color:var(--muted);border:1px solid var(--line-2);
border-radius:999px;padding:5px 11px;margin-bottom:22px;
}
h1{font-size:clamp(30px,5.2vw,46px);line-height:1.12;letter-spacing:-.03em;margin:0 0 18px;font-weight:650}
.lead{font-size:17.5px;color:var(--muted);margin:0 0 26px;max-width:660px}
.lead b{color:var(--fg);font-weight:600}
.cta{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:8px}
.btn{
font-family:var(--mono);font-size:13.5px;text-decoration:none;color:var(--fg);
border:1px solid var(--line-2);border-radius:8px;padding:9px 14px;background:var(--panel);
}
.btn:hover{border-color:#3a3a44}
.btn.primary{background:var(--fg);color:#0a0a0b;border-color:var(--fg);font-weight:600}
h2{font-size:22px;letter-spacing:-.02em;margin:0 0 6px;font-weight:620}
section{padding:34px 0;border-top:1px solid var(--line)}
.kicker{
font-family:var(--mono);font-size:11px;letter-spacing:.09em;text-transform:uppercase;
color:var(--faint);display:block;margin-bottom:9px;
}
p{color:var(--muted);margin:0 0 14px}
p b{color:var(--fg);font-weight:600}
.term{
border:1px solid var(--line);border-radius:10px;background:#08080a;overflow:hidden;margin:20px 0;
}
.term-bar{
font-family:var(--mono);font-size:11px;letter-spacing:.05em;text-transform:uppercase;
color:var(--faint);padding:9px 14px;border-bottom:1px solid var(--line);
}
pre{
margin:0;padding:15px;font-family:var(--mono);font-size:12.5px;line-height:1.7;
color:#a7adb8;overflow-x:auto;white-space:pre;
}
.r{color:var(--red)} .a{color:var(--amber)} .g{color:var(--green)} .f{color:var(--faint)}
ul{color:var(--muted);padding-left:20px;margin:0 0 14px}
li{margin-bottom:9px}
li b{color:var(--fg);font-weight:600}
code{font-family:var(--mono);font-size:.9em;background:#141418;border:1px solid var(--line);
border-radius:4px;padding:1px 5px;color:#c8ccd4}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:14px;margin-top:18px}
.card{border:1px solid var(--line);border-radius:10px;padding:16px;background:var(--panel)}
.card h3{font-family:var(--mono);font-size:12.5px;margin:0 0 7px;color:var(--fg);font-weight:600}
.card p{font-size:13.5px;margin:0}
.note{border-left:2px solid var(--line-2);padding-left:14px;margin:18px 0}
footer{border-top:1px solid var(--line);padding:26px 0 44px;color:var(--faint);font-size:13.5px}
footer a{color:var(--muted)}
</style>
</head>
<body>
<header>
<div class="wrap brand"><b>foreguard</b> <span>/ a dry-run trust layer for AI agents</span></div>
</header>
<div class="wrap">
<div class="hero">
<span class="tag">Rust · source-available · BUSL-1.1</span>
<h1>Preview what your AI agent is about to do, before it does it.</h1>
<p class="lead">
Autonomous agents are powerful and unnerving for the same reason: you cannot see what they
are about to do until it is already done. Foreguard sits between any MCP host and its tool
server. Read-only calls pass straight through. <b>Mutating calls are intercepted and
previewed</b>, showing the concrete effect rather than a bare verdict.
</p>
<div class="cta">
<a class="btn primary" href="https://crates.io/crates/foreguard">crates.io</a>
<a class="btn" href="https://github.com/nlj3/foreguard">GitHub</a>
<a class="btn" href="https://docs.rs/foreguard">docs.rs</a>
</div>
</div>
<section>
<span class="kicker">Install</span>
<h2>One command.</h2>
<div class="term"><pre>cargo install foreguard</pre></div>
<p>Then put it in front of the tool server your agent already uses. No agent changes, no prompt changes.</p>
<div class="term">
<div class="term-bar">.mcp.json / claude_desktop_config.json</div>
<pre>{
"mcpServers": {
"filesystem": {
"command": "foreguard",
"args": ["proxy", "--", "npx", "-y",
"@modelcontextprotocol/server-filesystem", "/path"]
}
}
}</pre>
</div>
</section>
<section>
<span class="kicker">Prompt injection</span>
<h2>It also tracks where the data came from.</h2>
<p>
Most defenses scan text, trying to recognise a malicious instruction by how it reads. That is
an arms race against natural language. Foreguard ignores what the text says and tracks
<b>where it came from</b>. With <code>--taint</code>, output from untrusted sources such as a
web fetch, an inbox read, or RAG retrieval is marked. If that data later turns up inside a
mutating call, it has violated Meta's Agents "Rule of Two", and the call stops for a human.
</p>
<div class="term">
<div class="term-bar">real session output · a poisoned page tries to exfiltrate</div>
<pre><span class="r">⛔ RULE-OF-TWO VIOLATION</span> this mutation carries
untrusted data (<span class="a">`attacker@evil.com`</span>); forcing human approval.
<span class="a">⚠ `send_email` (high risk)</span> · sends to attacker@evil.com
Execute this for real? [y/N]
<span class="g">✗ denied, dry-run, nothing executed</span></pre>
</div>
<p>
Approve instead, and the <b>exact call you previewed</b> is what executes. Nothing is
re-planned or re-generated between the preview and the action.
</p>
</section>
<section>
<span class="kicker">The loop</span>
<h2>Record, review, promote.</h2>
<div class="grid">
<div class="card">
<h3>proxy</h3>
<p>Sit between host and tool server. Mutations are previewed with their concrete effect and never executed.</p>
</div>
<div class="card">
<h3>--approve</h3>
<p>Each mutation pauses for a decision on your terminal. Only an explicit yes runs it.</p>
</div>
<div class="card">
<h3>--taint</h3>
<p>Track provenance. Untrusted data reaching a mutating call forces approval automatically.</p>
</div>
<div class="card">
<h3>--ledger</h3>
<p>Append a JSON line per call: what was asked, how it was judged, and what happened.</p>
</div>
<div class="card">
<h3>promote</h3>
<p>Replay a recorded ledger against a live server, running the same calls you already reviewed.</p>
</div>
<div class="card">
<h3>plan</h3>
<p>Score a batch of proposed tool calls offline and get a Mutation Plan, with JSON output.</p>
</div>
</div>
</section>
<section>
<span class="kicker">Honest limits</span>
<h2>Where the guarantee ends.</h2>
<p>
Taint tracking is <b>best-effort, not sound</b>, and saying so is the point. Foreguard sees
tool inputs and outputs, not the model's hidden reasoning, so data the model paraphrases or
re-encodes can slip a substring match. What it reliably catches is the common, un-laundered
path from untrusted input to mutating action.
</p>
<div class="note">
<p>
Where it is uncertain, it fails safe. A tainted mutation with no terminal attached is
denied rather than executed. The ledger is an audit log, not a tamper-proof one.
</p>
</div>
<p>
A security tool that overstates its guarantee is worse than one that draws the boundary
clearly and then holds it.
</p>
</section>
<section>
<span class="kicker">Origin</span>
<h2>Extracted from kedge.</h2>
<p>
Foreguard does not reinvent the engine, it extracts one. The fail-safe, deny-wins tool
classifier comes from <a href="https://github.com/nlj3/kedge">kedge</a>, a deterministic
AI-agent harness. Same code, one source of truth, published as a dependency rather than
forked.
</p>
</section>
</div>
<footer>
<div class="wrap">
Built by <a href="https://nlj.dev">Noel Jackson III</a> ·
<a href="https://github.com/nlj3/foreguard">source</a> ·
<a href="https://crates.io/crates/foreguard">crates.io</a>
</div>
</footer>
</body>
</html>