Proposal: Integrate PIC-Standard for Provenance & Impact Checks in LangGraph #1118

Open
opened 2026-02-20 17:43:09 -05:00 by yindo · 4 comments
Owner

Originally created by @madeinplutofabio on GitHub (Jan 13, 2026).

Checked other resources

  • This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/).
  • I added a clear and detailed title that summarizes the issue.
  • I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
  • I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue.

Example Code

from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated

# Define state
class AgentState(TypedDict):
    messages: Annotated[list[str], "A list of messages"]

# Dummy tool that could be risky (e.g., financial action from untrusted input)
def risky_tool(state: AgentState) -> AgentState:
    # Simulate untrusted input triggering high-impact action
    if "transfer money" in state["messages"][-1]:  # Untrusted prompt
        print("Executing risky action: Transferring $1000!")  # Potential causal gap
    return {"messages": state["messages"] + ["Action executed"]}

# Simple agent node
def agent(state: AgentState) -> AgentState:
    return {"messages": state["messages"] + ["Planning action"]}

# Build graph
graph = StateGraph(AgentState)
graph.add_node("agent", agent)
graph.add_node("tool", risky_tool)
graph.add_edge("agent", "tool")
graph.add_edge("tool", END)
graph.set_entry_point("agent")

# Compile and run with untrusted input
compiled_graph = graph.compile()
result = compiled_graph.invoke({"messages": ["User: transfer money based on this sketchy prompt"]})

print(result)  # Runs without safety checks, exposing causal gap

Error Message and Stack Trace (if applicable)

This example shows a LangGraph workflow where untrusted input can directly trigger a high-impact action (e.g., financial transfer) without provenance or impact validation, a potential security gap. My PIC-Standard integration (as a PICToolNode wrapper) would enforce JSON contracts to block this. See proposal below.

Description

Hi LangGraph team,

I'm the maintainer of PIC-Standard, an open-source protocol for enforcing safety in agentic AI via JSON "contracts" that tie provenance (data trust levels), intent (action rationale), and impact (risk taxonomy like money or privacy). It bridges the "causal gap" where untrusted inputs (e.g., prompt injections) could trigger high-risk side effects, complementing tools like DeepMind's CaMeL but with a production-ready JSON schema and Python SDK.

I've built a drop-in integration for LangGraph: a PICToolNode that validates action proposals before execution, blocking tainted actions while allowing trusted ones. This adds lightweight governance without disrupting workflows, which is ideal for enterprise use cases like FinTech or SaaS agents.

Key Benefits for LangGraph Users

  • Enhances security: Requires trusted evidence for high-impact tools (e.g., financial APIs).
  • Interoperable: Uses a simple JSON schema; extensible for custom impacts.
  • Quick to Adopt: Install via pip install pic-standard[langgraph]; minimal overhead.

Demo
Here is quick demo or can check full demo and code on our repo.

from langgraph.graph import StateGraph, END
from pic_standard.langgraph import PICToolNode  # Our middleware

# Define your agent state and tools...

graph = StateGraph(AgentState)
graph.add_node("agent", agent_node)
graph.add_node("tool", PICToolNode(tools=[your_tool]))  # Wraps with PIC validation

# ... rest of graph setup

# Run: Proposals attach via args['__pic'] and get verified

Proposal

  • Add PICToolNode as an optional built-in (or example) in LangGraph docs/codebase.
  • Mention in tutorials for safety-focused agents.

This could make LangGraph even stronger for production AI. Happy to iterate based on feedback or adjust the implementation!

Thanks,
Fabio Marcello Salvadori

System Info

  • OS: Windows 11, Ubuntu 22.04—use what you tested on
  • Python version: 3.10
  • LangGraph version: 0.0.25 (check via pip show langgraph—e.g., 0.0.25 as of Jan 2026)
  • LangChain version: 0.1.0 (if relevant, e.g., 0.1.0)
  • Other dependencies: Pydantic 2.5.3 (used in PIC verifier); no other specifics for this gap
  • Environment: Standard virtualenv; no Docker or cloud specifics
