Overview

KEEL is a vendor-neutral runtime trust layer for AI agents. It sits between an agent and the real world: every proposed action is verified before it executes, decided as ALLOW / BLOCK / ESCALATE, and recorded as a signed, tamper-proof certificate.

The core principle: the model proposes; it never adjudicates its own trustworthiness. Deterministic checks and calibrated evidence decide — an optional LLM reviewer may only lower a decision, never raise it. The system fails closed.

KEEL is universal — any framework (LangChain, OpenAI SDK, CrewAI, Claude Code, Cursor, custom), any language, any field. One policy and one audit log across every agent you run.

Install

KEEL is a self-contained Python package — no database or external services required to start.

pip install keel-trust     # installs as keel-trust, imports as `keel`

keel serve            # starts gateway + console + docs on :8347
keel serve --port 9000 --sandbox   # with the demo worlds
keel version

Open http://localhost:8347 for the site, /app for the operator console, and /docs for these docs. Data is stored under ~/.keel/data by default (override with KEEL_DATA_DIR).

Quickstart

1 · Get your API key

Sign in at /app and open Account (last item in the left-hand nav). Copy the key shown at the top and put it in your environment — the SDK and every REST example below read it from there.

export KEEL_API_KEY=keel_ak_...       # from /app → Account
Treat the key like a password: it authenticates as your account. Keep it in an environment variable or a secret manager, never in source control. You can rotate it from the same screen — every agent using the old key stops working until you update it.

2 · Point the SDK at your deployment

Use the URL you signed in to. If you run keel serve yourself, that is http://localhost:8347 instead.

export KEEL_URL=https://keel.best     # or http://localhost:8347 when self-hosting

3 · Put an agent behind the gateway

Three steps: register once, check before acting, report the outcome. With the Python SDK a decorator does all three.

import os
from keel.sdk import KeelGuard, GuardRejected

# api_key defaults to $KEEL_API_KEY; pass api_key="..." to override
guard = KeelGuard(os.environ["KEEL_URL"], agent_id="support-bot")
guard.register(name="Support Bot", action_classes={
    "send_reply":   {"risk": "low"},
    "issue_refund": {"risk": "high", "budget_per_day": 500,
                     "requires_evidence": True}})

@guard.protect("issue_refund", cost=lambda amount, **kw: amount)
def issue_refund(customer_id, amount):
    ...   # only runs on ALLOW; the wrapper reports success/failure automatically

try:
    issue_refund("cus_123", 49.0)
except GuardRejected as e:
    print("KEEL blocked it:", e.decision["reasons"])
New agents default to shadow mode: KEEL signs and records every decision and flags what it would have blocked, but blocks nothing except tripwires. Set shadow_mode=False at registration to enforce.

Core concepts

TermMeaning
agentAny AI system that takes actions. Registered once with the action classes it may attempt.
action classA named capability (send_email, run_command) with a risk level, optional budget, rate limit, and evidence requirement.
risklow / medium / high / critical — sets the success floor and human-oversight requirement.
tripwireAn irreversible-catastrophe pattern (DB drop, funds transfer, recursive delete…) hard-blocked in every mode.
certificateThe signed, logged record of a single decision.
outcomeWhat actually happened after an allowed action. Feeds calibration. Only externally-verified outcomes earn autonomy.

Decisions & verdicts

Every check returns one decision:

DecisionMeaning
ALLOWAll checks pass and the agent has earned the required trust — proceed.
BLOCKA tripwire, policy rule, or grounding check failed — refused.
ESCALATEPlausible but not yet trusted (cold start, risky, or novel) — sent to the human approval queue.
SHADOWAgent is in shadow mode — recorded and signed, but not enforced (except tripwires).

Checks run cheapest-and-most-fatal first: tripwires → schema → policy/budget → citation integrity → consistency → destructive-intent → calibrated confidence. The deterministic path completes in roughly two milliseconds.

Earned autonomy

Autonomy is earned per (agent, action class) from verified outcomes — never granted by configuration. KEEL computes an anytime-valid lower bound on the success rate (a betting confidence sequence, valid under continuous monitoring), audits it for drift with a Page-Hinkley detector, and holds an expected-harm ceiling with conformal risk control that self-tightens the required floor when allowed actions cause harm.

