Skip to main content

diplomat-gate

PyPI Python License CI

The enforcement layer for AI agents.

Deterministic. No LLM. Hash-chained audit. Works with OpenClaw, browser-use, LangChain, OpenAI Agents SDK.

Before / after: AI agent calling stripe and smtp without a guard vs. the same calls going through diplomat-gate policies

Your AI agent just emailed your insurance company. You didn't ask it to.

An AI assistant inferred the claims address from a document the user uploaded and sent a legal rebuttal — autonomously, without confirmation. Nothing in the framework stopped it.

This is what happens without a deterministic policy layer. Here is the fix:

pip install "diplomat-gate[yaml]"
# 10 lines of YAML
policies:
  - id: email.domain_blocklist
    blocked: ["*@lemonade.com", "*@*insurance*", "*@*legal*"]
    on_fail: STOP
  - id: email.rate_limit
    max: 2
    window: 1h
    on_fail: REVIEW
audit:
  enabled: true
$ python demos/openclaw/run.py --ci

SCENARIO 1 — OpenClaw agent, no diplomat-gate
  Emails sent without approval : 1
  Recipient                    : claims@lemonade.com
  🔥 Legal email sent to insurance company without user approval.

SCENARIO 2 — Same agent, behind diplomat-gate
  Verdict: STOP
    - email.domain_blocklist: Domain 'lemonade.com' is on the blocklist
  🛡  Email blocked before reaching the SMTP server.
  Emails actually sent: 0

  to: alice@example.com               Verdict: CONTINUE
  to: bob@example.com                 Verdict: REVIEW  (email.rate_limit)

SCENARIO 3 — Every verdict is hash-chained
  $ diplomat-gate audit verify
  OK: chain valid (3 record(s) checked)

No API key. No Docker. No setup. Run it yourself: python demos/openclaw/run.py


The problem

AI agents call APIs with real-world side effects — send email, charge a card, delete files, POST to a webhook — and most orchestration frameworks treat hard enforcement as the operator's responsibility. In practice that means there is none.

Running diplomat-agent scan on a typical agent codebase:

$ diplomat-agent scan ./my_agent

Scanning for tool calls with external side effects...

  email.send            12 call sites    0 / 12 have a deterministic policy check
  payment.charge         4 call sites    0 /  4 have a deterministic policy check
  files.delete           3 call sites    0 /  3 have a deterministic policy check
  webhook.post           2 call sites    0 /  2 have a deterministic policy check
  browser.navigate       7 call sites    0 /  7 have a deterministic policy check

  28 call sites with side effects found.
   0 are protected by a deterministic policy layer.

Recommended: diplomat-gate

diplomat-gate is the missing deterministic policy layer. It intercepts calls, evaluates them against a YAML policy file, and returns CONTINUE / REVIEW / STOP before execution — in ~20 µs for a single-policy evaluation, ~500 µs mean for a 5-policy set, with no LLM call, no network request.

Works with every framework

diplomat-gate is framework-agnostic. Any call that can be represented as a Python dict works out of the box. Adapters for popular SDKs are included.

Framework Integration How
OpenClaw ✓ dict API gate.evaluate({"action": ..., ...})
browser-use ✓ dict API gate.evaluate({"action": ..., ...})
LangChain ✓ built-in adapter from diplomat_gate.adapters.langchain import gated_tool
OpenAI Agents SDK ✓ built-in adapter from diplomat_gate.adapters.openai import filter_allowed
Any Python agent ✓ dict API if it calls an API, it can be gated

diplomat-gate: one enforcement layer for four agent frameworks — LangChain, OpenClaw, browser-use and OpenAI Agents SDK converging onto diplomat-gate, which emits CONTINUE, REVIEW, or STOP verdicts

Also works with Anthropic tool_use, CrewAI, AutoGen, PythonClaw, and any agent framework that exposes dict-like tool calls.

What's new in 0.4.0

  • Audit receipt format v1 — the hash-chained audit log is now a versioned specification (docs/receipt-format.md) with test vectors, and a single-file, standard-library-only verifier (tools/verify_receipts.py) lets a third party check a chain without installing this package.
  • diplomat-gate validate <gate.yaml> — validates a config without running any policy; stable JSON output for CI.
  • AI Act evidence page (draft) — docs/ai-act-evidence.md maps requirements to the evidence artifacts diplomat-gate produces, with what each does not cover. Draft, pending legal review.
  • License metadata — package metadata now declares BUSL-1.1, matching the LICENSE file (previously it declared Apache-2.0).

