signet
Globally hooks into all CrewAI tool calls to automatically cryptographically sign every action and maintain an audit log of agent activities.
Signs and audits every tool call via callback handlers (SignetCallbackHandler and AsyncSignetCallbackHandler), enabling cryptographic verification of both synchronous and asynchronous chain executions.
Signs tool calls in LangGraph workflows using LangChain's callback system, allowing cryptographic verification of state graph executions and agent interactions.
Auto-signs Bash tool calls in OpenAI's Codex CLI through a plugin hook, with optional MCP server integration for on-demand signing tools and verification capabilities.
Provides callbacks for the Vercel AI SDK (createSignetCallbacks) to cryptographically sign every tool call made by AI agents, with signed receipts accessible after text generation.
Your AI agent just called a tool. Can you prove what it did?
Most agent stacks log actions after the fact but never verify what was actually sent. If a request is replayed, tampered with, or forged, the execution side has no way to know.
Signet fixes this: each agent gets an Ed25519 identity, every tool call is signed, a hash-chained audit log records what happened, and clients or servers can verify the request before trusting it. 3 lines to sign. 3 lines to verify. Open source.
If Signet is useful to you, star this repo to help more teams find it.
Start with the CLI flow below to see signing in action, then jump to See It Reject Bad Requests to watch the server block unsigned, tampered, stale, or mis-targeted requests before they run.
Why Signet
Signet adds a lightweight trust layer for agent actions:
Sign every tool call with the agent's cryptographic key
Audit what happened with an append-only, hash-chained local log
Verify any action receipt offline, no network needed
Integrate with Claude Code, Codex CLI, MCP clients and servers, Python frameworks, and Vercel AI SDK
Try It In 30 Seconds
pip install signet-authfrom signet_auth import SigningAgent
agent = SigningAgent.create("my-agent", owner="team")
receipt = agent.sign("github_create_issue", params={"title": "fix bug"})
assert agent.verify(receipt)
print(receipt.id)If you're new, start with one of these four paths:
Choose Your Path
Claude Code: Best for the fastest first run in a coding agent. Run
/plugin install signet@claude-plugins-officialin Claude Code. In 5 minutes you'll have signed tool calls and a local audit log at~/.signet/audit/.Codex CLI: Best for signing Bash tool calls in Codex. Copy
plugins/codex/into~/.codex/plugins/signetand add onePostToolUsehook. In 5 minutes you'll have signed Bash actions in Codex using the same audit trail.MCP clients: Best if you control an MCP client or transport. Wrap your transport with
new SigningTransport(inner, secretKey, "my-agent"). In 5 minutes you'll have signedtools/callrequests with receipts inparams._meta._signet.MCP servers: Best if you want verification before execution. Call
verifyRequest(request, {...})in your tool handler. In 5 minutes you'll have signer, freshness, target-binding, and tool/params checks at the execution boundary.
See It Reject Bad Requests
Run the shortest execution-boundary demo:
cd examples/mcp-agent
npm run execution-boundary-demoSee examples/mcp-agent/demo-execution-boundary.mjs for the demo source.
When Teams Reach For Signet
You need an audit trail for coding agents, MCP tools, or CI automation
You want to prove which agent requested an action after an incident
You need receipts that can be verified offline without depending on a hosted service
You want signed tool-call evidence without adding a proxy or gateway to your stack
What Signet Is And Isn't
Signet is an attestation layer for agent actions: sign, audit, and verify
Signet is designed to fit into existing agent stacks with SDKs, plugins, and MCP middleware
Signet is not a policy engine, firewall, or action blocker
Signet is not a replacement for gateways; it complements prevention and enforcement tools
Install
# CLI
cargo install signet-cli
# Python
pip install signet-auth
# TypeScript (MCP middleware)
npm install @signet-auth/core @signet-auth/mcp
# TypeScript (MCP server verification)
npm install @signet-auth/mcp-server
# TypeScript (Vercel AI SDK middleware)
npm install @signet-auth/vercel-aiQuick Start
Claude Code Plugin
Auto-sign every tool call in Claude Code with zero configuration:
# Option A: From the official Anthropic plugin marketplace
/plugin install signet@claude-plugins-official
# Option B: Add Signet as a marketplace source, then install
/plugin marketplace add Prismer-AI/signet
/plugin install signet@signetEvery tool call is signed with Ed25519 and logged to a hash-chained audit trail at ~/.signet/audit/.
Alternative install methods:
# From Git
claude plugin add --from https://github.com/Prismer-AI/signet
# Via signet CLI
signet claude installCodex Plugin
Auto-sign every Bash tool call in Codex CLI:
git clone https://github.com/Prismer-AI/signet.git
cp -r signet/plugins/codex ~/.codex/plugins/signetThen add the hook to ~/.codex/hooks.json:
{
"hooks": {
"PostToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "node \"$HOME/.codex/plugins/signet/bin/sign.cjs\"",
"timeout": 5
}]
}]
}
}Or use the MCP server for on-demand signing tools:
codex mcp add signet -- npx @signet-auth/mcp-toolsCLI
# Generate an agent identity
signet identity generate --name my-agent
# Sign an action
signet sign --key my-agent --tool "github_create_issue" \
--params '{"title":"fix bug"}' --target mcp://github.local
# Verify a receipt
signet verify receipt.json --pubkey my-agent
# Audit recent actions
signet audit --since 24h
# Verify log integrity
signet verify --chainMCP Client Integration (TypeScript)
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { generateKeypair } from "@signet-auth/core";
import { SigningTransport } from "@signet-auth/mcp";
// Generate an agent identity
const { secretKey } = generateKeypair();
// Wrap any MCP transport -- all tool calls are now signed
const inner = new StdioClientTransport({ command: "my-mcp-server" });
const transport = new SigningTransport(inner, secretKey, "my-agent");
const client = new Client({ name: "my-agent", version: "1.0" }, {});
await client.connect(transport);
// Every callTool() is now cryptographically signed
const result = await client.callTool({
name: "echo",
arguments: { message: "Hello!" },
});Every tools/call request gets a signed receipt injected into params._meta._signet.
MCP Server Verification
If you control the MCP server too, verify requests before execution:
import { verifyRequest } from "@signet-auth/mcp-server";
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const verified = verifyRequest(request, {
trustedKeys: ["ed25519:..."],
maxAge: 300,
});
if (!verified.ok) return { content: [{ type: "text", text: verified.error }], isError: true };
console.log(`Verified: ${verified.signerName}`);
// process tool call...
});Vercel AI SDK Integration
import { generateText } from "ai";
import { generateKeypair } from "@signet-auth/core";
import { createSignetCallbacks } from "@signet-auth/vercel-ai";
const { secretKey } = generateKeypair();
const callbacks = createSignetCallbacks(secretKey, "my-agent");
const result = await generateText({
model: openai("gpt-4"),
tools: { myTool },
...callbacks,
prompt: "...",
});
// Every tool call is now signed
console.log(callbacks.receipts);Reference MCP Server
This repo also includes a minimal MCP reference server that demonstrates server-side verification with @signet-auth/mcp-server.
cd examples/mcp-agent
npm ci
npm run verifier-serverAvailable tools:
inspect_current_request— verifies the current MCP tool call if it includesparams._meta._signetverify_receipt— verifies a raw Signet receipt against a public keyverify_request_payload— verifies a synthetic MCPtools/callpayload offline
Environment variables:
SIGNET_TRUSTED_KEYS— comma-separateded25519:<base64>public keysSIGNET_REQUIRE_SIGNATURE—trueorfalse(defaultfalse)SIGNET_MAX_AGE— max receipt age in seconds (default300)SIGNET_EXPECTED_TARGET— optional expectedreceipt.action.target
Standalone MCP Signing Server
@signet-auth/mcp-tools exposes Signet signing, verification, and content hashing as MCP tools — plug into any MCP-compatible client:
npx @signet-auth/mcp-toolsAvailable tools: signet_generate_keypair, signet_sign, signet_verify, signet_content_hash.
Python (LangChain / CrewAI / AutoGen + 6 more)
pip install signet-authfrom signet_auth import SigningAgent
# Create an agent identity (saved to ~/.signet/keys/)
agent = SigningAgent.create("my-agent", owner="willamhou")
# Sign any tool call -- receipt is auto-appended to audit log
receipt = agent.sign("github_create_issue", params={"title": "fix bug"})
# Verify
assert agent.verify(receipt)
# Query audit log
for record in agent.audit_query(since="24h"):
print(f"{record.receipt.ts} {record.receipt.action.tool}")LangChain Integration
from signet_auth import SigningAgent
from signet_auth.langchain import SignetCallbackHandler
agent = SigningAgent("my-agent")
handler = SignetCallbackHandler(agent)
# Every tool call is now signed + audited
chain.invoke(input, config={"callbacks": [handler]})
# Async chains supported too
from signet_auth.langchain import AsyncSignetCallbackHandlerCrewAI Integration
from signet_auth import SigningAgent
from signet_auth.crewai import install_hooks
agent = SigningAgent("my-agent")
install_hooks(agent)
# All CrewAI tool calls are now globally signed
crew.kickoff()AutoGen Integration
from signet_auth import SigningAgent
from signet_auth.autogen import signed_tool, sign_tools
agent = SigningAgent("my-agent")
# Wrap a single tool
wrapped = signed_tool(tool, agent)
# Or wrap all tools at once
wrapped_tools = sign_tools([tool1, tool2], agent)LangGraph Integration
LangGraph uses LangChain's callback system — the same handler works directly:
from signet_auth import SigningAgent
from signet_auth.langgraph import SignetCallbackHandler
agent = SigningAgent("my-agent")
handler = SignetCallbackHandler(agent)
result = graph.invoke(input, config={"callbacks": [handler]})LlamaIndex Integration
from signet_auth import SigningAgent
from signet_auth.llamaindex import install_handler
agent = SigningAgent("my-agent")
handler = install_handler(agent)
# All tool call events are now signed
index = ... # your LlamaIndex setup
response = index.as_query_engine().query("What is Signet?")
# Access receipts
print(handler.receipts)Pydantic AI Integration
from signet_auth import SigningAgent
from signet_auth.pydantic_ai_integration import SignetMiddleware
agent = SigningAgent("my-agent")
middleware = SignetMiddleware(agent)
@middleware.wrap
def my_tool(query: str) -> str:
return f"result: {query}"Google ADK Integration
from signet_auth import SigningAgent
from signet_auth.google_adk import SignetPlugin
agent = SigningAgent("my-agent")
plugin = SignetPlugin(agent)
# Pass as callback to ADK agentSmolagents Integration
from signet_auth import SigningAgent
from signet_auth.smolagents import signet_step_callback
agent = SigningAgent("my-agent")
callback = signet_step_callback(agent)
bot = CodeAgent(tools=[...], model=model, step_callbacks=[callback])OpenAI Agents SDK Integration
from signet_auth import SigningAgent
from signet_auth.openai_agents import SignetAgentHooks
agent = SigningAgent("my-agent")
oai_agent = Agent(
name="assistant",
hooks=SignetAgentHooks(agent),
tools=[...],
)Note: Tool call arguments are not yet available in the hook API (issue #939). Only the tool name is signed.
Low-Level API
from signet_auth import generate_keypair, sign, verify, Action
kp = generate_keypair()
action = Action("github_create_issue", params={"title": "fix bug"})
receipt = sign(kp.secret_key, action, "my-agent", "willamhou")
assert verify(receipt, kp.public_key)Bilateral Receipt (Server Co-signing)
from signet_auth import generate_keypair, sign, sign_bilateral, verify_bilateral, Action
# Agent signs the tool call
agent_kp = generate_keypair()
action = Action("github_create_issue", params={"title": "fix bug"})
agent_receipt = sign(agent_kp.secret_key, action, "my-agent")
# Server co-signs with the response
server_kp = generate_keypair()
bilateral = sign_bilateral(
server_kp.secret_key, agent_receipt,
{"content": [{"type": "text", "text": "issue #42 created"}]},
"github-server",
)
assert verify_bilateral(bilateral, server_kp.public_key)
assert bilateral.v == 3 # v3 = bilateral receiptHow It Works
Your Agent
|
v
SigningTransport (wraps any MCP transport)
|
+---> Signs each tool call (Ed25519)
+---> Appends Action Receipt to local audit log (hash-chained)
+---> Forwards request to MCP server (unchanged)Agent-side only. MCP servers don't need to change.
Action Receipt
Every tool call produces a signed receipt:
{
"v": 1,
"id": "rec_e7039e7e7714e84f...",
"action": {
"tool": "github_create_issue",
"params": {"title": "fix bug"},
"params_hash": "sha256:b878192252cb...",
"target": "mcp://github.local",
"transport": "stdio"
},
"signer": {
"pubkey": "ed25519:0CRkURt/tc6r...",
"name": "demo-bot",
"owner": "willamhou"
},
"ts": "2026-03-29T23:24:03.309Z",
"nonce": "rnd_dcd4e135799393...",
"sig": "ed25519:6KUohbnSmehP..."
}The signature covers the entire receipt body (action + signer + timestamp + nonce) using RFC 8785 (JCS) canonical JSON. Modifying any field invalidates the signature.
CLI Commands
Command | Description |
| Generate Ed25519 identity (encrypted by default) |
| Generate without encryption (for CI) |
| List all identities |
| Export public key as JSON |
| Sign an action |
| Store only params hash (not raw params) |
| Write receipt to file instead of stdout |
| Skip audit log append |
| Verify a receipt signature |
| Verify audit log hash chain integrity |
| List recent actions |
| Filter by time (e.g. 24h, 7d) |
| Filter by tool name |
| Verify all receipt signatures |
| Export records as JSON |
| Install Claude Code plugin (PostToolUse signing hook) |
| Remove Claude Code plugin |
Passphrase via interactive prompt or SIGNET_PASSPHRASE env var for CI.
Documentation
Doc | Description |
System design, component overview, data flow | |
Crypto primitives, threat model, key storage | |
Step-by-step MCP setup with SigningTransport | |
GitHub Actions example, key management for CI | |
Querying, filtering, hash chain verification | |
Build instructions, development workflow | |
Version history |
Project Structure
signet/
├── crates/signet-core/ Rust core: identity, sign, verify, audit, keystore
├── signet-cli/ CLI tool (signet binary)
├── bindings/
│ ├── signet-ts/ WASM binding (wasm-bindgen)
│ └── signet-py/ Python binding (PyO3 + maturin)
├── plugins/
│ ├── claude-code/ Claude Code plugin (WASM signing + audit)
│ └── codex/ Codex CLI plugin (WASM signing + audit)
├── packages/
│ ├── signet-core/ @signet-auth/core — TypeScript wrapper
│ ├── signet-mcp/ @signet-auth/mcp — MCP SigningTransport middleware
│ ├── signet-mcp-server/ @signet-auth/mcp-server — Server verification
│ ├── signet-mcp-tools/ @signet-auth/mcp-tools — Standalone MCP signing server
│ └── signet-vercel-ai/ @signet-auth/vercel-ai — Vercel AI SDK middleware
├── examples/
│ ├── wasm-roundtrip/ WASM validation tests
│ └── mcp-agent/ MCP agent, echo server, and verifier server example
├── docs/ Design docs, specs, plans
├── LICENSE-APACHE
└── LICENSE-MITBuilding from Source
Prerequisites
Rust (1.70+)
wasm-pack
Node.js (18+)
Python (3.10+) + maturin (for Python binding)
Build
# Rust core + CLI
cargo build --release -p signet-cli
# WASM binding
wasm-pack build bindings/signet-ts --target nodejs --out-dir ../../packages/signet-core/wasm
# TypeScript packages
cd packages/signet-core && npm run build
cd packages/signet-mcp && npm run build
cd packages/signet-mcp-tools && npm run build# Python binding
cd bindings/signet-py
pip install maturin
maturin developTest
# Rust tests (95 tests)
cargo test --workspace
# Python tests (160 tests)
cd bindings/signet-py && pytest tests/ -v
# WASM roundtrip (8 tests)
node examples/wasm-roundtrip/test.mjs
# TypeScript tests (26 tests)
cd packages/signet-core && npm test
cd packages/signet-mcp && npm test
cd packages/signet-mcp-server && npm test
cd packages/signet-mcp-tools && npm test
# Plugin tests (54 tests)
cd plugins/claude-code && npm test
cd plugins/codex && npm test
# Vercel AI SDK tests (5 tests)
cd packages/signet-vercel-ai && npm test
# Reference verifier server smoke test
cd examples/mcp-agent && npm run smokeSecurity
Ed25519 signatures (128-bit security level,
ed25519-dalek)Argon2id key derivation (OWASP recommended minimum)
XChaCha20-Poly1305 key encryption with authenticated associated data (AAD)
SHA-256 hash chain for tamper-evident audit log
RFC 8785 (JCS) canonical JSON for deterministic signatures
Keys stored at ~/.signet/keys/ with 0600 permissions. Override with SIGNET_HOME env var.
What Signet proves
Agent key X signed intent to call tool Y with params Z at time T
What Signet does NOT prove (yet)
That the MCP server executed the action (use bilateral receipts with
signResponse()for server co-signing — shipped in v0.4)That signer.owner actually controls the key (planned: identity registry)
Signet is an attestation tool (proving what happened), not a prevention tool (blocking bad actions). It complements policy enforcement tools like firewalls and gateways.
License
Apache-2.0 + MIT dual license.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Prismer-AI/signet'
If you have feedback or need assistance with the MCP directory API, please join our Discord server