Originally created by @madeinplutofabio on GitHub (Jan 13, 2026). ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/). - [x] I added a clear and detailed title that summarizes the issue. - [x] I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example). - [x] I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue. ### Example Code ```python from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated # Define state class AgentState(TypedDict): messages: Annotated[list[str], "A list of messages"] # Dummy tool that could be risky (e.g., financial action from untrusted input) def risky_tool(state: AgentState) -> AgentState: # Simulate untrusted input triggering high-impact action if "transfer money" in state["messages"][-1]: # Untrusted prompt print("Executing risky action: Transferring $1000!") # Potential causal gap return {"messages": state["messages"] + ["Action executed"]} # Simple agent node def agent(state: AgentState) -> AgentState: return {"messages": state["messages"] + ["Planning action"]} # Build graph graph = StateGraph(AgentState) graph.add_node("agent", agent) graph.add_node("tool", risky_tool) graph.add_edge("agent", "tool") graph.add_edge("tool", END) graph.set_entry_point("agent") # Compile and run with untrusted input compiled_graph = graph.compile() result = compiled_graph.invoke({"messages": ["User: transfer money based on this sketchy prompt"]}) print(result) # Runs without safety checks, exposing causal gap ``` ### Error Message and Stack Trace (if applicable) ```shell This example shows a LangGraph workflow where untrusted input can directly trigger a high-impact action (e.g., financial transfer) without provenance or impact validation, a potential security gap. My PIC-Standard integration (as a PICToolNode wrapper) would enforce JSON contracts to block this. See proposal below. ``` ### Description Hi LangGraph team, I'm the maintainer of [PIC-Standard](https://github.com/madeinplutofabio/pic-standard), an open-source protocol for enforcing safety in agentic AI via JSON "contracts" that tie provenance (data trust levels), intent (action rationale), and impact (risk taxonomy like `money` or `privacy`). It bridges the "causal gap" where untrusted inputs (e.g., prompt injections) could trigger high-risk side effects, complementing tools like DeepMind's CaMeL but with a production-ready JSON schema and Python SDK. I've built a drop-in integration for LangGraph: a `PICToolNode` that validates action proposals before execution, blocking tainted actions while allowing trusted ones. This adds lightweight governance without disrupting workflows, which is ideal for enterprise use cases like FinTech or SaaS agents. **Key Benefits for LangGraph Users** - Enhances security: Requires trusted evidence for high-impact tools (e.g., financial APIs). - Interoperable: Uses a simple JSON schema; extensible for custom impacts. - Quick to Adopt: Install via `pip install pic-standard[langgraph]`; minimal overhead. **Demo** Here is quick demo or can check [full demo and code](https://github.com/madeinplutofabio/pic-standard/tree/main/examples) on our repo. ```python from langgraph.graph import StateGraph, END from pic_standard.langgraph import PICToolNode # Our middleware # Define your agent state and tools... graph = StateGraph(AgentState) graph.add_node("agent", agent_node) graph.add_node("tool", PICToolNode(tools=[your_tool])) # Wraps with PIC validation # ... rest of graph setup # Run: Proposals attach via args['__pic'] and get verified ``` **Proposal** - [ ] Add `PICToolNode` as an optional built-in (or example) in LangGraph docs/codebase. - [ ] Mention in tutorials for safety-focused agents. This could make LangGraph even stronger for production AI. Happy to iterate based on feedback or adjust the implementation! Thanks, Fabio Marcello Salvadori ### System Info - OS: Windows 11, Ubuntu 22.04—use what you tested on - Python version: 3.10 - LangGraph version: 0.0.25 (check via `pip show langgraph`—e.g., 0.0.25 as of Jan 2026) - LangChain version: 0.1.0 (if relevant, e.g., 0.1.0) - Other dependencies: Pydantic 2.5.3 (used in PIC verifier); no other specifics for this gap - Environment: Standard virtualenv; no Docker or cloud specifics
yindo added the bugpending labels 2026-02-20 17:43:09 -05:00
Author
Owner

@madeinplutofabio commented on GitHub (Feb 13, 2026):

Added evidence verification Ed25519 signatures.

@madeinplutofabio commented on GitHub (Feb 13, 2026): Added evidence verification Ed25519 signatures.
Author
Owner

@gspeter-max commented on GitHub (Feb 18, 2026):

This issue describes a feature request, not a bug. LangGraph already provides built-in mechanisms for tool validation and safety:

Existing Built-in Solutions

1. wrap_tool_call Parameter (ToolNode)

The ToolNode class already supports custom validation via the wrap_tool_call parameter. This allows intercepting tool execution for custom safety checks:

from langgraph.prebuilt import ToolNode

