System Design
Architecture
How Mithril's governance pipeline works — from incoming claim to admission decision. Every component is independent, testable, and contributes a distinct trust signal.
Governance Pipeline
Incoming Claim (MCP tool · Slack export · file · API · dashboard)
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 1. Credential Exfiltration Guard (secrets.py) │
│ Regex-based scanner (AWS keys, GitHub tokens, JWTs, │
│ PEM blocks, DB URIs, Bearer tokens, key=value creds). │
│ Redacts BEFORE anything else — Cognee, LLM, audit log │
│ never see the raw value. │
│ Credential planting penalizes source reputation. │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 2. Adaptive Source Reputation (reputation.py) │
│ SQLite-backed, self-adjusting EWMA. │
│ Starts from configured priors (Security Policy: 0.98, │
│ Slack: 0.60, Unknown Agent: 0.30, etc.). │
│ PENALTY_RATE = 0.22 (fast) · REWARD_RATE = 0.10 (slow) │
│ Trust is easy to lose, hard to earn. │
│ Weight: source_rep × 0.40 │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 3. Contradiction Detection (memory_analysis.py) │
│ cognee.recall(only_context=True) pulls verified context. │
│ LLM scores contradiction 0.0 → 1.0 (<score> tags). │
│ Threshold: score > 0.3 = contradiction found. │
│ Corroboration: extra aligned chunks = bonus (max 3). │
│ Also scores standalone content danger before admission. │
│ Weights: contradiction × -0.40, corroboration × 0.25 │
│ content_danger × -0.35 │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 4. Trust Scorer (scorer.py) │
│ raw = src×0.40 + corroboration×0.25 + freshness×0.05 │
│ - contradiction×0.40 - content_danger×0.35 │
│ final_score = raw / max_theoretical_score │
│ Normalized to [0, 1] so thresholds apply cleanly. │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 5. Admission Gate (gate.py) │
│ Pure function: maps final_score → AdmissionStatus. │
│ ≥ 0.85 → Accept ≥ 0.60 → Warn ≥ 0.40 → Review │
│ ≥ 0.20 → Quarantine < 0.20 → Reject │
└──────────────────────────────────────────────────────────────┘
│
├── ACCEPT / WARN → cognee.remember(node_set=["verified"])
├── QUARANTINE / REVIEW / REJECT → SQLite quarantine store
├── Source reputation updated (EWMA) from the outcome
└── All decisions → Audit log (SQLite)Trust Score Formula
The trust score is a weighted sum of five independent signals, then normalized to [0, 1]:
raw = source_reputation × 0.40
+ corroboration × 0.25
+ freshness × 0.05
- contradiction × 0.40
- content_danger × 0.35
final_score = clamp(raw / max_theoretical_score, 0, 1)Source Reputation (×0.40)
Corroboration (×0.25)
Freshness (×0.05)
Contradiction (×-0.40)
Content Danger (×-0.35)
Credential Exfiltration Guard
Memory poisoning has a mirror-image threat: credential planting. An agent or poisoned message writes an API key into shared memory. Once it's in the graph, every future agent with recall access can read it — a durable data leak.
Mithril treats this as an attack on the trust system, not just a privacy scrub:
Redact at Ingest
Penalize the Source
Record It
Detected Credential Types
| Kind | Pattern |
|---|---|
| Private Key PEM | -----BEGIN ... PRIVATE KEY----- |
| AWS Access Key | AKIA / ASIA + 16 alphanumeric |
| GitHub Token | ghp_, gho_, ghu_, ghs_, ghr_ + 36+ chars |
| Slack Token | xoxb-, xoxp-, xoxa- + 10+ chars |
| Google API Key | AIza + 35 chars |
| OpenAI / SK Key | sk- (with or-, proj-, ant- prefix) |
| Stripe Key | sk_live_, rk_test_ + 16+ chars |
| JSON Web Token | eyJ...header.payload.signature |
| DB URI Credentials | ://user:password@ in connection URIs |
| Bearer Token | Bearer + 16+ char token |
| Credential Assignment | password=, api_key=, secret= values |
Adaptive Source Reputation
Source reputation is not a static lookup table. It starts from configured priors and moves with every decision using a bounded exponential weighted moving average (EWMA):
# Trust earned per good outcome (slow to build)
REWARD_RATE = 0.10
# Trust lost per bad outcome (fast to lose)
PENALTY_RATE = 0.22
# On a good decision (accept/warn, no contradiction):
new_rep = clamp(current + 0.10 × (1.0 - current))
# On a bad decision (quarantine/reject, or contradiction found):
new_rep = clamp(current + 0.22 × (0.0 - current))
# Floor: 0.05 — even the worst source can still submit
# Ceiling: 0.99 — perfection is never reachedThis means a Slack channel at 0.60 reputation drops to ~0.47 after one blocked claim, and to ~0.36 after two. Recovering back to 0.60 requires many consecutive accepted claims. The asymmetry is intentional — it mirrors how trust works in the real world.
Cognee Integration
Mithril uses specific Cognee APIs to create a separation between verified and quarantined memory:
cognee.remember()
node_set=["verified", source_name] into the verified_memories dataset. Warned claims additionally get the low_confidence tag.cognee.recall(only_context=True)
cognee.recall(node_name=["verified"])
cognee.improve()
firewall.improve().Three Entry Points, One Gate
No matter how a claim enters Mithril, it passes through the same governance pipeline:
| Surface | What it is | Entry point |
|---|---|---|
| MCP Server | Agents call mithril_remember / mithril_recall over stdio | mcp_server/server.py |
| Ingestion Connector | Parse Slack export or .txt/.jsonl file and run every message through the gate | mithril/ingest.py |
| REST API + Dashboard | FastAPI backend + Next.js provenance UI | api/main.py |
Project Layout
mithril/ Core package
├── firewall.py Main Mithril class — chains the full pipeline
├── scorer.py Weighted trust score formula
├── gate.py Admission gate (score → status)
├── reputation.py Adaptive source reputation (SQLite EWMA)
├── memory_analysis.py Contradiction detection + corroboration
├── secrets.py Credential exfiltration guard (11 patterns)
├── ingest.py Slack export / file ingestion connectors
├── audit.py SQLite audit log
├── quarantine.py SQLite quarantine store
├── models.py Shared dataclasses and enums
├── config.py Weights, thresholds, source priors
└── utils.py Recall text extraction helpers
mcp_server/ MCP server — exposes 4 tools to agents
api/ FastAPI backend (9 endpoints)
benchmark/ Labeled memory-poisoning benchmark
demo/ Attack demo, vanilla comparison, ingestion demo
tests/ Unit + integration tests
ui/ Next.js landing page + provenance dashboard