Obsigno
Open-source runtime governance for AI agents at the MCP tool-call layer.
Obsigno is a transparent stdio proxy between an MCP client and server. It evaluates each tools/call before execution, forwards allowed calls, returns MCP-compatible tool errors for denied calls, and writes every decision to an Ed25519-signed, hash-chained ledger.
It is the policy gate + flight recorder for tool-using agents:
- Drop-in MCP enforcement — replace the server command; do not change client or server code.
- Native policy options — built-in ordered JSON rules, OPA Data API, or Cedar via
cedarpy. - Cross-server correlation — sign a shared agent-task trace ID and stable MCP server identity at every hop.
- Tamper-evident, policy-bound evidence — hash arguments/results and exact local policy bytes, chain entries, and sign every record.
- Portable trace evidence — export a trace with a derived manifest, full chain prefix, optional exact policy snapshots, and public key for offline verification.
- Independent verification — detect edits, deletion, reordering, policy-reference changes, broken chains, or the wrong signer.
- Live audit surface — FastAPI endpoints and a self-hosted dashboard.
- One-command demo — Docker Compose launches a verified six-event cross-server agent trace.
Cryptography makes historical modification detectable. Put production ledgers on append-only/WORM or object-lock storage to make modification operationally difficult or impossible.
See
COMPETITIVE.mdfor the evidence-backed competitor matrix and why Obsigno is positioned as the verifiable evidence layer for governed MCP actions, not another general MCP gateway.
Five-minute quickstart
Requires Docker with Compose.
docker compose up -d --build
Open http://localhost:8787. The quickstart runs six real tools/call requests through the stdio proxy and its demo upstream server, creates a persistent signing key, and records signed events across five server identities. The dashboard shows:
- chain verification status;
- allowed and denied agent actions;
- policy reasons;
- correlated task traces;
- MCP server identities and event hashes.
Check it without the UI:
curl http://localhost:8787/api/health
# {"status":"ok","entries":6,"integrity":"verified","checked":6}
Stop the services while preserving the ledger:
docker compose down
Remove the demo ledger and keys as well:
docker compose down -v
Architecture
AI agent / MCP client
|
| JSON-RPC over stdio
v
+-------------------------------+
| Obsigno |
| 1. intercept tools/call |
| 2. evaluate JSON/OPA/Cedar |
| 3. deny or forward |
| 4. sign policy evidence |
+-------------------------------+
|
v
MCP server
+--------------------------+
| signed JSONL ledger |
| FastAPI + live dashboard |
| offline public verifier |
+--------------------------+
Non-tools/call traffic—including initialization, discovery, resources, prompts, notifications, and unrelated responses—passes through transparently. Protocol stdout remains reserved for MCP JSON-RPC; diagnostics stay on stderr.
Install from source
Requires Python 3.10+.
python -m venv .venv
# Windows
.venv\Scripts\pip install -e ".[web,cedar,dev]"
.venv\Scripts\pytest -q
# macOS/Linux
.venv/bin/pip install -e ".[web,cedar,dev]"
.venv/bin/pytest -q
With uv:
uv venv
uv pip install -e ".[web,cedar,dev]"
uv run pytest -q
Available commands:
| Command | Purpose |
|---|---|
obsigno-mcp |
Transparent stdio MCP policy/audit proxy |
obsigno-verify |
Offline full-ledger chain and signature verifier |
obsigno-evidence |
Export or verify a portable trace evidence bundle |
obsigno-dashboard |
FastAPI audit API and live dashboard |
obsigno-demo-proxy |
Exercise the real proxy and demo upstream for Compose |
Put any stdio MCP server behind Obsigno
Everything after -- is the original MCP server command.
Audit-only adoption
Omit a policy to allow all tools while recording signed evidence:
obsigno-mcp \
--server-id filesystem-mcp \
-- npx -y @modelcontextprotocol/server-filesystem /safe/root
On first run Obsigno creates:
.obsigno/audit.jsonl
.obsigno/audit.jsonl.lock
.obsigno/private_key.pem
.obsigno/public_key.pem
Built-in ordered YAML or JSON policy
obsigno-mcp \
--policy policies/demo.policy.yaml \
--server-id filesystem-mcp \
-- npx -y @modelcontextprotocol/server-filesystem /safe/root
Use .yaml/.yml for YAML or .json for JSON. The first matching rule wins, and supplying a policy enables default deny. For example, the equivalent JSON shape is:
{
"default_reason": "no matching allow rule (default deny)",
"rules": [
{
"id": "block-destructive",
"effect": "deny",
"tool": "delete_database",
"reason": "destructive database operations are blocked"
},
{
"id": "allow-reads",
"effect": "allow",
"tool": "read_file",
"reason": "read-only file access is permitted"
}
]
}
Rules may include an exact actor; "*" matches every actor or tool. Set the actor with --actor agent://name/session.
Native Cedar policy
Install the cedar extra, then use the included policy:
obsigno-mcp \
--policy-backend cedar \
--policy policies/policy.example.cedar \
--server-id filesystem-mcp \
-- npx -y @modelcontextprotocol/server-filesystem /safe/root
Obsigno maps the request to Cedar as:
principal = Agent::<actor>
action = Action::<tool name>
resource = MCPTool::<tool name>
context.arguments = tool arguments
Cedar evaluation is local. Forbid policy IDs, permit policy IDs, and reasons are included in the signed decision record. The optional dependency is pinned to cedarpy==4.8.7 for deterministic engine semantics; cedarpy is Rust-backed but community-maintained and is not an official AWS/Cedar Team binding.
Native OPA policy
Obsigno calls OPA's Data API and expects either a boolean result or this object:
{
"result": {
"allow": true,
"reason": "read-only and research tools are allowed",
"rule_id": "allow-read-research",
"policy_ref": "opa:obsigno-example:v1"
}
}
Start the pinned OPA 1.19.0 sidecar with the included Rego policy:
docker compose --profile opa up -d opa
curl http://127.0.0.1:8181/health
The development profile binds OPA only to loopback because its server also exposes policy-management APIs. Then point the proxy at the decision endpoint:
obsigno-mcp \
--policy-backend opa \
--opa-url http://127.0.0.1:8181/v1/data/obsigno/decision \
--server-id filesystem-mcp \
-- npx -y @modelcontextprotocol/server-filesystem /safe/root
OPA receives:
{
"input": {
"actor": "agent://researcher",
"tool": "read_file",
"arguments": {"path": "/safe/root/report.csv"}
}
}
OPA network failures, malformed responses, responses without a boolean allow, and other backend errors fail closed. OPA can return policy_ref as a bundle revision, Git commit, or other deployment identifier; Obsigno signs that reference into the event. Built-in JSON and Cedar policies automatically use sha256:<digest> of the exact local policy bytes.
Correlate one agent task across MCP servers
Supply the same trace ID in MCP's extensible _meta object to every tool call in a task:
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {"path": "/workspace/brief.md"},
"_meta": {
"obsigno/trace_id": "task-research-2026-08-03"
}
}
}
Obsigno preserves that trace ID, adds obsigno/server_id, forwards the metadata, and signs both fields into the ledger. Configure each proxy with a distinct stable --server-id.
If the client does not provide a trace ID, Obsigno creates a UUID for that individual call. Task-level correlation therefore requires the client/orchestrator to propagate the same ID across calls. The dashboard groups signed records by trace ID and displays every server touched by that task.
The ledger serializes appends with both in-process and OS-level file locks, so separate proxy processes can safely extend one local chain. For distributed hosts, use a single append service or a storage backend that provides equivalent atomic ordering.
Dashboard and audit API
Run against local files:
obsigno-dashboard \
--host 127.0.0.1 \
--port 8787 \
--ledger .obsigno/audit.jsonl \
--private-key .obsigno/private_key.pem \
--public-key .obsigno/public_key.pem
The CLI initializes missing keys and an empty ledger. API endpoints:
| Endpoint | Description |
|---|---|
GET /api/health |
Service status, entry count, and live integrity result |
GET /api/events |
Newest signed events; supports trace_id, decision, server_id, and limit |
GET /api/traces |
Correlated trace summaries with servers and deny counts |
GET /api/traces/{trace_id}/evidence |
Portable offline-verifiable trace bundle |
GET /api/verify |
Full chain/signature verification details |
GET / |
Auto-refreshing self-hosted dashboard |
Example:
curl "http://localhost:8787/api/events?decision=deny&limit=20"
curl "http://localhost:8787/api/traces?limit=20"
Drop-in Claude Desktop-style configuration on Windows
Replace the original server command with obsigno-mcp.exe, then append the original command after --:
{
"mcpServers": {
"governed-filesystem": {
"command": "C:\\path\\to\\obsigno\\.venv\\Scripts\\obsigno-mcp.exe",
"args": [
"--policy-backend",
"cedar",
"--policy",
"C:\\path\\to\\obsigno\\policies\\policy.example.cedar",
"--server-id",
"filesystem-mcp",
"--ledger",
"C:\\path\\to\\obsigno\\.obsigno\\audit.jsonl",
"--",
"npx",
"-y",
"@modelcontextprotocol/server-filesystem",
"C:\\safe\\root"
]
}
}
}
Verify the evidence offline
obsigno-verify .obsigno/audit.jsonl .obsigno/public_key.pem
# OK 6 entries verified, chain + signatures intact
Export one trace as a portable evidence bundle and verify it without Obsigno running:
obsigno-evidence export \
--ledger .obsigno/audit.jsonl \
--public-key .obsigno/public_key.pem \
--trace-id task-research-2026-08-03 \
--policy-file policies/policy.example.cedar \
--output task-research.evidence.json
obsigno-evidence verify task-research.evidence.json \
--require-policy-snapshots
Repeat --policy-file when a trace used multiple local policies. Obsigno embeds only snapshots whose SHA-256 matches a signed policy_ref; strict verification fails if a required local snapshot is missing or changed. OPA references such as bundle revisions remain externally identified unless the exact policy bundle is supplied through a future attestation integration.
A linear hash chain needs every predecessor through the final selected event, so a trace bundle contains the complete ledger prefix up to that point plus trace_event_seqs. It does not contain raw arguments or results, but it can expose metadata for unrelated events in that prefix. Review that trade-off before sharing a bundle externally. Pin and compare the reported public-key fingerprint when the verifier receives the key through an untrusted channel.
Each signed record includes:
- actor, action, tool, timestamp, and sequence;
- trace ID and MCP server identity;
- allow/deny decision, reason, matched rule ID, and policy version/hash reference;
- SHA-256 hashes of arguments and results;
- previous-entry hash, record hash, and Ed25519 signature.
Raw tool arguments and results are not written to the ledger. This reduces accidental retention of prompts, PII, secrets, and customer payloads while still proving whether supplied evidence matches the original event.
Python API
from obsigno import AuditLedger, Gateway, PolicyEngine, Rule, generate_keypair
policy = PolicyEngine([
Rule(id="reads", effect="allow", tool="read_file", reason="reads allowed"),
])
ledger = AuditLedger("audit.jsonl", generate_keypair())
gateway = Gateway(policy, ledger)
result = gateway.call("agent://bot", "read_file", read_file, {"path": "/a.txt"})
Run python demo.py for a local allow/deny and tamper-detection demonstration.
Security boundary
Obsigno is unbypassable only when the MCP client can reach the target server exclusively through the proxy and cannot launch or connect to it separately.
For production:
- Keep the private signing key outside the agent's permissions.
- Restrict direct network/process access to governed MCP servers.
- Export signed ledgers to append-only/WORM or object-lock storage.
- Pin dependencies and container images.
- Authenticate and authorize access to the dashboard/API; the Stage-0 dashboard intentionally has no built-in identity layer.
- Monitor OPA availability; OPA decisions fail closed by design.
This repository provides tamper evidence, not absolute prevention of deletion. Loss of the only ledger copy is still loss of evidence.
Compliance positioning
Obsigno can supply technical logging evidence for governance programs such as EU AI Act Article 12 record-keeping, SOC 2 controls, and ISO/IEC 42001 processes. It does not make a system compliant by itself and is not legal advice.
Article 12 requires high-risk AI systems to technically allow automatic event recording over the system lifetime. The Act does not specifically mandate MCP logging, and “tamper-evident” is a defensible engineering practice rather than verbatim Article 12 language. Applicability, retention, and required fields depend on the organization's role and system classification.
Development
pytest -q
uv build
docker compose config
docker compose up -d --build
Repository layout:
src/obsigno/mcp_proxy.py transparent MCP enforcement proxy
src/obsigno/demo_proxy.py real proxied Compose demo and upstream
src/obsigno/policy_backends.py native Cedar and OPA adapters
src/obsigno/ledger.py signed append-only ledger
src/obsigno/verify.py offline full-ledger verifier
src/obsigno/evidence.py portable trace evidence export/verifier
src/obsigno/web.py FastAPI audit and evidence API
src/obsigno/static/ live dashboard
policies/ Cedar and Rego examples
landing/ product landing page
tests/ unit and end-to-end tests
License
Apache-2.0 intended. The proxy, policy gate, ledger, verifier, API, and dashboard are designed to remain genuinely useful when self-hosted.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file obsigno-0.3.0.tar.gz.
File metadata
- Download URL: obsigno-0.3.0.tar.gz
- Upload date:
- Size: 53.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25ee480c0e81659ce8d0053c5e2e95bf366839d7fe280ba22c2eae7580cea614
|
|
| MD5 |
9279d9ab067f8cdce2856bc96dc11f35
|
|
| BLAKE2b-256 |
2d442b1ad945066093b3549686a3d7b6a17639b7d2d653f33577b5ee2e59e903
|
File details
Details for the file obsigno-0.3.0-py3-none-any.whl.
File metadata
- Download URL: obsigno-0.3.0-py3-none-any.whl
- Upload date:
- Size: 38.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
587c8e2ebc859c38908aba49390d1ea79638529e50a6f01ffcf70b268335adfe
|
|
| MD5 |
b54fe7e284acf2bbb817399fa24dd754
|
|
| BLAKE2b-256 |
216bb12dfdadf9f5d1b697d0f228a7f28b501eb1862b91f401c2822663b262ee
|