def safety_validator(request, execute):
    # Check if tool is high-risk
    if request.tool_call["name"] in ["transfer_money", "delete_data"]:
        # Add your safety checks here
        if not is_authorized(request.runtime.state):
            return ToolMessage(
                content="Blocked: insufficient authorization",
                name=request.tool_call["name"],
                tool_call_id=request.tool_call["id"],
                status="error"
            )
    return execute(request)

tool_node = ToolNode([risky_tool], wrap_tool_call=safety_validator)

2. ValidationNode

For schema-based validation, LangGraph provides ValidationNode:

from langgraph.prebuilt import ValidationNode
from pydantic import BaseModel, field_validator

class SafeTransfer(BaseModel):
    amount: float
    reason: str
    authorization_code: str
    
    @field_validator("authorization_code")
    def must_be_authorized(cls, v):
        if not is_valid_auth_code(v):
            raise ValueError("Invalid authorization")
        return v

validation_node = ValidationNode([SafeTransfer])

3. Human-in-the-Loop Interrupts

For critical actions, use interrupts for human approval:

from langgraph.types import interrupt

def critical_action_node(state):
    tool_call = state["messages"][-1].tool_calls[0]
    response = interrupt([{
        "action_request": {
            "action": tool_call['name'],
            "args": tool_call['args']
        },
        "config": {
            "allow_ignore": True,
            "allow_respond": True,
            "allow_edit": False,
            "allow_accept": False
        }
    }])
    # Handle human response...

Conclusion

The example code shows normal tool execution behavior, not a vulnerability. The "causal gap" described is addressed by existing LangGraph features. Users can implement custom safety policies using the wrap_tool_call parameter without requiring third-party dependencies in core.

For PIC-Standard integration, I recommend publishing it as a separate package that users can optionally install, similar to how other LangGraph extensions work. The wrap_tool_call extension point is designed for exactly this use case.

@gspeter-max commented on GitHub (Feb 18, 2026): This issue describes a feature request, not a bug. LangGraph already provides built-in mechanisms for tool validation and safety: ## Existing Built-in Solutions ### 1. `wrap_tool_call` Parameter (ToolNode) The `ToolNode` class already supports custom validation via the `wrap_tool_call` parameter. This allows intercepting tool execution for custom safety checks: ```python from langgraph.prebuilt import ToolNode def safety_validator(request, execute): # Check if tool is high-risk if request.tool_call["name"] in ["transfer_money", "delete_data"]: # Add your safety checks here if not is_authorized(request.runtime.state): return ToolMessage( content="Blocked: insufficient authorization", name=request.tool_call["name"], tool_call_id=request.tool_call["id"], status="error" ) return execute(request) tool_node = ToolNode([risky_tool], wrap_tool_call=safety_validator) ``` ### 2. ValidationNode For schema-based validation, LangGraph provides `ValidationNode`: ```python from langgraph.prebuilt import ValidationNode from pydantic import BaseModel, field_validator class SafeTransfer(BaseModel): amount: float reason: str authorization_code: str @field_validator("authorization_code") def must_be_authorized(cls, v): if not is_valid_auth_code(v): raise ValueError("Invalid authorization") return v validation_node = ValidationNode([SafeTransfer]) ``` ### 3. Human-in-the-Loop Interrupts For critical actions, use interrupts for human approval: ```python from langgraph.types import interrupt def critical_action_node(state): tool_call = state["messages"][-1].tool_calls[0] response = interrupt([{ "action_request": { "action": tool_call['name'], "args": tool_call['args'] }, "config": { "allow_ignore": True, "allow_respond": True, "allow_edit": False, "allow_accept": False } }]) # Handle human response... ``` ## Conclusion The example code shows normal tool execution behavior, not a vulnerability. The "causal gap" described is addressed by existing LangGraph features. Users can implement custom safety policies using the `wrap_tool_call` parameter without requiring third-party dependencies in core. For PIC-Standard integration, I recommend publishing it as a separate package that users can optionally install, similar to how other LangGraph extensions work. The `wrap_tool_call` extension point is designed for exactly this use case.
Author
Owner

@madeinplutofabio commented on GitHub (Feb 19, 2026):

Thanks @gspeter-max , I totally agree that ToolNode(wrap_tool_call=...) is the right extension point.

PIC is not proposing a new execution mechanism, but a portable contract standard + a reference enforcement suite:

  • PIC/1.0 schema (intent/impact/provenance/claims/evidence/action) so different runtimes can interop
  • deterministic verifier rules (fail-closed) including privacy/money/irreversible gating
  • tool binding invariant (proposal.action.tool must match the actual tool call)
  • optional evidence verification (hash + Ed25519 sig) backed by a keyring with expiry/revocation

