deterministic no LLM Apache-2.0

Guardrails for
AI-written code.

rein reviews the code your agents write and blocks unsafe, broken, or hallucinated calls before they run. The same verdict every time, with the reason, and a fix where one applies.

pip install rein-engine

0LLM calls
500+tests
5integrations
Apache‑2.0open source
a real agent, gated by rein
A real agent + rein guardrail (deterministic, no LLM):

[1] dangerous code
    agent tried: save_code('import os\nos.system(user_input)')
    -> BLOCKED before execution; tool body never ran
       security.os-system (high): os.system invokes a shell; use
       subprocess with a list and shell=False.

[2] clean code
    agent tried: save_code('def add(a, b): return a + b')
    -> allowed; tool ran

Same verdict every run, with no model in the review loop.

The problem

Agents write real code now.

Autonomous coding agents write files and run commands on their own. The failure modes are specific and dangerous: a hallucinated import that does not exist, a hard-coded secret, an os.system call on user input, code that will not even parse. Letting a second LLM review it is non-deterministic, it passes once and fails the next run, and unauditable, you cannot see why. rein is the deterministic layer that sits at the boundary and decides the same way, every time.

How it works

One hook, gated at the boundary.

1

The agent proposes

It is about to write a file or run a command. rein hooks that moment, before anything executes.

2

rein reviews

Plain functions parse and check the code: security, secrets, hallucinated imports, undefined names. No LLM.

3

Block or allow

At or above your threshold, the call is cancelled with the reason and a fix. Otherwise it runs untouched.

Coverage

What it catches.

Unsafe code

os.system, eval, exec, pickle, subprocess shell=True, weak hashes, by AST, not guesswork.

Hard-coded secrets

API keys and tokens committed into source, caught before they ship.

Hallucinated imports

An import that resolves to no stdlib, dependency, or project module, the classic agent failure a linter misses.

Undefined names

A name used but bound nowhere reachable: a NameError caught before it runs.

Best-of-breed scanners

Opt into bandit, gitleaks, ruff, and semgrep, run over the agent's code before it touches disk.

A reason and a fix

Findings come with why they fired and, where one applies, how to remedy it, not just an alert.

Explainability

Every verdict, explained.

No black box. Each finding names the rule, the severity, why it fired, and how to fix it where there is a known remedy, the same output whether a person, CI, or an agent triggers it.

HIGHsecurity.os-systemblocked

os.system invokes a shell; whoever controls the argument controls the machine.

Fixsubprocess.run(["ls", "-l"], shell=False)

Severity → action

infolowmedhighcrit

Blocks at HIGH by default. Configurable per project, lower it to also gate MEDIUM (weak hashes, hallucinated imports).

Why deterministic

Not a second opinion.

LLM reviewer

  • Non-deterministic: passes once, fails the next run
  • Costs tokens on every check
  • Can be prompt-injected by the very code it reviews
  • Cannot explain its verdict

rein

  • Deterministic: identical verdict every run
  • No tokens, no model, no network round-trip
  • Plain functions over an AST, nothing to manipulate
  • Every finding has a reason and a fix

Who it's for

Built for teams shipping agents.

Agent builders

Building on Claude Code, OpenHands, or Strands? Gate every tool call deterministically, before it runs.

Platform & devtools teams

Shipping an agent product? Drop in a guardrail your users can trust: no model cost, fully auditable.

Security-minded orgs

Adopting AI codegen? Keep unsafe, secret-leaking, and hallucinated code out of your repos, the same way every time.

Quickstart

Three ways to use it.

from rein_strands import ReinToolGuard

agent = Agent(model=model, tools=tools,
              hooks=[ReinToolGuard()])

Works with Strands and OpenHands. Blocks HIGH+ before the tool runs.

# .github/workflows/rein.yml
- uses: SametAtas/rein@v0.3.0
  with:
    path: src/

Reviews every pull request and emits SARIF.

pip install rein-engine
rein review .

Or wire it as a pre-commit hook for local gating.

Integrations

Plugs in where your agents run.

Strands OpenHands GitHub Action MCP server pre-commit

FAQ

Questions.

Does rein use an LLM?

No. Detection is plain deterministic functions over the code's AST plus regex for secrets. Same input, same verdict, every time.

Is it slow?

The native checks run locally, a few milliseconds per file, with no network round-trip. The external scanners (bandit, semgrep, and so on) are opt-in, since they cost real time per call.

What languages?

Python today for the code analysis; secret detection works on any text. More languages are on the roadmap.

Will it cry wolf on working code?

Precision is the point. On large real codebases (requests, Flask, click, and rich), rein's import- and name-resolution checks produced zero false positives. It biases toward false negatives over false positives, so a real, working tool call is not blocked.

What does deep mode add?

It runs bandit, gitleaks, ruff, or semgrep over the agent's code before it executes, folding their findings into the same verdict. Opt-in per scanner.

Is it open source?

Yes, Apache-2.0. The engine is on PyPI as rein-engine, with adapters for the major agent frameworks.

Stop it before the agent runs.

An LLM reviewer guesses, and forgets. rein decides the same way, every time.

pip install rein-engine