signafAIde
Documentation menu

Audit log for LangChain agents

Add the middleware to a LangChain agent, or wrap the tools you hand it, and every tool call is written as a signed, chained record: a decision before the tool runs and an outcome after it. Python and TypeScript.

Full steps: Python quickstart, TypeScript quickstart. What recording covers and where it stops: Tool coverage.

Python: the middleware #

langchain-signafaide is a LangChain agent middleware. Add it to create_agent and every tool call the agent makes is recorded, without wrapping each tool.

Terminal
pip install langchain-signafaide
Python
from langchain.agents import create_agent
from langchain_signafaide import SignafaideMiddleware

tools = [lookup_order, refund]
agent = create_agent(
    model,
    tools=tools,
    middleware=[SignafaideMiddleware(agent="support-bot", tools=tools)],
)
  • agent names the record chain: one name per running agent instance in production.
  • tools declares every tool the agent can call as recorded, so signafaide verify can say that all of them are covered. Without it, a tool is declared when it is first called.
  • strict=True runs a tool only once the witness has confirmed its record; store_content=True also sends inputs and outputs, encrypted.
  • It works with invoke and ainvoke. A tool that raises is recorded with its error, and the error is raised unchanged.

Use the middleware or wrap_tools for an agent's tools, not both, or each call is recorded twice. The package needs Python 3.10 or later and LangChain 1.x; its source is in the SDK repository.

Wrapping the tools #

One call around the tools you already have. wrap_tools takes @tool functions and BaseTool instances; wrapTools takes tool(), DynamicStructuredTool and StructuredTool instances. The copies still work with bind_tools and bindTools, and LangChain is not a dependency of the SDK.

Python

import signafaide
from langchain.agents import create_agent

tools = signafaide.wrap_tools(
    [lookup_order, refund], agent="support-bot"
)
agent = create_agent(model, tools=tools)

TypeScript

import { wrapTools } from "signafaide";
import { createAgent } from "langchain";

const tools = wrapTools([lookupOrder, refund], {
  agent: "support-bot",
});
const agent = createAgent({ model, tools });

A verify run #

Run the agent once, then signafaide verify (npx signafaide verify in a TypeScript project). This is a local run in which the agent called refund once: three records for the tool list, one decision and one outcome.

$ signafaide verify
local state: /home/dev/support-agent/.signafaide
 1. Envelope schema              PASS         5 records
 2. Record hashes                NOT CHECKED  0 records; 5 not yet shipped (no receipt); nothing co-signed yet
 3. Client signatures            PASS         5 records
 4. Agent chains                 PASS         5 records
 5. Merkle proofs                NOT CHECKED  0 records; nothing co-signed yet
 6. Batch hashes and signatures  NOT CHECKED  0 batches; nothing co-signed yet
 7. Batch chain                  NOT CHECKED  0 batches; nothing co-signed yet
 8. Signed heads                 NOT CHECKED  0 heads; nothing co-signed yet
 9. drand rounds                 NOT CHECKED  0 rounds
10. Anchors                      NOT CHECKED  0 anchors; local state holds no anchors
11. OpenTimestamps               NOT CHECKED  0 proofs
12. Content hashes               PASS         5 records
coverage: _pending/nokey/support-bot: all 2 declared tools are recorded
checked here: signatures, chain links and content hashes of all 5 records, using this machine's keys
co-signature, public timestamp: not yet. Run `signafaide init` to send hashes, or self-host (SIGNAFAIDE_URL)
PASS: 4 of 12 checks passed, 8 not checked (nothing co-signed yet)

What is recorded and what is not #

  • Recorded. Every tool call that goes through the middleware, and every invoke of a wrapped tool (and ainvoke in Python), including the calls the model asks for: the tool name and input, then the tool's own return value or its error. Inputs and outputs stay in .signafaide/ on your machine. Nothing is sent until you run signafaide init, and then only salted hashes.
  • Not recorded. A tool you did not wrap, and a call your code makes around the agent or the wrapped copy, leaves no record. Give the full list to declare_tools or declareTools and signafaide verify names the tools that are not recorded.
  • Evidence, not certification. Records prove integrity and timing, not that a decision was right. The eight checks not run above need a witness: the co-signature and the public timestamp come after signafaide init.

Next: install, wrap, verify and send hashes, step by step. LangChain quickstart