OpenTerms protocol integration for LangChain: permission-aware AI agents
Project description
langchain-openterms
Permission-aware LangChain agents that check a domain's openterms.json
before taking action.
Fail-closed by default. If the site has no openterms.json, or the
permission is not explicitly granted, execution is blocked. You must
opt in to permissive behavior explicitly with fail_closed=False.
Installation
pip install langchain-openterms
# With openterms-py SDK (recommended, requires >=0.3.1):
pip install "langchain-openterms[sdk]"
Canonical Permission Keys
Only these 7 keys are recognized:
| Key | Meaning |
|---|---|
read_content |
Read / display page content |
scrape_data |
Automated scraping / crawling |
api_access |
Access the domain's API |
create_account |
Create a user account |
make_purchases |
Complete a purchase |
post_content |
Post or publish content |
allow_training |
Use content for model training |
Fail-Closed Defaults
The following states block execution by default:
null/None— domain unreachable or SDK errorno_openterms_json— noopenterms.jsonfile foundnot_specified— key absent fromopenterms.jsonlow-confidence— validator confidence too lowconditional— permission has unverifiable conditionsdenied— explicit deny
Only an explicit allowed: true in openterms.json permits execution.
Quick Start: OpenTermsGuard
Wrap any LangChain tool. Execution is blocked unless permission is explicitly granted.
from langchain_community.tools import BraveSearch
from langchain_openterms import OpenTermsGuard
search = BraveSearch.from_api_key(api_key="...", search_kwargs={"count": 3})
# Fail-closed by default — blocks if no openterms.json or not explicitly allowed
guarded_search = OpenTermsGuard(
tool=search,
action="read_content",
)
result = guarded_search.invoke("https://example.com/pricing")
if "blocked" in result.lower():
# denied, not_specified, missing file, low-confidence — all blocked
print("Cannot proceed:", result)
else:
print("Allowed:", result)
The guard never silently proceeds on ambiguous results. If the check returns anything other than an explicit allow, the tool returns a block message.
Permissive Opt-In (explicit, not recommended for production)
# Only use fail_closed=False when you have a deliberate reason
permissive_guard = OpenTermsGuard(
tool=search,
action="read_content",
fail_closed=False, # pass through when no openterms.json found
)
With fail_closed=False, only an explicit denied blocks. Missing files
and unspecified permissions pass through.
Denial Callback
denied_domains = []
guard = OpenTermsGuard(
tool=search,
action="scrape_data",
on_denied=lambda domain, action, result: denied_domains.append(domain),
)
Agent Tool: OpenTermsChecker
Agents can call this tool directly to check permissions before deciding whether to proceed.
import json
from langchain_openterms import OpenTermsChecker
checker = OpenTermsChecker()
# Agent calls: "<domain> <action>"
result_json = checker.invoke("example.com scrape_data")
parsed = json.loads(result_json)
# Gate strictly on allowed=True
if parsed["check"]["allowed"] is True:
# Only here is execution safe
pass
else:
# blocked: denied, not_specified, missing file, low-confidence, conditional
print("Blocked:", parsed["check"]["reason"])
Passive Observer: OpenTermsCallbackHandler
Logs permission checks without blocking. Use this for monitoring only.
Does not enforce permissions — use OpenTermsGuard for that.
from langchain_openterms import OpenTermsCallbackHandler
handler = OpenTermsCallbackHandler(
default_action="read_content",
on_check=lambda r: print(f"{r['domain']}: allowed={r['allowed']}"),
)
agent.invoke({"input": "..."}, config={"callbacks": [handler]})
# Review after the run — these are domains where action was NOT explicitly allowed
for check in handler.checks:
if check["allowed"] is not True:
print(f"Would be blocked: {check['domain']} — {check['reason']}")
bool() Truthiness
Check results use strict truthiness: bool(result) is True only for
explicitly allowed results. Do not rely on truthiness for dict results —
always check result["allowed"] is True explicitly.
Using with openterms-py SDK
Install with SDK support for the most accurate results:
pip install "langchain-openterms[sdk]"
# Requires openterms-py>=0.3.1
The SDK applies fail-closed semantics at the check level. Empty caches, unreachable domains, and missing files all return non-allow decisions — never a permissive default.
openterms-py Dependency
- With SDK (
[sdk]extra oropenterms-py>=0.3.1installed): Uses the SDK for all permission checks. Requires openterms-py>=0.3.1 for correct fail-closed behavior. - Without SDK: Falls back to a built-in HTTP client with equivalent
fail-closed semantics. Missing files and unspecified keys return
allowed=None, which blocks underfail_closed=True(the default).
Version History
- 0.4.0 — Fail-closed by default (
fail_closed=True). Blocks null, not_specified, no_openterms_json, low-confidence, conditional, denied.fail_closed=Falseopt-in for permissive behavior. Canonical keys only. openterms-py>=0.3.1 required for SDK mode. - 0.3.1 — Initial public release.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file langchain_openterms-0.4.0.tar.gz.
File metadata
- Download URL: langchain_openterms-0.4.0.tar.gz
- Upload date:
- Size: 17.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7175cab2f83a795653b4140bef8d68325727d6f9b7d7eac95025308a360ccc1c
|
|
| MD5 |
61f43cc94d0d3f8178979d0cdebeef92
|
|
| BLAKE2b-256 |
f0ca318a227b23fb16e4800ffd77b135218c8e81c0fb8ee9746ffcbbd307491a
|
File details
Details for the file langchain_openterms-0.4.0-py3-none-any.whl.
File metadata
- Download URL: langchain_openterms-0.4.0-py3-none-any.whl
- Upload date:
- Size: 13.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6a25d17578b4ac41cf0f8875839b5224df96e71c3b9a73b4556fc918f04934fa
|
|
| MD5 |
4fd36465b361f389c462d9dc4fec71ca
|
|
| BLAKE2b-256 |
8f5b743bf3d7467891c02ad045611d2cef1dad6a5b91f9a66d006dfeb21752f9
|