█▀▀ █▀▀ █▀█▀█ █
▀▀▓ ▓░ █ ▓ ▓░
▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀
Trust-aware context mediation for LLM agents, RAG pipelines, and tool-calling systems.
┌─────────────────────────────────────────────────────────────────────┐
│ LLM agents call tools, read documents, store memories. │
│ Every integration is a new attack surface. │
│ │
│ • Poisoned memory → privilege escalation │
│ • Malicious tool out → agent hijacked, data exfiltrated │
│ • Prompt injection → agent instructions overridden │
│ │
│ Existing guardrails secure the MODEL. │
│ SCML secures the MIDDLEWARE. │
└─────────────────────────────────────────────────────────────────────┘
LLM agents call external tools, read untrusted documents, and make decisions with real-world consequences. Every integration is a new attack surface. Existing guardrails focus on the model layer. SCML sits one layer lower — the middleware between the agent and the world — where policy is enforced before any tool call is authorised, before any memory write is persisted, and before any outbound response leaves the process.
SCML is a trust-aware context mediation middleware. Every data path passes through a pipeline that labels data, scans for injection, enforces declarative per-agent policy, redacts sensitive content, and logs every decision to a tamper-evident SHA-256 hash chain.
SCML Mediation Pipeline
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Trust │──▶│ Injection│──▶│ Policy │──▶│ Memory │──▶│ Output │──▶│ Audit │
│ Label │ │ Scan │ │ Gate │ │ Check │ │ Redact │ │ Log │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
Propagate Detect & Allow / Deny Quarantine Strip PII SHA-256
taint block inject per-agent & score & secrets hash chain
Side-effect operations fail closed: an unreachable mediator denies by default, never permits by silence.
One SCML instance can serve many companies, each with its own policy document. The tenant is bound to the API key, never supplied by the client:
| Key format | Tenant | Principal |
|---|---|---|
sk-abc123 |
default (TRUST_MEDIATOR_DEFAULT_TENANT) |
fingerprint |
alice:sk-abc123 |
default |
alice |
acme@sk-abc123 |
acme |
fingerprint |
acme@ops:sk-abc123 |
acme |
ops |
CallerDepderives the tenant from the authenticated key; no request model accepts atenant_idfield, so a caller cannot claim another company's namespace by shaping the body.- Policy reads, writes, per-agent updates, version history and rollback are all
scoped to
caller.tenant— Acme's agents and versions never collide with Globex's, even for the sameagent_id. - A tenant that enrols no policy is deny-all (fail-closed at the tenant
boundary); only the
defaulttenant falls back to the gateway YAML. - Pre-tenancy deployments are unchanged: an unqualified key resolves to the
defaulttenant, exactly as before.
pip install trust-mediator # client SDK — 14 packages, ~32 MB
pip install "trust-mediator[server]" # run the mediator yourselffrom scml import SCMLClient
scml = SCMLClient("http://localhost:8000", api_key="sk-...")
# Label and scan untrusted content
ctx = scml.mediate_context(session_id="s1", content=untrusted_document)
# Gate a tool call — untrusted arguments are denied, trusted ones pass
decision = scml.mediate_tool_call(
session_id="s1",
tool_name="send_email",
arguments={"to": ctx.parsed["recipient"]},
argument_trust_labels={"to": ctx.trust_label},
)
if not decision.allowed:
raise RuntimeError(decision.reason) # fail closedconst { SCML } = require('scml-client');
const scml = new SCML({ url: process.env.SCML_URL });
const ctx = await scml.mediateContext({ sessionId, content: untrustedDoc });
const d = await scml.mediateToolCall({
sessionId, tool: 'send_email',
arguments: { to: ctx.parsed?.recipient },
argumentTrustLabels: { to: ctx.trustLabel ?? 'untrusted_data' },
});
if (!d.allowed) throw new Error(d.reason);SCML exposes its full security pipeline as MCP tools — any MCP-compatible client gets tool-call authorization, injection scanning, memory quarantine, and PII redaction with zero code changes.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ LLM Client │──────▶│ SCML MCP │──────▶│ Your Tool │
│ (Claude, │ │ Server │ │ (API, DB, │
│ Cursor) │◀──────│ │◀──────│ file, etc) │
└──────────────┘ │ • authorize │ └──────────────┘
│ • scan │
│ • quarantine│
│ • redact │
└──────────────┘
pip install trust-mediator mcpSCML_URL=http://localhost:8000 SCML_API_KEY=sk-your-key \
python -m trust_mediator.mcp.serverAdd to ~/.claude/claude_desktop_config.json:
{
"mcpServers": {
"scml": {
"command": "python",
"args": ["-m", "trust_mediator.mcp.server"],
"env": {
"SCML_URL": "http://localhost:8000",
"SCML_API_KEY": ""
}
}
}
}| Tool | What it does |
|---|---|
authorize_tool_call |
Check if a tool call is allowed by policy |
scan_content |
Detect prompt injection in external content |
check_memory_write |
Score a memory write for integrity |
redact_output |
Strip PII and secrets from responses |
get_audit_trail |
Replay session decisions with hash chain |
get_policy |
Show active security policy |
health_check |
Verify SCML is running |
SCML is evaluated against four published attack corpora with 2,100+ attack cases. Every number is reproducible from a committed result file.
| KPI | Measured | Target | |
|---|---|---|---|
| Injection ASR | 0.0% | < 5% | ✅ |
| ASR reduction vs undefended | 100.0% | ≥ 90% | ✅ |
| False-positive rate | 0.0% | < 3% | ✅ |
| Utility | 100.0% | ≥ 90% | ✅ |
| Latency (p95) | 0.4 ms | < 400 ms | ✅ |
Ablation truth: Removing the scanner → 0.0% ASR. Removing tool policy → 100.0% ASR. Tool policy is the entire defence. The scanner detects 0 of 1,054 attacks.
| Metric | Undefended | SCML | Change |
|---|---|---|---|
| Attack Success Rate | 29.0% | 7.3% | 75% reduction |
| Benign Utility | 74.2% | 59.8% | 81% retained |
| Suite | Undefended | SCML | Benign Retained |
|---|---|---|---|
banking | 49.3% | 0.0% | 75% |
workspace | 16.8% | 4.8% | 94% |
travel | 30.7% | 7.1% | 64% |
slack | 63.8% | 30.5% | 71% |
The slack gap (30.5% ASR) is caused by attacks delivered inside tool results that the model rewrites rather than copies. The deterministic ToolOutputSanitizer strips instruction framing before the agent reads them — no LLM call, no new dependencies.
| Arm | ASR | Benign Utility |
|---|---|---|
| SCML | 33.3% | 52.4% |
SCML + --sanitize | 0.0% | 52.4% |
105 attacked cases (21 tasks × 5 injections), 122 injected spans stripped, zero benign utility cost.
| Harm Vector | Cases | Harmful After Defence |
|---|---|---|
tool | 13 | 0 |
control-bypass | 22 | 0 |
output | 5 | 1 |
informational | 8 | no mediator gate |
Pessimistic harm total: 18.8% (9/48). The 22 control-bypass cases are inert because the mediator never reads agent memory to decide authorisation.
git clone https://github.com/ravindu57/SCML.git && cd SCML
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
DATABASE_URL="" REDIS_URL="" TRUST_MEDIATOR_API_KEYS="" \
.venv/bin/uvicorn trust_mediator.api.app:app --port 8000 &
sleep 2
bash demo.sh # run the demo
.venv/bin/pytest tests/ -q # 614 passed
cd clients/typescript && npm test # 25 passedInteractive docs: http://localhost:8000/docs
bash deploy.sh # builds image, starts Postgres + Redis + mediatorbash run-demo.sh # mediator :8000, dashboard :3100, agents :4000/:4100
bash run-demo.sh --stop # tear down| Page | URL | Shows |
|---|---|---|
| Command Center | localhost:3100/index.html | Decisions, latency, audit trail |
| Live Agent Demo | localhost:3100/demo.html | Every decision as it lands |
| Tool Policies | localhost:3100/policy.html | Per-agent allow-lists, version history |
| Memory Integrity | localhost:3100/memory.html | Quarantined writes, integrity scores |
| Audit Logs | localhost:3100/audit.html | Full replay with hash-chain verification |
┌─────────────────────────────────────────────────────────────────────────┐
│ SCML System Architecture │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Python │ │ TypeScript │ │ gRPC │ │
│ │ SDK │ │ SDK │ │ Client │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ FastAPI │ /v1/mediate/* │
│ │ + gRPC │ /v1/audit/* │
│ └──────┬──────┘ │
│ │ │
│ ┌───────────────────────────▼───────────────────────────┐ │
│ │ Mediation Pipeline │ │
│ │ │ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ Trust │→│Inject. │→│ Policy │→│Memory │ │ │
│ │ │ Router │ │Scanner │ │ Engine │ │Integrity│ │ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │
│ │ │ │ │ │
│ │ └────────────┬───────────────┘ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Output │ │ Audit │ │ │
│ │ │ Redactor │ │ Logger │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ PostgreSQL / SQLite│ │
│ │ + Redis (optional) │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
| Layer | What It Does | PRD |
|---|---|---|
IngressInterceptor | Classifies input provenance | §5.1 |
TrustRouter | Labels data, propagates taint | §5.2 |
InjectionScanner | Heuristic + pluggable ML classifier | §6.3 |
ToolPolicyEngine | Declarative allow-list, rate limits, approval gates | §7 |
MemoryIntegrityLayer | Quarantine → score → persist/reject | §8 |
OutputRedactor | PII, secrets, entropy detection | §9 |
AuditLogger | SHA-256 tamper-evident hash chain, Kafka fan-out | §10 |
PolicyStore | Versioned YAML policy, hot-reload | §11 |
All config via TRUST_MEDIATOR_* env vars — nothing hardcoded. Every decision emits an AuditEvent.
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/mediate/context | Label + scan retrieved content |
POST | /v1/mediate/tool-call | Authorise a proposed tool call |
POST | /v1/mediate/memory/write | Vet a memory write |
POST | /v1/mediate/memory/read | Verify a memory read |
POST | /v1/mediate/output | Redact + authorise outbound response |
GET | /v1/audit/replay/{sessionId} | Full session decision trail |
GET | /v1/policy | Read active policy |
PUT | /v1/policy | Update policy |
Full integration guide:
INTEGRATION.md— Python SDK, TypeScript SDK, LangChain guard, HTTP API, embedded mode, and examples for CrewAI, LangGraph, and OpenAI function calling.
| Concern | Mechanism |
|---|---|
| Kubernetes | k8s/ — gateway + sidecar, HPA, PDB |
| Rate Limiting | Redis-backed cluster-wide (REDIS_URL) or in-process |
| gRPC | TRUST_MEDIATOR_GRPC_ENABLED=true (port 50051) |
| Audit Pipeline | Kafka fan-out + SIEM webhook; DB chain authoritative |
| TLS / mTLS | TRUST_MEDIATOR_TLS_* env vars, production refuses plaintext |
| Auth | TRUST_MEDIATOR_API_KEYS / _FILE, HMAC-compared, live-rotatable |
| CI | ruff + pytest 3.11/3.12 with test-count floor + Docker build smoke |
DATABASE_URL="" TRUST_MEDIATOR_ENV=development REDIS_URL="" TRUST_MEDIATOR_API_KEYS="" \
.venv/bin/pytest tests/ -q # 614 passed
cd clients/typescript && npm test # 25 passed