One policy layer for every AI model.
Protect prompts, model responses, tool calls, retrieval content, logs, tickets and traces with provider-neutral privacy and AI governance controls for .NET.
- Provider-neutral
dotnet add package LLMPolicyMesh --version 0.6.0-beta.3
The trust boundary
Application
Owns clients and storage
LLMPolicyMesh
- Inspect
- Redact
- Limit
- Route
- Audit
Any AI model
Any provider or endpoint
One immutable policy snapshot is resolved per logical operation and applied to the primary attempt and to every permitted retry or fallback.
The operating problem
Everything that reaches a model is untrusted, and everything that leaves one is too.
Prompts, support tickets, logs, traces, retrieved documents, model responses and tool results can all carry personal data, credentials or attacker-controlled instructions. Meanwhile model SDKs expose different request schemas, and enterprise controls need to behave the same way across all of them.
Untrusted AI traffic
- Prompts and support tickets
- Tool results
- Retrieved documents
- Model output
- Logs and traces
Any of which can contain:
Enterprise requirements
- Privacy
- Security
- Cost control
- Auditability
- Provider independence
Retries and fallbacks can multiply cost or bypass tenant budgets unless every attempt shares one logical policy and accounting boundary. Audit destinations need sanitized evidence without forcing a specific database, logging framework or cloud service on every consumer.
One governed access layer for every model
Use commercial, cloud-hosted, local, self-hosted or custom AI models through one provider-neutral policy boundary. If your application can reach a model through a compatible client or HTTP endpoint, LLMPolicyMesh can govern what enters and leaves that interaction.
Provider freedom without fragmented controls
- Protect provider clients through standard .NET AI integration
- Govern vendor-specific and custom HTTP payloads
- Apply consistent policy across primary and fallback models
- Keep credentials, endpoints and provider choice under application control
- Change providers without rebuilding the governance layer
How it works
Three stages, one policy boundary
The gateway resolves one immutable policy snapshot for a logical operation, then applies the same protection across the primary attempt and every permitted retry or fallback.
-
Untrusted input
Prompts, retrieved chunks, tools, logs, tickets and model payloads enter the policy boundary.
-
Policy mesh
LLMPolicyMesh applies, in order:
- Input limits
- PII and secret detection
- Redaction
- Prompt-injection inspection
- Retrieval validation
- Tool policies
- Budget reservation
- Retry and routing policy
- Audit and telemetry
-
Controlled output
Only protected requests and sanitized responses are released. Limits, tool policy, optional injection analysis, custom rules and privacy redaction are reapplied on the way back.
The logical request sequence
| Step | Stage | Enforcement |
|---|---|---|
| 1 | Select policy | Use the default, an explicit named policy, or content-free tenant selection. |
| 2 | Bound input | Enforce input bytes, characters, messages, content items and optional application token counts. |
| 3 | Inspect tools | Validate declarations, explicit choices, call counts, arguments and tool results. |
| 4 | Inspect normalized view | Decode supported evasions for inspection only; outbound content is never replaced by decoded text. |
| 5 | Protect privacy | Apply application PII/NER, custom rules and built-in structured redaction before dispatch. |
| 6 | Price and reserve | Resolve price, enforce per-request cost and reserve tenant capacity atomically. |
| 7 | Dispatch | Send through the application-owned HttpClient or IChatClient and emit sanitized operational evidence. |
| 8 | Settle | Use provider usage or conservative bounded estimates; ambiguous failures are charged according to configured policy. |
| 9 | Protect output | Reapply limits, tool policy, optional injection analysis, custom rules and privacy redaction. |
| 10 | Return safely | Audit the outcome and release only protected response content. |
Six control planes
- Privacy — detect and transform PII and secrets across text, structured payloads, logs, requests, responses and streams.
- Policy — normalize inspection copies, detect injection and evasion patterns, protect retrieval chunks, enforce limits, run application rules.
- Tool security — allow or deny tools, constrain arguments, inspect encoded values, sanitize indirect instructions.
- Economics — price requests, enforce maximum spend, reserve tenant budgets atomically, settle from actual or conservative usage.
- Resilience — bounded retry, timeout, circuit breaker and explicit fallback routes without weakening policy.
- Evidence — sanitized audit records, privacy-safe telemetry and optional tamper-evident ledgers.
Core capabilities
Eighteen controls, one consolidated package
All shipping functionality is delivered as a single NuGet package. There are no separate privacy, gateway, audit, logging, HTTP or ASP.NET integration packages to discover and keep in step.
Implemented in reviewed source
PII and secret protection
Detection and redaction across identity, financial, network and credential data, with configurable precision and optional country coverage.
Learn more about the privacy enginePrompt-injection and evasion inspection
Bounded multi-pass normalization creates inspection variants for Unicode tricks, invisible characters, encodings and reversed text. Variants are inspected, never substituted.
Retrieval and RAG guardrails
Shape validation, chunk bounds and score policy for retrieved content. Malformed score shapes and duplicate properties fail closed.
Learn more about retrieval checksTool allow/deny policy
Deny rules override allow rules. Unlisted tools can be rejected outright, and declarations and calls are count-bounded.
Tool-result protection
Per-tool risk levels drive indirect-instruction inspection, with reject, sanitize or replace outcomes.
Request and tenant budgets
Atomic reservations compare committed plus outstanding cost against hourly, daily or monthly UTC limits.
Learn more about budgetsRetry and fallback routing
Only configured transient statuses, transport failures, attempt timeouts and classified exceptions are retried. Each fallback route gets full policy.
Circuit breaking and timeouts
Endpoint-local closed, open and half-open states prevent work and spending on known-open routes. Attempt and total timeouts are bounded.
Safe streaming redaction
Complete logical output is buffered, bounded and redacted before release, so a sensitive value split across chunks cannot leak early.
Sanitized auditing
Gateway and HTTP-handler factories place the package sanitizer before your sinks, so evidence never becomes a second privacy leak.
Learn more about auditingMicrosoft logging
A privacy-safe ILogger wrapper redacts formatted messages, structured values, scopes and exception text.
ASP.NET Core middleware
Incoming request and outgoing response bodies are protected through the shared framework. Available on net8.0.
Outbound HTTP handler
A DelegatingHandler protects arbitrary textual outbound HTTP request and response bodies.
Microsoft.Extensions.AI integration
Wrap any application-created IChatClient to protect messages, instructions, functions, results, responses and streaming updates.
Custom policy rules
Ordered input and output policy rules with predicate validation enforce application-specific contracts.
Named tenant policies
Immutable policy snapshots can be selected explicitly or through content-free tenant metadata.
Remote and local PII analyzer contracts
Public contracts for remote PII analyzers, prompt-injection analyzers, selectors and token counters, with thresholds and explicit fail-open or fail-closed behaviour.
Privacy-safe telemetry
ActivitySource and Meter emit policy and rule outcomes, latency and counts without payload content.
Privacy engine
PII and secret protection before data reaches the model
The privacy subsystem is designed for ordinary text and structured payloads alike, with configurable precision, opt-in country packs and both reversible and irreversible operators.
Implemented coverage
Identity and contact
Financial
Network and device
Credentials and secrets
Country packs
Generic and US coverage is always available. The United Kingdom, Germany, India, Italy and Spain packs are opt-in.
Transformations
- Replace — substitute a redaction token.
- Remove — drop the value entirely.
- Mask — preserve edges for readability.
- Deterministic HMAC — keyed, stable pseudonyms for joins.
- Authenticated reversible encryption — recover the original later.
- Keep — deliberately retain a matched value.
- Application-defined operators — your own transformation.
Granular entity configuration takes precedence over broad compatibility categories.
Precision controls
- Checksum and structural validation
- Bounded context scoring
- Exact exclusions and entity/rule exclusions
- Confidence thresholds
- Custom regex and national-ID definitions
Not claimed
Malformed structured input, invalid analyzer spans, overlapping results, unsafe exclusions and configured limit violations are designed to fail safely. The package does not claim that a finite recognizer set can identify every future identifier or credential format.
Structured payloads, not just prose
Protection is content-aware, so redaction survives serialization. The same engine covers property names, numeric JSON values, exact RFC 6901 paths and subtrees, and streamed content.
var options = new PiiRedactionOptions();
// Opt in to a country pack and override one entity's operator.
using var nationalIdMask = PiiOperator.Mask(
'*', preserveSuffixLength: 2);
options.Operators[PiiEntityType.NationalIdentifier] = nationalIdMask;
var redactor = LLMPolicyMeshPrivacy.CreateRedactor(
PiiCountryPack.UnitedKingdom);
PiiRedactionResult result = redactor.Redact(payload, options);
await foreach (string safeChunk in
LLMPolicyMeshApi.RedactStreamAsync(modelChunks))
{
await response.WriteAsync(safeChunk);
}
Model access
Use any model client. Apply one policy boundary.
Consumers can govern opaque HTTP model traffic or any Microsoft.Extensions.AI
IChatClient without introducing a model-provider dependency into
LLMPolicyMesh.
Microsoft.Extensions.AI
The application creates any provider client implementing IChatClient, and
LLMPolicyMesh wraps it. The wrapper owns and disposes the supplied client; credentials
and provider configuration stay application concerns.
Opaque HTTP gateway
The application provides the endpoint, authentication, HTTP client and provider payload. LLMPolicyMesh carries an opaque payload rather than forcing provider-specific request classes on you.
Examples
// The application creates its own provider client.
IChatClient provider = CreateApplicationModelClient();
var policy = new LLMPolicyMeshOptions();
policy.PromptInjection.Enabled = true;
// A policy wrapper, not a provider client factory.
using IChatClient protectedClient =
LLMPolicyMeshApi.ProtectChatClient(provider, policy);
// You own the endpoint, authentication, HttpClient and the provider's own
// JSON payload. LLMPolicyMesh carries that payload as opaque content.
var policy = new LLMPolicyMeshOptions();
policy.PromptInjection.Enabled = true;
HttpClient httpClient = CreateApplicationHttpClient();
ILLMPolicyGateway gateway =
LLMPolicyMeshApi.CreateGateway(httpClient, policy);
// LLMEndpoint carries validated endpoint metadata.
// LLMRequest carries the opaque provider payload.
LLMEndpoint endpoint = BuildApplicationEndpoint();
LLMRequest request = BuildProviderPayload(endpoint, prompt);
LLMResponse response = await gateway.SendAsync(request, cancellationToken);
LLMUsage usage = response.Usage;
var policy = new LLMPolicyMeshOptions();
policy.Resilience.Enabled = true;
policy.Resilience.MaximumAttemptsPerRoute = 2;
policy.Resilience.MaximumTotalAttempts = 4;
policy.Resilience.AttemptTimeout = TimeSpan.FromSeconds(20);
policy.Resilience.TotalTimeout = TimeSpan.FromSeconds(60);
// Every ordered client receives the complete policy boundary.
using IChatClient protectedClient =
LLMPolicyMeshApi.ProtectChatClient(
new[]
{
new LLMChatClientRoute("primary", primaryClient),
new LLMChatClientRoute("fallback", fallbackClient),
},
policy);
// Named policies are immutable at gateway creation and may be selected
// explicitly or through content-free metadata.
LLMNamedPolicy regulated = LLMPolicyMeshApi.CreateNamedPolicy(
"regulated",
policy =>
{
policy.PromptInjection.Enabled = true;
policy.Retrieval.Enabled = true;
policy.Limits.Enabled = true;
policy.Limits.MaximumInputTokens = 8_000;
});
ILLMPolicyGateway gateway = LLMPolicyMeshApi.CreateGateway(
httpClient,
namedPolicies: new[] { regulated });
// LLMRequest can select it with policyName: "regulated".
These examples demonstrate public package usage. Confirm exact member signatures, overloads and configuration requirements against the published API documentation before copying them into production code.
Security
Tools and retrieved content are separate security surfaces
The policy engine treats model input, model output, retrieval content and tool results as potentially hostile, while remaining independent of any provider schema.
Tool security
- Allow and deny rules
- Deny precedence over allow
- Declaration filtering
- Required tool choices
- Call limits
- Duplicate JSON property rejection
- Nested argument inspection
- Encoded argument inspection
- Destructive operation checks
- Per-tool constraints
- Risk-based tool-result sanitization
Defence-in-depth checks cover destructive SQL, commands, traversal, scripts, templates and unsafe network targets. Exact values, maximum lengths, URL hosts, required properties and custom rules can be applied per tool.
Retrieval and RAG
- Retrieval array validation
- Chunk limits
- Relevance score requirements
- Finite numeric score enforcement
- Property and nested string inspection
- Unsafe chunk rejection
- Remove or replace actions
- Prompt-injection checks inside retrieved content
Recognized collections must be arrays. Duplicate properties and malformed score shapes fail closed. Present scores must be unique, numeric, finite and above the configured minimum. An unsafe chunk can be replaced in full, so attacker fragments cannot survive.
Configure each security surface explicitly
var policy = new LLMPolicyMeshOptions();
policy.Tools.Enabled = true;
policy.Tools.DenyUnlistedTools = true;
policy.Tools.AllowedTools.Add("search_*");
policy.Tools.AllowedTools.Add("read_ticket");
policy.Tools.DeniedTools.Add("*_admin");
policy.Tools.ToolResults.Action = ToolResultPolicyAction.Sanitize;
using IChatClient protectedClient =
LLMPolicyMeshApi.ProtectChatClient(provider, policy);
var policy = new LLMPolicyMeshOptions();
policy.PromptInjection.Enabled = true;
policy.Retrieval.Enabled = true;
policy.Retrieval.Action = RetrievalPolicyAction.Remove;
policy.Retrieval.MaximumChunks = 12;
policy.Retrieval.MinimumRelevanceScore = 0.55;
policy.Retrieval.RequireRelevanceScore = true;
ILLMPolicyGateway gateway =
LLMPolicyMeshApi.CreateGateway(httpClient, policy);
Inspection-only normalization
Bounded multi-pass normalization creates additional inspection variants for Unicode tricks, invisible characters, hexadecimal, percent encoding, Base64 and Base64url, JSON escapes and reversed text.
These variants are inspected but never substituted into the outbound payload.
Threat-model limit
Deterministic rules reduce common injection and evasion risk. They do not prove arbitrary natural-language content safe. High-assurance deployments should combine this layer with least-privilege tools, retrieval provenance, application authorization and an independently evaluated semantic classifier. Tool policy also does not replace safe tool implementation: parameterized queries, process isolation, canonical path validation, egress controls and least-privilege credentials remain your responsibility.
Economics and resilience
Retries and fallbacks share one logical request boundary
Cost and resilience are governed together, so transient failures and fallback routes cannot silently bypass spending policy.
Request lifecycle
-
Resolve price
Application-owned, asynchronous, provider-neutral pricing. Missing or failed resolution is fail-closed.
-
Estimate protected request
Protected input plus declared maximum output tokens are priced before provider dispatch.
-
Enforce request limit
The maximum cost per request is applied to the estimate.
-
Reserve tenant budget
Atomic reservations compare committed plus outstanding cost against hourly, daily or monthly UTC limits.
-
Dispatch attempt
Sent through your own
HttpClientorIChatClient, with sanitized operational evidence emitted. -
Settle actual or conservative cost
Actual usage replaces estimates. Indeterminate attempts can commit the estimate, so a failed attempt plus a retry cannot hide spend.
Economic controls
- Maximum cost per request
- Maximum cost per tenant
- Hourly, daily and monthly periods
- Application-owned price provider
- Application-owned distributed store
- Bounded process-local store
Resilience controls
- Retry classification
- Attempt and total timeouts
Retry-Aftersupport- Endpoint-local circuit breaker
- Ordered fallback routes
- Idempotency protection
Application-owned
No database required
A bounded process-local budget store is available for a single application instance. Multi-process durability and atomicity belong to an application implementation of the public budget-store contract. LLMPolicyMesh does not require Redis, SQL Server, SQLite, PostgreSQL or any other database provider.
var policy = new LLMPolicyMeshOptions();
policy.Budget.Enabled = true;
policy.Budget.MaximumCostPerRequestUsd = 0.25m;
policy.Budget.MaximumCostPerTenantUsd = 40m;
policy.Budget.TenantPeriod = LLMBudgetPeriod.Daily;
policy.Budget.PriceProvider = applicationPriceProvider;
policy.Budget.Store = applicationBudgetStore;
policy.Resilience.Enabled = true;
policy.Resilience.MaximumAttemptsPerRoute = 2;
policy.Resilience.MaximumTotalAttempts = 4;
policy.Resilience.TotalTimeout = TimeSpan.FromSeconds(60);
Audit and observability
Evidence that does not become a second privacy leak
Built-in paths sanitize before forwarding, and telemetry keeps payload content out by default.
What the package constructs
- Provider-neutral
IAuditSinkdestination contract - Mandatory audit sanitization, placed before your sinks
- Microsoft
ILoggerredaction of messages, structured values, scopes and exception text - Delegate, composite and JSON Lines sinks
- Tamper-evident SHA-256 ledger over a record sequence
- Optional application-keyed HMAC-SHA-256 ledger
- Ledger verification that classifies malformed records, gaps, broken links, wrong keys, field ambiguity and incomplete final records
- Privacy-safe
ActivitySource - Privacy-safe
Meter - Per-policy and per-rule outcomes and latency
Application-owned
You choose the destination
IAuditSink is a contract, not a storage engine. Point it anywhere:
- Microsoft logging
- SQLite
- SQL Server
- PostgreSQL
- Cloud storage
- SIEM
- Custom application sink
Database libraries and schemas belong to the consuming application. Key rotation, retention, access control, durable flushing and backups do too. That is intentional: the package supplies safe contracts and evidence construction without pulling database providers or native binaries into every consumer.
IAuditSink audit =
LLMPolicyMeshAudit.CreateLoggerSink(logger);
using IChatClient protectedClient =
LLMPolicyMeshApi.ProtectChatClient(
provider,
policy,
audit);
Framework integrations
The same policy core at every common .NET boundary
Functionality is not split across optional NuGet packages. Each surface below ships inside the one package.
| Integration | Target | What it protects |
|---|---|---|
| Opaque HTTP gateway | netstandard2.1 + net8.0 |
Provider request and response payloads, limits, tools, budgets, retry and fallback, audit. |
Microsoft.Extensions.AI IChatClient |
netstandard2.1 + net8.0 |
Messages, instructions, functions, results, responses and streams. |
| ASP.NET Core middleware | net8.0 |
Incoming request and outgoing response bodies through Microsoft.AspNetCore.App. |
DelegatingHandler |
netstandard2.1 + net8.0 |
Arbitrary textual outbound HTTP request and response bodies. |
Microsoft ILogger |
netstandard2.1 + net8.0 |
Formatted messages, state values, scopes and exceptions. |
| Dependency injection | netstandard2.1 + net8.0 |
Registration of an application-owned HttpClient and the consolidated gateway API. |
| Streaming helper | netstandard2.1 + net8.0 |
Buffer-before-release redaction of asynchronous text chunks. |
ASP.NET Core is additive
The net8.0 target uses the Microsoft.AspNetCore.App shared framework for
middleware. The netstandard2.1 asset stays free of that requirement and
exposes the cross-platform provider-neutral core.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLLMPolicyMesh(
services => services.GetRequiredService<HttpClient>(),
options => options.PromptInjection.Enabled = true);
var app = builder.Build();
app.UseLLMPolicyMeshRedaction();
app.Run();
Ownership boundary
What the package enforces, and what stays yours
The design stays useful because it draws a hard line between reusable policy enforcement and environment-specific security ownership.
| LLMPolicyMesh enforces | Your application owns |
|---|---|
| Privacy and policy ordering | Provider credentials |
| Tool and retrieval controls | Tool authorization and sandboxing |
| Budget contracts and validation | Distributed durable storage |
| Sanitized audit construction | Audit destination and retention |
| Retry and fallback policy | Provider SLA and endpoint access |
| Safe streaming | End-user authorization |
The application also keeps the concrete model client, endpoints, model selection, databases and distributed storage, logging providers, audit destinations, infrastructure and authorization. LLMPolicyMesh governs the interaction around those components.
Questions
Frequently asked questions
No. LLMPolicyMesh uses provider-neutral integration points and lets your application supply and configure the model client or endpoint.
Yes. There are two access paths. You can wrap any client implementing IChatClient, or you can use the opaque HTTP gateway, where your application chooses the provider JSON schema, endpoint, authentication and model. LLMPolicyMesh carries the payload rather than translating vendor protocols into a proprietary request object.
No. It does not store API keys, discover models, or translate every vendor protocol. Credentials, secret storage, endpoint authorization and provider SDK configuration remain application concerns.
No. A bounded process-local budget store is available for a single application instance. Multi-process durability and atomicity belong to your implementation of the public budget-store contract.
No. The core package does not require a database provider. Durable storage integrations remain an application choice.
Yes. IAuditSink is a provider-neutral destination contract, so you choose logging, a database, cloud storage or anything else. Delegate, composite and JSON Lines sinks ship with the package, and the sanitizer is placed before your sink. Destination, key storage and rotation, durable flush, access control, backups and retention are yours.
Yes. ASP.NET Core applications can apply policy-aware redaction to incoming request and outgoing response bodies.
Yes. The IChatClient wrapper protects messages, instructions, functions, results, responses and streaming updates. The protected wrapper owns and disposes the client you supply.
Yes. Declarations, explicit choices, calls, arguments and results are each inspected before use or release. Deny rules override allow rules, duplicate JSON properties are rejected, nested and JSON-encoded argument values are inspected, and per-tool risk levels drive indirect-instruction inspection with reject, sanitize or replace outcomes.
Tool policy does not replace safe tool implementation. Parameterized queries, process isolation, canonical path validation, network egress controls, least-privilege credentials and normal authorization are still yours.
Yes. Applications can add custom rules, conditional selection, external analysis and validation to enforce application-specific requirements.
Yes. Atomic reservations compare committed plus outstanding cost against hourly, daily or monthly UTC limits, and actual usage replaces estimates at settlement. Indeterminate provider attempts can commit the estimate, so a failed attempt plus a retry cannot hide spend. Pricing and the distributed atomic store are application-owned contracts.
No. It supplies technical controls that can support GDPR, CCPA and enterprise AI governance programs. Whether a deployment complies depends on configuration, lawful basis, data flows, retention, access controls, organizational procedures and jurisdiction-specific advice. No external regulatory or security certification is asserted.
The beta label indicates a pre-stable release whose public experience may continue to evolve based on compatibility feedback and production adoption.
Project links
Repository, package and documentation URLs
Public project destinations will be activated after publication. Until then, related buttons and navigation entries return here instead of pointing to an incomplete or invented URL.
- GitHub repository
- Updating soon
- NuGet package
- Updating soon
- Documentation
- Updating soon
- Security policy
- Updating soon
- MIT licence
- Updating soon
- packageVersion
- Currently
0.6.0-beta.3
Put one enforceable policy boundary around every model call.
Install the package, keep your own model client, and govern what crosses the boundary in both directions.
dotnet add package LLMPolicyMesh --version 0.6.0-beta.3