What's new in 0.3.0

  • Reproducible OpenClaw demo — python demos/openclaw/run.py shows the insurance email incident in under 60 seconds, no API key needed.
  • Release validation pipeline — scripts/validate_release.py runs a 15-step gate: lint → tests → benchmarks → build → install → smoke → demo.

What's new in 0.2.0

  • Hash-chained audit trail — every verdict is sealed with a SHA-256 record hash that links to its predecessor. Tampering with a historical row breaks the chain and is detected by diplomat-gate audit verify.
  • Review queue — REVIEW verdicts are auto-enqueued in a separate SQLite database. Operators approve or reject from the CLI or programmatically. Pending → approved / rejected / expired lifecycle is enforced server-side.
  • Adapters for OpenAI tool calls, Anthropic tool_use blocks, and LangChain-style tools — duck-typed, no SDK import required.
  • CLI (diplomat-gate audit verify | rebuild-chain, diplomat-gate review list | show | approve | reject).
  • Sensitive field redaction by default in audit and review storage (recipient, to, email, domain, amount, card_last4, phone).
  • 8 runnable examples under examples/, CI matrix across Python 3.10–3.13 × Linux / Windows / macOS, microbenchmarks under benchmarks/.

See the full CHANGELOG.md.

60-second setup

# gate.yaml
version: "1"

audit:
  enabled: true
  path: "./diplomat-audit.db"

review_queue:
  enabled: true
  path: "./diplomat-review.db"

payment:
  - id: payment.amount_limit
    max_amount: 10000
    on_fail: STOP

email:
  - id: email.domain_blocklist
    blocked: ["*.banque-*.fr", "*.gouv.fr"]
    on_fail: STOP
from diplomat_gate import Gate

gate = Gate.from_yaml("gate.yaml")

verdict = gate.evaluate({"action": "charge_card", "amount": 15_000})
# verdict.decision  -> Decision.STOP
# verdict.violations -> [Violation(policy_id="payment.amount_limit", ...)]
# verdict.latency_ms -> ~0.05

How it works

%%{init: {'theme':'base', 'themeVariables': {
  'primaryColor': '#FFFFFF',
  'primaryTextColor': '#0F172A',
  'primaryBorderColor': '#CBD5E1',
  'lineColor': '#64748B',
  'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
}}}%%

flowchart LR
    A["AI agent<br/><span style='color:#64748B;font-size:12px'>LangChain · OpenClaw<br/>browser-use · OpenAI SDK</span>"]
    B{{"diplomat-gate<br/><span style='color:#E0E7FF;font-size:12px'>policy evaluation</span>"}}
    C["Real-world action<br/><span style='color:#D1FAE5;font-size:12px'>Stripe · SendGrid · DB</span>"]
    D["Blocked<br/><span style='color:#FEE2E2;font-size:12px'>exception raised</span>"]
    E["Review queue<br/><span style='color:#FEF3C7;font-size:12px'>human approval</span>"]
    F[("SHA-256<br/>hash-chained<br/>audit trail")]

    A -->|"tool call"| B
    B -->|"CONTINUE"| C
    B -->|"STOP"| D
    B -->|"REVIEW"| E
    B -.->|"every verdict"| F

    style A fill:#F8FAFC,stroke:#CBD5E1,stroke-width:1.5px,color:#0F172A
    style B fill:#4F46E5,stroke:#312E81,stroke-width:2px,color:#FFFFFF
    style C fill:#10B981,stroke:#047857,stroke-width:1.5px,color:#FFFFFF
    style D fill:#EF4444,stroke:#991B1B,stroke-width:1.5px,color:#FFFFFF
    style E fill:#F59E0B,stroke:#B45309,stroke-width:1.5px,color:#FFFFFF
    style F fill:#F1F5F9,stroke:#64748B,stroke-width:1.5px,color:#334155

No LLM calls. No network requests. Pure deterministic evaluation. Each verdict produces a Receipt with a SHA-256 hash of the canonical tool call.

