New — v0.10

Stop the shell command before your AI agent runs it.

Your AI calls verify_shell_exec first. Deslint reads .deslint/policy.yml and returns allow / warn / deny in under a millisecond. Same input, same verdict, every time.

Sub-1ms warm7 built-in dangerous-pattern checksLocal-first · zero egressMCP-native · MIT

Why a deterministic firewall

An AI cannot be its own firewall.

Every IDE will ship an “AI reviews AI” layer by 2027. Useful for taste, unusable as a compliance control: variance per run, slow per call, cloud round-trip required, no signed verdict. Four properties separate the firewall from a reviewer:

PropertyWhy AI can't matchWhy customers need it
DeterminismLLMs have variance per run — same prompt, different output.CI gates need pass/fail consistency. Auditors require reproducibility.
Sub-10ms latencyLLMs take 1–30 seconds. Agent loops can't tolerate that on every action.Pre-execution gating only works if it's cheaper than the agent's own thinking cycle.
Local-firstCloud AI services need data to function. Regulated industries forbid egress.Finance, health, government, defense — entire verticals can't use cloud LLM tools.
Auditable verdictsLLM verdicts can't be cited or signed. Same input, different verdict tomorrow.Every block produces a citable reason and matched-pattern. Auditor-ready trail.

The verify_before_* family

Five interceptors. One policy file.

The firewall ships interceptors incrementally. The policy DSL is stable from v0.10 forward — author the whole policy now and each interceptor starts enforcing as it lands.

verify_shell_exec Shipped v0.10

Pre-execution gate for shell commands. Allow / deny / built-in dangerous-pattern checks.

`pnpm publish` → deny (denylist) · `rm -rf /` → deny (builtin:destructive-rm)
verify_outbound_requestNext

Allowlist hosts the agent may call; block private/loopback/metadata IPs (SSRF) at runtime.

`fetch(req.body.url)` → deny (private-IP target)
verify_file_readPlanned

Confine reads to project root; flag reads of .env, secrets directories, and credential stores.

`fs.readFile("../../.aws/credentials")` → deny (outside-root)
verify_secret_accessPlanned

Intercept process.env.* reads; flag access to unallowlisted secret keys.

`process.env.STRIPE_SECRET` from a script → deny (unallowlisted)
verify_git_opPlanned

Block force-push to protected branches; block history rewrites and tag deletes.

`git push --force origin main` → deny (protected branch)

Built-in dangerous-pattern checks

7 curated regexes you don't have to write yourself.

Every policy ships with a vetted set of dangerous-pattern detectors so you don't have to research the canonical exploit signatures. Three are on by default; the rest opt in via builtinChecks. Allowlist matches always win — legitimate use cases have an escape hatch.

Destructive rm

Default on
destructive-rm

rm -rf /, rm -rf ~, rm -rf $HOME — but NOT rm -rf node_modules / ./build

curl | sh

Default on
curl-pipe-shell

curl ... | sh, wget ... | bash and variants — the canonical drive-by-install

Reverse shell

Default on
reverse-shell

nc -e, bash -i >& /dev/tcp/.../, python -c 'socket.connect(...)' — known exploitation signatures

sudo invocations

Opt-in
sudo

Any sudo invocation. Off by default — opt in via builtinChecks

History rewrite

Opt-in
history-rewrite

git reset --hard, git filter-branch, git push --force, reflog expire

Process substitution

Opt-in
process-substitution

<(curl ...), >(...) — supply-chain exploit pattern

Crypto miners

Opt-in
crypto-mining

Known miner binaries: xmrig, cgminer, minerd, ethminer, cpuminer

Authoring a policy

Copy this. Done.

Drop this into .deslint/policy.yml. The next shell command your agent proposes runs through it. Patterns prefixed re: are regex; everything else is a literal exact match. Deny beats allow on overlap, so “allow everything starting with pnpm, but deny pnpm publish” reads cleanly.

.deslint/policy.yml
version: 1
name: acme-corp/strict
severity: error

shellExec:
  deny:
    - "pnpm publish"
    - "re:^npm install -g"
    - "re:^git push --force"

  allow:
    - "re:^pnpm (test|run |install$|build$)"
    - "re:^git (status|diff|log|add|commit|fetch|pull)"
    - "re:^ls( |$)"
    - "pwd"

  defaultAction: deny

  builtinChecks:
    - destructive-rm
    - curl-pipe-shell
    - reverse-shell
    - history-rewrite
    - sudo

Get the firewall in your agent loop.

Install @deslint/mcp, point your agent at it, drop a .deslint/policy.yml in your repo. The next shell command your agent proposes runs through verify_shell_exec.