TierBehavior
T1Certify & recommend — a human executes / approves.
T2Auto-execute, notify — earned from ≥10 externally-verified successes at a calibrated floor ≥0.80.
T3Auto-execute, silent — earned from ≥30 verified successes at floor ≥0.95, zero harm.
Trust-farming is closed: a clean record on low-risk actions never unlocks high-risk classes, and an agent's own success reports never promote it — only outcomes verified by an external signal (webhook, human review, reconciliation) count.

Certificates & ledger

Every decision is an Ed25519-signed certificate appended to a Merkle transparency log. Altering any historical certificate breaks the chain — which is what makes a KEEL record admissible in a postmortem, an audit, or a regulatory review. Fetch and verify any certificate, with its inclusion proof, via the API or the console's Ledger view.

The open certificate schema is published at /api/schema/certificate.

Verify a certificate offline — without trusting us

This is what makes a KEEL certificate different from a log line: anyone holding the JSON and the authority's public key can check it, with no server, no account, and no network. If our infrastructure disappeared tomorrow, every certificate ever issued would still verify.

# save any certificate (bare, API response, or audit-pack sample)
curl -s "$KEEL_URL/api/certificates/keel:cert:XXXX?domain=gateway" \
  -H "Authorization: Bearer $KEEL_API_KEY" > cert.json

# verify it anywhere — signature, payload hash, and Merkle inclusion
keel verify cert.json --key <authority_public_key_hex>
#   ✓ signature
#   ✓ leaf_matches_payload
#   ✓ inclusion_proof
#   result      : VALID

Change one field of the certificate and the signature fails; swap in another entry's inclusion proof and the leaf check fails. Pass --root to pin the log root you saw earlier, and --json for a machine-readable report. The verifier is deliberately dependency-free (keel/cert/verifier.py) so it can be reviewed and vendored by a third party.

Agent passports — trust that travels

Cold start is where every trust system fails: an agent with thousands of verified outcomes behind one deployment starts from zero everywhere else. A passport is the agent's earned record — per action class: n, successes, harms, externally-verified count — signed by the issuing deployment's authority and verifiable offline like any KEEL artifact.

# issuing side: export the agent's earned record
curl -s -X POST "$KEEL_URL/api/gateway/agents/support-bot/passport" \
  -H "Authorization: Bearer $KEEL_API_KEY" > passport.json

# receiving side: due diligence, then adopt with the issuer's key
keel passport verify passport.json --key <issuer_public_key_hex>
curl -s -X POST "$KEEL_URL/api/gateway/passport/import" \
  -H "Authorization: Bearer $KEEL_API_KEY" -H 'Content-Type: application/json' \
  -d "{\"passport\": $(cat passport.json), \"issuer_key\": \"<hex>\"}"

The statistical rules are the point, and they are deliberately conservative:

  • Imported evidence is discounted 50% and capped, then held to the same Clopper-Pearson bound as local evidence — a foreign record pays a transfer price.
  • It can bridge cold start for medium-risk actions only. High and critical actions require locally earned evidence, always.
  • Tiers are never imported, a record containing any harm confers no benefit, and a passport can never vouch for itself — adoption requires the issuer's key obtained out-of-band.

Counterfactual policy replay — change policy with proof

Before you tighten or loosen anything, ask what the change would have done to the decisions your agents actually faced. Replay re-runs your recorded decision evidence under a candidate policy — no side effects, nothing stored — and returns the diff.

curl -s -X POST "$KEEL_URL/api/gateway/replay" \
  -H "Authorization: Bearer $KEEL_API_KEY" -H 'Content-Type: application/json' \
  -d '{"risk_overrides": {"issue_refund": "high"}, "floor_delta": 0.05}'
# → {"replayed": 412, "transitions": {"ALLOW→ESCALATE": 17, ...},
#    "summary": {"would_tighten": 17, "would_loosen": 0}, "flips": [...]}

Knobs: risk_overrides (reclassify an action class), floor_delta (raise every success-floor), tier_req (change the tier required per risk), enforce_shadow (preview what shadow-mode exit would do). Unknown knobs are an error, never a silent no-op. Every report states its limits: replay is evidence-preserving, not behaviour-preserving, and the adaptive advisory layers only ever tighten — so its ALLOW counts are an upper bound.

Signed log checkpoints — hold us to our log