So yes: users can implement custom checks with wrap_tool_call, but PIC provides a tested, reusable, cross-runtime policy+evidence layer that you can plug into that hook.

I’m happy to publish/maintain PIC as an optional package (already done) and contribute an official LangGraph docs recipe showing how to wire PIC into wrap_tool_call for enterprise tool governance.

Is there any official package / extension repo/channel where to list PIC's LangGraph integration?

@madeinplutofabio commented on GitHub (Feb 19, 2026): Thanks @gspeter-max , I totally agree that ToolNode(wrap_tool_call=...) is the right extension point. PIC is not proposing a new execution mechanism, but a portable contract standard + a reference enforcement suite: - PIC/1.0 schema (intent/impact/provenance/claims/evidence/action) so different runtimes can interop - deterministic verifier rules (fail-closed) including privacy/money/irreversible gating - tool binding invariant (proposal.action.tool must match the actual tool call) - optional evidence verification (hash + Ed25519 sig) backed by a keyring with expiry/revocation So yes: users can implement custom checks with wrap_tool_call, but PIC provides a tested, reusable, cross-runtime policy+evidence layer that you can plug into that hook. I’m happy to publish/maintain PIC as an optional package (already done) and contribute an official LangGraph docs recipe showing how to wire PIC into wrap_tool_call for enterprise tool governance. Is there any official package / extension repo/channel where to list PIC's LangGraph integration?
Author
Owner

@gspeter-max commented on GitHub (Feb 19, 2026):

@madeinplutofabio Great question! Here are the best options for listing your PIC-Standard LangGraph integration:

Option 1: Add to "Awesome LangGraph" (Recommended)

Submit your integration to the community-curated list:

  • Repository: von-development/awesome-LangGraph
  • Open a PR adding your project with:
    • Project name and description
    • Link to your repo
    • Category (e.g., "Security", "Tool Validation")

This is where community integrations are typically discoverable.

Option 2: Add to LangGraph Examples

Contribute an example showing how to use PIC-Standard with LangGraph:

  • Repo: langchain-ai/langgraph
  • Directory: /examples/ (e.g., /examples/pic-standard-integration/)
  • Show how to wire wrap_tool_call with PIC validation

Option 3: Share on LangChain Forum

Post your integration on the LangChain Forum with:

  • Explanation of PIC-Standard
  • Code examples for LangGraph integration
  • Use cases and benefits

Option 4: Write a Community Tutorial

Create a tutorial or blog post and share it. The LangChain team may feature community content in their docs or newsletter.


My recommendation: Start with Option 1 (Awesome LangGraph) since that's the standard place for community LangGraph tools and integrations. You can also do Option 3 (Forum post) to get visibility from the LangGraph team.

Since you're maintaining PIC as a separate package (which is the right approach), these channels will help users discover your integration without requiring it to be in LangGraph core.

@gspeter-max commented on GitHub (Feb 19, 2026): @madeinplutofabio Great question! Here are the best options for listing your PIC-Standard LangGraph integration: ## Option 1: Add to "Awesome LangGraph" (Recommended) ⭐ Submit your integration to the community-curated list: - Repository: [von-development/awesome-LangGraph](https://github.com/von-development/awesome-LangGraph) - Open a PR adding your project with: - Project name and description - Link to your repo - Category (e.g., "Security", "Tool Validation") This is where community integrations are typically discoverable. ## Option 2: Add to LangGraph Examples Contribute an example showing how to use PIC-Standard with LangGraph: - Repo: `langchain-ai/langgraph` - Directory: `/examples/` (e.g., `/examples/pic-standard-integration/`) - Show how to wire `wrap_tool_call` with PIC validation ## Option 3: Share on LangChain Forum Post your integration on the [LangChain Forum](https://forum.langchain.com/) with: - Explanation of PIC-Standard - Code examples for LangGraph integration - Use cases and benefits ## Option 4: Write a Community Tutorial Create a tutorial or blog post and share it. The LangChain team may feature community content in their docs or newsletter. --- **My recommendation:** Start with **Option 1** (Awesome LangGraph) since that's the standard place for community LangGraph tools and integrations. You can also do **Option 3** (Forum post) to get visibility from the LangGraph team. Since you're maintaining PIC as a separate package (which is the right approach), these channels will help users discover your integration without requiring it to be in LangGraph core.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#1118