# 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](/docs/quickstart/python-langchain), [TypeScript quickstart](/docs/quickstart/typescript-langchain). What recording covers and where it stops: [Tool coverage](/docs/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.

```bash
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](https://github.com/AIME-TECH-UK-LIMITED/signafaide-sdk/tree/main/python-langchain).

## 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)
```

```ts
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.

```text
$ 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](/docs/quickstart/python-langchain)