GET /api/gateway/checkpoint is public and returns a signed (size, root, time) statement of the transparency log. Fetch and keep them — no account needed. Two authority-signed checkpoints of the same size with different roots are non-repudiable proof the log was rewritten; a shrinking size is proof entries were dropped.

curl -s $KEEL_URL/api/gateway/checkpoint > monday.json
curl -s $KEEL_URL/api/gateway/checkpoint > friday.json
keel checkpoint compare monday.json friday.json --key <authority_key_hex>
# verdict: APPEND-CONSISTENT · FORK-PROOF · TRUNCATION-PROOF

The comparison never over-claims: growth is reported as consistent with honest append, not proof of it.

Python SDK

keel.sdk.KeelGuard(base_url, agent_id, wait_for_approval_s=0)

MethodPurpose
register(name, action_classes, framework, shadow_mode) Declare the agent and its capabilities (once, at startup).
check(action_class, intent, payload, targets, claims, evidence, cost, …) Ask before acting. Returns the decision dict.
outcome(request_id, success, harm, detail) Report what happened after an allowed action.
@protect(action_class, cost=…, targets=…) Decorator: wraps a function so it runs only on ALLOW and auto-reports the outcome.
export_passport() This agent's earned record, signed for another deployment.
import_passport(passport, issuer_key) Adopt a foreign record as a discounted prior (key obtained out-of-band).
replay_policy(risk_overrides=…, floor_delta=…) Diff a candidate policy against recorded decisions before shipping it.

REST API

Any language can integrate over HTTP. Core endpoints:

EndpointPurpose
POST/api/gateway/agents Register an agent and its action classes.
POST/api/gateway/check Decide an action. Returns {decision, request_id, reasons, cert_id, confidence}.
POST/api/gateway/outcome Report an outcome for calibration.
GET/api/gateway/decisions Recent signed decisions.
GET/api/gateway/approvals Pending human-approval queue.
POST/api/gateway/approvals/{id} Approve or deny an escalation (records approver identity).
GET/api/gateway/audit-pack Generate the self-verifying compliance evidence bundle.
POST/api/webhook/alertmanager Ingest Prometheus Alertmanager events (ops vertical).

Every /api endpoint requires your account key, with three deliberate exceptions that exist to be read by strangers: the sign-in routes, the open certificate schema (/api/schema/certificate), and the signed log checkpoint (/api/gateway/checkpoint). Without the Authorization header the server returns 401.

curl -X POST "$KEEL_URL/api/gateway/check" \
  -H "Authorization: Bearer $KEEL_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"agent_id":"support-bot","action_class":"issue_refund",
       "intent":"refund duplicate charge","payload":{"amount":49},
       "cost":49,"claims":[{"statement":"charged 49 twice",
       "evidence_refs":["e1"]}],"evidence":[{"ref":"e1",
       "content":"charges: 49 at 09:14, 49 at 09:14"}]}'

MCP proxy (non-bypassable)

For MCP-based agents (Claude Code, Cursor), deploy KEEL as a proxy that holds the real tool servers. The agent connects to KEEL instead of the tools, so it physically cannot act without a decision. Action classes are auto-derived from the tools' schemas — zero integration code.

keel guard proxy.json

# proxy.json
{"agent_id": "claude-code",
 "shadow_mode": true,
 "risk_overrides": {"run_command": "high", "write_file": "medium"},
 "servers": [{"name": "fs", "command": "npx",
              "args": ["-y", "@modelcontextprotocol/server-filesystem", "/work"]}]}

Attach it to Claude Code with:

claude mcp add keel-guarded -- keel guard /path/to/proxy.json

The console

The operator console at /app is the daily surface: registered agents with their earned tiers, the live decision feed, the human approval queue, the transparency ledger with inclusion proofs, and one-click evidence-pack export. Everything it shows comes through the same API — nothing is hidden from integrators.

Deployment

KEEL runs as a single process for evaluation and scales out for production.

# container
docker compose up                    # KEEL alone
docker compose --profile full up     # + Qdrant + tracing

# kubernetes
helm install keel deploy/helm/keel

Optional integrations activate by environment variable: KEEL_LLM_MODEL (LiteLLM routing for the optional reviewer), QDRANT_URL (vector retrieval), OTEL_EXPORTER_OTLP_ENDPOINT (OpenTelemetry tracing). None are required.

Accounts & login

