abe
README.md
# Abe
<p align="left"><img src="https://raw.githubusercontent.com/flowinfosystems-index/abe/main/docs/abe-mascot.png" width="160" alt="Abe, a friendly traffic light giving a thumbs up"></p>
### Act. Block. Escalate.
**Abe is a free, open-source guardrail that checks every AI agent action before it happens.**
Your agent is about to pay an invoice, buy servers, delete a table or email a customer. Abe checks the action
against your rules in under a millisecond, on your machine, and answers **`ACT`**, **`BLOCK`** or **`ESCALATE`**,
with a tamper-evident record of why.
<a href="https://www.youtube.com/watch?v=ctE8Kz9a6W0"><img src="https://img.youtube.com/vi/ctE8Kz9a6W0/hqdefault.jpg" width="480" alt="Watch the demo: an AI agent tries to spend $12,500 twice"></a>
▶ **[Watch the 40-second demo](https://www.youtube.com/watch?v=ctE8Kz9a6W0)**: an AI agent tries to spend $12,500 twice. Rules
decide the easy calls; Flow catches the duplicate and approves the real renewal. ([Run it yourself](examples/demo/))
## Free. Really.
Apache-2.0. **No account, no API key, no network, no model, no usage limits.** Every check runs on your machine,
at no cost, forever.
## Why teams put Abe in front of their agents
- **Stops expensive mistakes**: duplicate payments, over-budget purchases, force-pushes to `main`, `DROP TABLE` in production.
- **Same answer every time**: plain YAML rules you can read, test, and replay against past decisions before going live.
- **Proof for auditors**: every decision gets a hash-verified record (optionally Ed25519-signed) showing which rule fired and why.
- **Works with any agent**: Python, TypeScript, a local HTTP sidecar, Docker, or MCP (Claude, Cursor and other MCP clients).
- **No-risk rollout**: shadow mode runs beside your current approval process and shows where Abe would agree.
- **Fails closed**: bad input or a broken rule returns `ESCALATE`, never `ACT`.
## Pay only for the close calls (optional)
When rules can't decide, Abe returns `ESCALATE`. By default that goes to a person. Add `--resolver flow` and only
that action is sent to [Flow](https://fjp.flowinfo.co/abe#escalate) (redacted), which returns a decision, a reason, and what
would prove it wrong, for about $0.03 to $0.05 each. `ACT` and `BLOCK` never leave your machine. [Details below](#when-rules-arent-enough-flow).
## Quick start
```bash
pip install abe-ai # or: npm install abe-ai
abe init # writes abe-policy.yaml + request.json
abe check request.json
```
```text
Decision: ESCALATE
Reason: FINANCIAL_THRESHOLD_EXCEEDED
Rules: purchase_review_limit
Risk: MEDIUM
Record: jgr_01M3Q4...
```
## Python
```python
from abe import Abe
abe = Abe("abe-policy.yaml")
result = abe.check(
action={"type": "wire_transfer", "amount": 50000},
context={"agent_id": "finance-agent"},
)
if result.decision == "ACT":
... # execute exactly as evaluated
elif result.decision == "BLOCK":
... # do not execute; tell the user result.reason_code
else: # ESCALATE
... # hold; ask a human, or let a resolver decide
result.record.to_dict() # the Judgment-Grounded Record (store it, ship it to your SIEM)
```
## TypeScript
```typescript
import { Abe } from "abe-ai";
const abe = new Abe({ policy: "./abe-policy.yaml" });
const result = await abe.check({
action: { type: "purchase", amount: 12500, currency: "USD" },
context: { agent_id: "procurement-agent", principal_id: "user_123" },
});
console.log(result.decision); // "ESCALATE"
```
## A policy
```yaml
version: "0.1"
defaults: { unmatched: ESCALATE } # nothing matched -> needs judgment (fail closed)
rules:
- id: purchase_hard_limit
when: { all: [ { field: action.type, op: eq, value: purchase }, { field: action.amount, op: gt, value: 100000 } ] }
decision: BLOCK
reason_code: HARD_POLICY_VIOLATION
- id: purchase_review_limit
when: { all: [ { field: action.type, op: eq, value: purchase }, { field: action.amount, op: gt, value: 10000 } ] }
decision: ESCALATE
reason_code: FINANCIAL_THRESHOLD_EXCEEDED
- id: routine_purchase
when: { all: [ { field: action.type, op: eq, value: purchase }, { field: action.amount, op: lte, value: 500 } ] }
decision: ACT
judgment_required:
- action.type: terminate_employee
```
Also: authorization per agent, required evidence, risk and irreversibility thresholds, confidence minimums.
`BLOCK` beats `ESCALATE` beats `ACT`. Bad input or a broken rule returns `ESCALATE` / `EVALUATION_FAILURE`, never `ACT`.
Full reference: [`SPEC.md`](SPEC.md).
## Test before you enforce
**Replay** saved requests through a policy before it touches real money. Label each case with what your people
decided and Abe reports where it disagrees; compare against the live policy to see exactly which decisions a change moves.
```bash
# cases.jsonl: one request per line, or {"id": "inv-1042", "request": {...}, "expected": "BLOCK"}
abe replay cases.jsonl --policy new-policy.yaml --baseline abe-policy.yaml
# Replayed 1204 case(s) against ... ACT 1088 BLOCK 31 ESCALATE 85
# Changed vs baseline: 3 inv-1042: ACT -> ESCALATE [purchase_review_limit]
# Agreement with expected: 1198/1204 (99.5%)
abe replay cases.jsonl --policy new-policy.yaml --baseline abe-policy.yaml --fail-on-change # CI: exit 1 if anything moved
```
**Shadow mode** runs Abe beside your current approval process. Decisions, results and exit codes are exactly what
enforce mode would return, and every record is marked `"mode": "shadow"`. Keep approving the way you do today,
report what your people decided, and each record's falsifier shows whether they agreed.
```python
from abe.stores import SQLiteStore
abe = Abe("abe-policy.yaml", mode="shadow", store=SQLiteStore("abe.db")) # or: abe serve --shadow · ABE_MODE=shadow
r = abe.check(action={"type": "purchase", "amount": 48000}, context={"agent_id": "ap-agent"})
# ... your existing approval happens ...
abe.record_outcome(r.records[0], "approved") # people approved; if Abe said BLOCK, the falsifier triggers
```
When the agreement rate is where you want it, drop `mode="shadow"` and Abe enforces.
## Every way to run it
| | |
|---|---|
| Library | `pip install abe-ai` · `npm install abe-ai` |
| CLI | `abe check · replay · validate-policy · validate-record · conformance · keygen` |
| Local HTTP sidecar | `abe serve --policy abe-policy.yaml` → `POST http://127.0.0.1:8787/v1/check` (any language) |
| Docker sidecar | `docker run -e ABE_TOKEN=… -v $PWD:/policy:ro -p 127.0.0.1:8787:8787 ghcr.io/flowinfosystems-index/abe` |
| MCP server | `pip install "abe-ai[mcp]"` → `abe mcp --policy /abs/abe-policy.yaml` (tool: `fjp_check_action`) |
| + Flow on escalations | add `--resolver flow` (or `ABE_RESOLVER=flow`) with `FLOW_API_KEY` to `check`, `serve`, `mcp` or the Docker image |
## Records you can audit
Every call returns a record with the decision, every matched rule, the **exact policy hash**, the **request hash**,
and a SHA-256 `record_hash` (optionally Ed25519-signed with `abe keygen`). Records never change: outcomes and
resolutions are appended as linked records. Each one is a valid FJP-CONF v0.1 Judgment-Grounded Record.
```bash
abe check request.json --record-out jgr.json --sign-key abe-signing-key.pem
abe validate-record jgr.json --public-key abe-signing-key.pub.pem
```
## When rules aren't enough: Flow
Abe is deliberately useful without Flow. When it returns `ESCALATE`, you can hand that one action to
[Flow's judgment service](https://fjp.flowinfo.co) and get a verb, a reason and a falsifiable record back:
```python
from abe import Abe
from abe_flow import FlowResolver # pip install abe-flow
abe = Abe("abe-policy.yaml", resolver=FlowResolver(api_key=os.environ["FLOW_API_KEY"]))
```
No Python? The CLI, sidecar and MCP server take the same resolver (`pip install "abe-ai[mcp,flow]"`):
```json
{"mcpServers": {"abe": {"command": "abe",
"args": ["mcp", "--policy", "/abs/abe-policy.yaml", "--resolver", "flow"],
"env": {"FLOW_API_KEY": "fjp_live_..."}}}}
```
`abe serve --resolver flow` and `docker run -e ABE_RESOLVER=flow -e FLOW_API_KEY=… …` work the same way. If Flow
is out of credits, the MCP result says so and links to the refill page; the action stays `ESCALATE`.
`ACT` and `BLOCK` never leave your machine. Only escalations are sent (redacted), and if Flow is unreachable the
answer simply stays `ESCALATE`. Human-approval escalations are never auto-resolved.
## Conformance
```bash
abe conformance --bench # FJP-CONF v0.1 Gate profile, Level 3 — 160 checks, offline
```
## Repository
```
SPEC.md the normative specification
schemas/ JSON Schemas (request, response, record, policy)
python/ abe-ai (PyPI): core, CLI, HTTP, MCP, stores, signing, conformance
typescript/ abe-ai (npm): same API, same records, same hashes
flow-resolver/ abe-flow (PyPI): Gate → Flow for ESCALATE only
conformance/fixtures/ golden decisions + canonical-JSON vectors shared by every SDK
examples/ the $12,500 demo, purchasing, software agent, physical agent, travel (+ Judd), Flow
```
Apache-2.0. FJP™, Abe™ (Abe) and FJP-CONF™ are trademarks of Flow Information Systems — see [`TRADEMARKS.md`](TRADEMARKS.md).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues