Skip to content

Latest commit

 

History

108 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image
█▀▀ █▀▀ █▀█▀█ █  
▀▀▓ ▓░  █   ▓ ▓░ 
▀▀▀ ▀▀▀ ▀   ▀ ▀▀▀

SCML — Securing Agentic AI at the Middleware Layer






Trust-aware context mediation for LLM agents, RAG pipelines, and tool-calling systems.

CI Tests Python TypeScript License


The Problem

  ┌─────────────────────────────────────────────────────────────────────┐
  │  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.


What SCML Does

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.

Multi-Tenant Policy Namespaces

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
  • CallerDep derives the tenant from the authenticated key; no request model accepts a tenant_id field, 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 same agent_id.
  • A tenant that enrols no policy is deny-all (fail-closed at the tenant boundary); only the default tenant falls back to the gateway YAML.
  • Pre-tenancy deployments are unchanged: an unqualified key resolves to the default tenant, exactly as before.

Install

pip install trust-mediator            # client SDK — 14 packages, ~32 MB
pip install "trust-mediator[server]"  # run the mediator yourself

Python SDK

from 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 closed

TypeScript SDK (zero runtime deps)

const { 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);

MCP Server (Claude Desktop / Cursor / Windsurf)

MCP Server

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    │
                         └──────────────┘

Install

pip install trust-mediator mcp

Run

SCML_URL=http://localhost:8000 SCML_API_KEY=sk-your-key \
  python -m trust_mediator.mcp.server

Claude Desktop config

Add 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": ""
      }
    }
  }
}

Available MCP tools

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

Benchmarks

SCML is evaluated against four published attack corpora with 2,100+ attack cases. Every number is reproducible from a committed result file.

InjecAgent — 1,054 third-party cases (ACL Findings 2024)

KPIMeasuredTarget
Injection ASR0.0%< 5%
ASR reduction vs undefended100.0%≥ 90%
False-positive rate0.0%< 3%
Utility100.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.

AgentDojo — All Four Suites (949 attacked cases)

MetricUndefendedSCMLChange
Attack Success Rate29.0%7.3%75% reduction
Benign Utility74.2%59.8%81% retained
SuiteUndefendedSCMLBenign Retained
banking49.3%0.0%75%
workspace16.8%4.8%94%
travel30.7%7.1%64%
slack63.8%30.5%71%

Tool-Output Sanitizer — Closing the Slack Gap (FR-OR-03)

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.

ArmASRBenign Utility
SCML33.3%52.4%
SCML + --sanitize0.0%52.4%

105 attacked cases (21 tasks × 5 injections), 122 injected spans stripped, zero benign utility cost.

Memory Poisoning (48 in-house cases)

Harm VectorCasesHarmful After Defence
tool130
control-bypass220
output51
informational8no 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.


Quick Start

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 passed

Interactive docs: http://localhost:8000/docs

Docker Compose

bash deploy.sh    # builds image, starts Postgres + Redis + mediator

Dashboard

bash run-demo.sh          # mediator :8000, dashboard :3100, agents :4000/:4100
bash run-demo.sh --stop   # tear down
PageURLShows
Command Centerlocalhost:3100/index.htmlDecisions, latency, audit trail
Live Agent Demolocalhost:3100/demo.htmlEvery decision as it lands
Tool Policieslocalhost:3100/policy.htmlPer-agent allow-lists, version history
Memory Integritylocalhost:3100/memory.htmlQuarantined writes, integrity scores
Audit Logslocalhost:3100/audit.htmlFull replay with hash-chain verification

Architecture

  ┌─────────────────────────────────────────────────────────────────────────┐
  │                        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) │                             │
  │                    └─────────────────────┘                              │
  └─────────────────────────────────────────────────────────────────────────┘
LayerWhat It DoesPRD
IngressInterceptorClassifies input provenance§5.1
TrustRouterLabels data, propagates taint§5.2
InjectionScannerHeuristic + pluggable ML classifier§6.3
ToolPolicyEngineDeclarative allow-list, rate limits, approval gates§7
MemoryIntegrityLayerQuarantine → score → persist/reject§8
OutputRedactorPII, secrets, entropy detection§9
AuditLoggerSHA-256 tamper-evident hash chain, Kafka fan-out§10
PolicyStoreVersioned YAML policy, hot-reload§11

All config via TRUST_MEDIATOR_* env vars — nothing hardcoded. Every decision emits an AuditEvent.


API Surface

MethodEndpointDescription
POST/v1/mediate/contextLabel + scan retrieved content
POST/v1/mediate/tool-callAuthorise a proposed tool call
POST/v1/mediate/memory/writeVet a memory write
POST/v1/mediate/memory/readVerify a memory read
POST/v1/mediate/outputRedact + authorise outbound response
GET/v1/audit/replay/{sessionId}Full session decision trail
GET/v1/policyRead active policy
PUT/v1/policyUpdate policy

Production Deployment

Full integration guide: INTEGRATION.md — Python SDK, TypeScript SDK, LangChain guard, HTTP API, embedded mode, and examples for CrewAI, LangGraph, and OpenAI function calling.

ConcernMechanism
Kubernetesk8s/ — gateway + sidecar, HPA, PDB
Rate LimitingRedis-backed cluster-wide (REDIS_URL) or in-process
gRPCTRUST_MEDIATOR_GRPC_ENABLED=true (port 50051)
Audit PipelineKafka fan-out + SIEM webhook; DB chain authoritative
TLS / mTLSTRUST_MEDIATOR_TLS_* env vars, production refuses plaintext
AuthTRUST_MEDIATOR_API_KEYS / _FILE, HMAC-compared, live-rotatable
CIruff + pytest 3.11/3.12 with test-count floor + Docker build smoke

Tests

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

Apache License 2.0 — includes patent grant and defensive termination.

GitHub

About

Production middleware for securing agentic AI systems. Enforces least-agency policy, scans for prompt injection, gates tool calls, and logs every decision to a tamper-evident SHA-256 chain. Evaluated against InjecAgent (1,054 cases), AgentDojo (949 cases), and memory poisoning (48 cases). Python + TypeScript SDKs.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages