Trust enforcement for LangChain and LangGraph agents.
LangChain Guard is the CapiscIO trust enforcement adapter for LangChain and LangGraph. It verifies caller trust badges, enforces security policies, and emits audit events — all composable via LangChain's LCEL pipe (|) operator with zero configuration.
pip install langchain-capiscioTurn any LangChain agent into a trust-verified agent in 2 lines:
from langchain_capiscio import CapiscioGuard
# Zero-config — reads CAPISCIO_API_KEY from env, connects to registry
secured = CapiscioGuard() | my_chain
result = secured.invoke({"input": "Summarize this ticket"})CapiscioGuard reads your environment, connects to the CapiscIO registry on first use, and verifies caller trust badges before every invocation.
LangChain agents orchestrate powerful tools — search, databases, code execution. But LangChain itself doesn't define how to:
- Authenticate which agent is calling your chain
- Authorize whether that agent meets your trust requirements
- Audit what happened for post-incident review
LangChain Guard solves this with:
| Feature | Description |
|---|---|
CapiscioGuard |
Runnable[dict, dict] — verifies trust badges before downstream execution. Composable with |. |
CapiscioTool |
Wraps a LangChain Tool with trust enforcement at the tool-call boundary. |
CapiscioCallbackHandler |
Audit trail — emits chain/tool lifecycle events to the CapiscIO EventEmitter. |
@capiscio_guard |
Decorator for LangGraph function-based nodes. |
verify_badge / resolve_agent_card |
Convenience @tool-decorated functions for agent-driven trust checks. |
Control enforcement behavior per guard instance:
guard = CapiscioGuard(mode="block") # Fail closed (production default)
guard = CapiscioGuard(mode="monitor") # Warn but continue
guard = CapiscioGuard(mode="log") # Log onlyCapiscioGuard is a LangChain Runnable — compose it with any chain or agent via the | operator:
from langchain_capiscio import CapiscioGuard
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(llm, tools)
secured = CapiscioGuard(mode="log") | agent
result = secured.invoke({"input": "What's 42 * 17?"})Emit structured audit events (task lifecycle, tool calls) to the CapiscIO dashboard:
from langchain_capiscio import CapiscioCallbackHandler
handler = CapiscioCallbackHandler(emitter=my_event_emitter)
result = chain.invoke(
{"input": "..."},
config={"callbacks": [handler]},
)Events emitted: task_started, task_completed, task_failed, tool_call, tool_result.
from langchain_capiscio import CapiscioGuard, capiscio_guard
# Option 1: Runnable as graph node
graph.add_node("verify", CapiscioGuard())
# Option 2: Decorator
@capiscio_guard(mode="block")
def call_agent(state: dict) -> dict:
...Set environment variables and create a guard with no arguments:
export CAPISCIO_API_KEY="cap_..."
export CAPISCIO_SERVER_URL="https://dev.registry.capisc.io" # optional
export CAPISCIO_AGENT_NAME="my-agent" # optional
export CAPISCIO_DEV_MODE="true" # optionalguard = CapiscioGuard() # reads env vars, connects on first invoke()guard = CapiscioGuard(
mode="block",
api_key="cap_...",
name="my-agent",
server_url="https://dev.registry.capisc.io",
)Pass extra keyword arguments through to CapiscIO.connect():
guard = CapiscioGuard(
mode="log",
connect_kwargs={
"dev_mode": True,
"keys_dir": "capiscio_keys/",
"agent_card": my_card_dict,
},
)CapiscioGuard.from_env() mirrors the CapiscIO.from_env() / MCPServerIdentity.from_env() pattern used across CapiscIO packages:
guard = CapiscioGuard.from_env(mode="log")| Variable | Required | Description | Default |
|---|---|---|---|
CAPISCIO_API_KEY |
Yes* | Registry API key | — |
CAPISCIO_SERVER_URL |
No | Registry URL override | https://registry.capisc.io |
CAPISCIO_AGENT_NAME |
No | Agent name for registration | — |
CAPISCIO_DEV_MODE |
No | Enable dev mode (true/1/yes) |
false |
CAPISCIO_AGENT_PRIVATE_KEY_JWK |
No | JSON-encoded Ed25519 private JWK for ephemeral environments | — |
*Required if not passed explicitly via constructor.
Priority: explicit constructor args > connect_kwargs > env vars > SDK defaults.
In ephemeral environments (Docker, Lambda, Cloud Run) the local ~/.capiscio/keys/ directory doesn't survive restarts. Without a persisted key, the SDK generates a new keypair on every start, which means a new DID and invalidated badges.
On first run the SDK generates a keypair and logs a capture hint:
╔══════════════════════════════════════════════════════════════════╗
║ New agent identity generated — save key for persistence ║
╚══════════════════════════════════════════════════════════════════╝
Add to your secrets manager / .env:
CAPISCIO_AGENT_PRIVATE_KEY_JWK='{"kty":"OKP","crv":"Ed25519","d":"...","x":"...","kid":"did:key:z6Mk..."}'
Copy that value into your secrets manager and set it as an environment variable. On subsequent starts the SDK recovers the same DID without generating a new identity.
Key resolution priority: env var → local file → generate new.
# docker-compose.yml
services:
langchain-agent:
image: my-langchain-agent
environment:
CAPISCIO_API_KEY: ${CAPISCIO_API_KEY}
CAPISCIO_AGENT_PRIVATE_KEY_JWK: ${AGENT_KEY_JWK} # from secrets manager
CAPISCIO_DEV_MODE: "false"# No code changes needed — CapiscioGuard reads env vars automatically
secured = CapiscioGuard(mode="block") | my_agentWarning: Never bake private keys into container images. Inject them at runtime via environment variables or mounted secrets.
See the Ephemeral Deployment Guide for secrets manager examples and volume-mount alternatives.
CapiscioGuard extracts the caller's badge token from (in priority order):
- Context variable — set by A2A server middleware via
set_capiscio_context() - RunnableConfig —
config={"configurable": {"capiscio_badge": token}} - Input dict —
{"capiscio_badge": token, ...}
For A2A server integrations, set the context at the HTTP perimeter:
from langchain_capiscio import CapiscioRequestContext, set_capiscio_context
set_capiscio_context(CapiscioRequestContext(
badge_token=badge_jwt,
caller_did="did:web:caller.example.com",
))| Level | Name | Description |
|---|---|---|
| 0 | Self-Signed (SS) | No external validation, did:key issuer |
| 1 | Registered (REG) | Account registration with CapiscIO Registry |
| 2 | Domain Validated (DV) | Domain ownership verified via DNS/HTTP challenge |
| 3 | Organization Validated (OV) | Organization existence verified (DUNS, legal entity) |
| 4 | Extended Validated (EV) | Manual review + legal agreement with CapiscIO |
CapiscioGuard(mode, api_key, name, server_url, connect_kwargs, identity, config)— LCEL-composable trust enforcement RunnableCapiscioGuard.invoke(input, config)— Verify badge and pass through to downstreamCapiscioGuard.ainvoke(input, config)— Async versionCapiscioGuard.from_env(mode, **kwargs)— Create guard from environment variables
CapiscioCallbackHandler(emitter, identity)— Emit chain/tool lifecycle events to CapiscIO
CapiscioTool(tool, mode, identity, api_key)— Wrap a LangChainToolwith trust enforcementverify_badge—@tool-decorated function for agent-driven badge verificationresolve_agent_card—@tool-decorated function for agent card resolution
@capiscio_guard(mode, identity, config, api_key)— Decorator for LangGraph function-based nodes
set_capiscio_context(ctx)— Set request context (badge token, caller DID) for the current invocationget_capiscio_context()— Retrieve current request contextCapiscioRequestContext— Dataclass holding badge token and caller DID
# Clone repository
git clone https://github.com/capiscio/langchain-capiscio.git
cd langchain-capiscio
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest -v
# Run tests with coverage
pytest --cov=langchain_capiscio --cov-report=htmlApache License 2.0
See CONTRIBUTING.md for guidelines.