In production (KEEL_AUTH_REQUIRED=1) the console requires an account. Each account is fully isolated: its agents, decisions, calibration, integration settings and evidence packs are its own, and are never visible to another account.

  • First visit to /appCreate your account; then sign in.
  • Passwords are scrypt-hashed; sessions are signed, httpOnly cookies.
  • Each account gets an API key (shown in the console). Agents authenticate to the gateway with Authorization: Bearer keel_ak_… so their actions and billing attribute to that account.

For local self-host you can leave auth off; a single default account is used so nothing needs configuring to try. See DEPLOY.md.

Security

  • Fails closed. Unknown agents, undeclared action classes, and internal errors all deny.
  • Signing keys should live in an HSM/KMS outside the agent's reach in production (Team & Enterprise tiers).
  • Evidence is untrusted input — grounded status never raises the trust of an action touching credentials, funds, or data egress; instruction-shaped evidence is flagged.
  • Tamper-evidence: the Merkle log detects any alteration of historical records; the audit pack re-verifies itself from the root.

Compliance

The evidence pack (/api/gateway/audit-pack) maps signed decisions to the controls auditors and insurers sample:

FrameworkWhat KEEL provides
EU AI Act Art 12Per-decision signed, timestamped, tamper-evident logs.
EU AI Act Art 14Risk-tiered human oversight with recorded approver identity.
ISO/IEC 42001Uniform-random sampled decisions with verifiable signatures.
NIST AI RMFMeasured calibration and drift; autonomy from outcomes, not config.
Honest limits, stated on every certificate: statistical bounds are marginal per-bucket (never a per-decision probability), void under detected drift, and citation integrity verifies traceability — not that the evidence is true.

Plans

KEEL is free. Every capability is included for every account — self-hosted or on the managed service. There is no paid tier, no licence key, no quota to buy, and no payment is ever taken.

CapabilityIncluded
Gateway, SDK, tripwires, calibration
Signed transparency log
Evidence pack — full export & scheduling
Slack / ticketing approval routing
Hardened signing-key custody
SSO, RBAC, private deployment
WORM retention & SCITT export

Check what your account can do at any time:

curl -s https://keel.best/api/entitlement \
  -H "Authorization: Bearer $KEEL_API_KEY"
# → {"plan":"free","features":[...],"valid":true,"expires_at":null}
An audit evidence pack is scoped to your own account: it contains your certificates, your calibration tables and your approval records, never another tenant's.

Verified benchmarks

Every performance claim on this site is measured, not asserted. Reproduce them with examples/assess_gateway.py against a running server.

ClaimMeasured
Deterministic decision latencyp50 ≈ 2.4 ms · p99 < 6 ms
Catastrophes caught (labeled set, cold-start & at earned trust)100% (0 missed)
Legitimate work permanently blocked0 false blocks
False positives on common dev commands0 / 15
Transparency ledger integritychain-consistent, signatures verify
Test suite71 passing (auth, isolation, security, RL, stats)
The figures cited on the home page (Gartner cancellation forecast, MIT pilot-failure rate, the Salesloft/Drift breach scope) are published third-party research, not KEEL measurements — they describe the problem, not the product.

FAQ

Is this just another approve/deny prompt?

No. A permission prompt asks a human in the moment and leaves no record. KEEL blocks catastrophes without asking (so no one can be tricked into approving them), earns autonomy statistically so routine work stops prompting, governs every agent under one policy and one signed log, and produces the audit evidence a prompt cannot.

Does it work with my framework / language?

Yes — any framework via the SDK or REST, and MCP-based agents (Claude Code, Cursor) via the non-bypassable proxy. The guarantees never depend on the model or framework.

What if the LLM (Gemini) is down or unset?

The deterministic core runs with no LLM at all. The optional reviewer only ever lowers a decision; if it's unavailable, decisions proceed on the deterministic and statistical layers.

What are the honest limits?

Statistical bounds are marginal per-bucket, never a per-decision probability; they are void under detected drift (KEEL widens or abstains); citation integrity verifies that a claim traces to its evidence, not that the evidence is true. Every certificate states its own scope.

How much does it cost?

Nothing. Every capability is included for every account, self-hosted or on the managed service — there is no paid tier, no licence key, and no payment is ever taken. See Plans.

Questions? Open the console or read the product overview.