Decorator API

from diplomat_gate import Blocked, Gate, NeedsReview, configure, gate

configure(Gate.from_yaml("gate.yaml"))


@gate(action="charge_card")
def charge(amount: int, customer_id: str) -> dict:
    return stripe.charges.create(amount=amount, customer=customer_id)


charge(amount=500, customer_id="cus_123")  # CONTINUE -> normal return
charge(amount=50_000, customer_id="cus_123")  # STOP    -> raises Blocked
  • CONTINUE: function executes, returns its normal value.
  • STOP: raises Blocked with the full Verdict attached.
  • REVIEW: raises NeedsReview; if review_queue is enabled, the call is also persisted for an operator to approve/reject.

Validate your gate.yaml

Catch typos and misconfigured policies before deployment:

diplomat-gate validate gate.yaml
# OK: 8 policies loaded, 0 errors, 0 warnings

Use --json for CI integration:

diplomat-gate validate gate.yaml --json --output report.json --quiet
echo $?  # 0 if valid, 1 if errors, 2 if I/O error

See docs/cli.md for full flag reference and the JSON schema.

Audit trail

Every verdict is recorded in an append-only SQLite log with a SHA-256 hash chain. The chain is tamper-resistant: accidental corruption and post-hoc row edits are detected by the verifier. Note that rebuild_chain() can recreate a valid chain over tampered data — an attacker with write access to the .db file is not stopped by the chain alone. For non-repudiation, ship records to a write-once store.

%%{init: {'theme':'base', 'themeVariables': {
  'primaryColor': '#FFFFFF',
  'primaryTextColor': '#0F172A',
  'primaryBorderColor': '#CBD5E1',
  'lineColor': '#64748B',
  'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
}}}%%
flowchart LR
    V1["Verdict N-1<br/><span style='color:#64748B;font-size:11px'>hash: abc…</span>"]
    V2["Verdict N<br/><span style='color:#64748B;font-size:11px'>hash: SHA-256(canonical+prev)</span>"]
    V3["Verdict N+1<br/><span style='color:#64748B;font-size:11px'>hash: def…</span>"]
    DB[("SQLite<br/>audit.db")]

    V1 -->|"prev_hash"| V2
    V2 -->|"prev_hash"| V3
    V1 & V2 & V3 --> DB
    DB -->|"audit verify"| OK["✓ chain intact"]

    style V1 fill:#F1F5F9,stroke:#CBD5E1,color:#0F172A
    style V2 fill:#4F46E5,stroke:#312E81,color:#FFFFFF
    style V3 fill:#F1F5F9,stroke:#CBD5E1,color:#0F172A
    style DB fill:#F8FAFC,stroke:#94A3B8,color:#334155
    style OK fill:#D1FAE5,stroke:#6EE7B7,color:#065F46
diplomat-gate audit verify        --db ./diplomat-audit.db
diplomat-gate audit rebuild-chain --db ./diplomat-audit.db   # one-shot recovery

The chain is verifiable by a third party without installing this package: the format is specified in docs/receipt-format.md and tools/verify_receipts.py is a single-file, standard-library-only verifier (python verify_receipts.py <audit.db>).

Sensitive parameters in violation contexts (recipient, to, email, domain, amount, card_last4, phone) are redacted to h:<sha256-prefix> before persistence. See docs/audit-trail.md for schema, threat model, and migration from 0.1.x.

Review queue (human-in-the-loop)

A REVIEW verdict is enqueued automatically in a separate SQLite database when review_queue.enabled is true.

diplomat-gate review list    --db ./diplomat-review.db
diplomat-gate review show    --db ./diplomat-review.db --id <item_id>
diplomat-gate review approve --db ./diplomat-review.db --id <item_id> --reviewer alice
diplomat-gate review reject  --db ./diplomat-review.db --id <item_id> --reviewer alice --note "..."

See docs/review-queue.md.

Adapters

Bring-your-own LLM SDK. Adapters are duck-typed — installing the SDK is not required to use them.

from diplomat_gate.adapters.openai import filter_allowed as openai_filter
from diplomat_gate.adapters.anthropic import filter_allowed as anthropic_filter
from diplomat_gate.adapters.langchain import gated_tool

# OpenAI
allowed, review, blocked = openai_filter(gate, response.choices[0].message.tool_calls)

# Anthropic
allowed, review, blocked = anthropic_filter(gate, response.content)

# LangChain
safe_tool = gated_tool(my_langchain_tool, gate)

See docs/adapters.md.

Payment policies

Policy What it checks Config
payment.amount_limit Single transaction cap max_amount: 10000
payment.daily_limit Cumulative daily spend max_daily: 50000
payment.velocity Max transactions per window max_txn: 20, window: 1h
payment.duplicate_detection Same amount + recipient within window window: 5m
payment.recipient_blocklist Block specific recipients (glob) blocked: ["evil_*"]

Email policies

Policy What it checks Config
email.domain_blocklist Restricted recipient domains blocked: ["*.banque-*.fr"]
email.rate_limit Max emails per window max: 50, window: 1h
email.business_hours Sends outside work hours start: 9, end: 18, tz: Europe/Paris
email.content_scan Credit cards, SSNs, API keys, private keys in body patterns: [credit_card, ssn]

Every policy takes a severity (critical / high / medium / low) and an on_fail action (STOP or REVIEW).

Custom policies: see docs/writing-policies.md.

Performance

Microbenchmarks (python benchmarks/run.py, dev laptop, 5 000 iters):

Scenario mean p95 p99 ops/s
simple_allow ~18 µs ~31 µs ~41 µs 54 000
simple_block ~19 µs ~33 µs ~47 µs 52 000
multi_policy (5) ~496 µs ~958 µs ~1564 µs 2 000
with_audit_sqlite ~558 µs ~625 µs ~1925 µs 1 800

These numbers reflect measurements on a mid-range 2024 Windows laptop under typical dev load. Values vary significantly with system state (CPU load, SQLite WAL cache, temperature) — we've observed 1.5x to 4x variation on the same machine across sessions. The benchmarks are meant to show order of magnitude, not exact latencies.

Audit numbers are dominated by fsync. The p99 long tail on with_audit_sqlite reflects SQLite fsync latency spikes — expect similar behavior on shared storage. Re-run python benchmarks/run.py on your hardware before quoting publicly.

Zero mandatory dependencies

diplomat-gate ships pure-stdlib. Optional extras:

Extra Brings in Used for
[yaml] pyyaml Gate.from_yaml(...)
[rich] rich colored CLI output
[openai] openai>=1.0 optional, for typed adapter usage
[anthropic] anthropic>=0.20 optional, for typed adapter usage
[langchain] langchain-core>=0.1 optional, for typed adapter usage
[all] all of the above one-shot install
pip install diplomat-gate          # core only
pip install "diplomat-gate[yaml]"  # for YAML policy files
pip install "diplomat-gate[all]"   # everything

Examples

Eight runnable examples — each works from the repo root and from inside examples/. None of them require an SDK install.

python examples/01_basic_gate.py
python examples/02_yaml_config.py
python examples/03_decorator.py
python examples/04_audit_trail.py
python examples/05_review_queue.py
python examples/06_openai_adapter.py
python examples/07_anthropic_adapter.py
python examples/08_langchain_adapter.py

See examples/README.md.

Use with diplomat-agent

diplomat-agent scans your codebase and reports every tool call with real-world side effects. diplomat-gate protects them.

Need centralized governance?

diplomat-gate is local-first and free, and it already ships a hash-chained, third-party-verifiable audit log and a human review queue (see docs/ai-act-evidence.md — draft, pending legal review — for which requirements this evidence can feed, and which it does not cover). For teams that need a hosted control plane:

diplomat.run — immutable cross-tenant audit trail, real-time dashboard, managed approval routing, compliance export.

Requirements

  • Python 3.10+
  • Zero mandatory dependencies (stdlib only)
  • Optional extras as listed above

How this was built

diplomat-gate is solo-maintained and AI-assisted: a large share of the commits are authored by me but committed through an AI coding agent. What matters for a security tool is not who typed the lines but whether the behavior is verifiable. The credibility anchors are the ones you can check yourself: 272 tests, a 15-step release gate (scripts/validate_release.py), a green CI matrix across Python 3.10–3.13 on Linux / Windows / macOS, and runnable examples that need no API key. Run python -m pytest tests/ -v and python benchmarks/run.py and judge the tool on its output.

Limitations

diplomat-gate is a syntactic enforcement layer, not a semantic one. It is designed to be one layer in a defense-in-depth strategy, not a silver bullet.

What diplomat-gate does well:

  • Exact-match, glob, regex, and amount-based policies on tool calls
  • Deterministic verdicts you can test and reason about
  • Hash-chained audit log resistant to accidental corruption
  • Framework-agnostic integration via dict or adapters

What diplomat-gate does not do:

  • Intent classification: a hallucinated recipient that doesn't match any policy pattern will not be blocked. Design policies with the principle of least privilege (allowlist > blocklist when possible).
  • Prompt injection detection: diplomat-gate cannot read agent prompts, only its tool calls. For prompt-level threats, combine with LLM-based guardrails (e.g. Guardrails AI, NVIDIA NeMo).
  • Strong tamper-evidence: the SQLite hash chain detects accidental or post-hoc edits. It does not protect against a local attacker with write access — by design, rebuild-chain can recreate a valid chain for legitimate recovery. For audit-grade logging, ship records to a write-once external store.
  • Multi-process rate-limit accuracy: the current rate-limit and velocity policies are accurate in single-process use. For multi-process workloads, wrap evaluation with an external lock or use a distributed store.

When NOT to use diplomat-gate:

  • If you need semantic understanding of tool call intent → use an LLM judge alongside diplomat-gate.
  • If your threat model includes a privileged local attacker → diplomat-gate's audit log alone is insufficient; combine with write-once external logging.
  • If you need fully distributed rate limiting across nodes → use a Redis- or database-backed rate limiter.

diplomat-gate is intentionally narrow. Use it for what it does well, compose it with complementary tools for what it doesn't.

License

diplomat-gate is licensed under the Business Source License 1.1 (BSL 1.1). See LICENSE for the full terms.

In plain terms:

  • Free to use: you can read, modify, self-host and run diplomat-gate in production for your own internal use, including in commercial products that use it to guard your own agents.
  • Not allowed without a commercial license: offering diplomat-gate (or a substantial part of its functionality) to third parties as a hosted, managed or resold service, such as an agent-governance or audit service that competes with Diplomat.
  • Becomes open source: each version converts automatically to Apache License 2.0 four years after its release.
Licensor Diplomat Services SASU
Licensed Work diplomat-gate 0.4.0
Additional Use Grant Production use is permitted, provided you do not offer the Licensed Work to third parties as a hosted or managed service that competes with the Licensor's paid offerings.
Change Date Four years after each version's release date
Change License Apache License 2.0

Need a commercial license or have a question about your use case? Contact josselin@diplomat.run.

BSL 1.1 is a source-available license, not an OSI-approved open source license. The static scanner diplomat-agent remains fully open source under its own license.

Contributing

By submitting a contribution, you agree that it may be distributed under the terms of this license and assign the rights needed for the Licensor to relicense it. See CONTRIBUTING.md.

Metadata

Release files for diplomat-gate 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for diplomat-gate 0.4.0
File Size Uploaded
diplomat_gate-0.4.0.tar.gz 122.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for diplomat-gate 0.4.0
File Interpreter ABI Platform
diplomat_gate-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 170.5 kB

Release files / diplomat_gate-0.4.0.tar.gz

Download URL diplomat_gate-0.4.0.tar.gz
Size 122.3 kB
Tags Source
SHA-256 checksum
How to use checksums
10d371a3ee87bbf00b07c3cc4041ffc698ccf1494066340bb30afffb2c87da24
BLAKE2b-256 checksum
How to use checksums
cb0f6cc2328c46b3f67c36d47c61c4ca41df48b4b734d03aac8f8c8a8d548beb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release files / diplomat_gate-0.4.0-py3-none-any.whl

Download URL diplomat_gate-0.4.0-py3-none-any.whl
Size 48.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6fb02d035b688772a1c1b9e2599d28136725a2c9a7b5ffc02870adad6471d19b
BLAKE2b-256 checksum
How to use checksums
35f2173f459a0743f71d8385c0daaf336f169c75e48737aa4d4850f10502d076
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page