PRECINCT Gateway

The PRECINCT Gateway is a PRECINCT-native component, purpose-built for this architecture. It is not a fork or wrapper of an existing proxy; it is original software designed from the ground up to enforce the 13-layer middleware chain across all five governed planes.

Overview

The PRECINCT Gateway is the centralized enforcement point for all agent-to-tool communication in the PRECINCT architecture. It functions as a reverse proxy that sits between AI agents and upstream MCP servers, ensuring every request is evaluated against security policy before reaching any tool.

38,000

Lines of Go implementing the gateway core, middleware chain, and integration layers.

62,000

Lines of test code. The test surface exceeds the production surface by 1.6x, covering unit, integration, and end-to-end scenarios.

13 Layers

Defense-in-depth middleware chain. Every request traverses all 13 layers in strict order before reaching the upstream.

The gateway implements the complete 13-layer defense-in-depth middleware chain and governs all five planes of the PRECINCT architecture: LLM/Model Egress, Context/Memory, Tool, Control Loop, and Ingress/Event.

Threat Validation

The gateway's 13-layer middleware chain has been validated against all 16 case studies in Research (Shapira et al., 2026, arXiv:2602.20021v1). See the defense mapping table for which gateway layers defend against each attack scenario.

How It Works

When an agent sends a tool call, the gateway intercepts the JSON-RPC request and routes it through the middleware chain. Each layer makes an independent security decision. If any layer denies the request, processing halts and a structured error code is returned to the agent. If all layers approve, the request is forwarded to the upstream MCP server with real credentials injected at the last possible moment.

sequenceDiagram participant Agent as AI Agent participant GW as PRECINCT Gateway participant SPIFFE as SPIFFE/SPIRE participant OPA as OPA Engine participant DLP as DLP Scanner participant SPIKE as SPIKE Secrets participant MCP as Upstream MCP Server Agent->>GW: POST / (JSON-RPC tool call) Note over GW: Layer 0-2: Metrics, Size, Body Capture GW->>SPIFFE: Verify mTLS SVID SPIFFE-->>GW: Identity confirmed (spiffe://trust-domain/agent/xyz) Note over GW: Layer 4: Write hash-chained audit record Note over GW: Layer 5: Verify tool SHA-256 hash GW->>OPA: Evaluate policy (identity + tool + params) OPA-->>GW: ALLOW (decision ID: d-29fa...) GW->>DLP: Scan request payload DLP-->>GW: Clean (no credentials, no PII) Note over GW: Layer 8: Update session risk score Note over GW: Layer 9: Risk below threshold (no step-up needed) Note over GW: Layer 10: Deep scan (async, clean) Note over GW: Layer 11-12: Rate limit OK, circuit closed GW->>SPIKE: Resolve token references SPIKE-->>GW: Real credentials (scoped, short-lived) GW->>MCP: Forward request (with real credentials) MCP-->>GW: Tool response Note over GW: Response firewall: redact sensitive data GW-->>Agent: Filtered response (handles, not raw values)

API Surface

The gateway exposes a minimal API surface. The primary endpoint handles all MCP JSON-RPC traffic. Additional endpoints support health checking and response firewall handle dereference.

PRECINCT Gateway API endpoints
Method Path Description
POST / Main JSON-RPC endpoint. All MCP-over-HTTP traffic flows through this endpoint. Accepts standard JSON-RPC 2.0 payloads.
POST /data/dereference Response Firewall handle dereference. Resolves opaque data handles returned by the response firewall back to their original values, subject to identity-based access control.
GET /health Health check endpoint. Returns the gateway's readiness status including upstream connectivity and middleware chain availability.
Control Plane Endpoints
POST /v1/ingress/admit Ingress plane admission. Validates canonical connector envelopes: SPIFFE source principal matching, SHA-256 payload content-addressing, replay detection with 30-minute nonce TTL, and 10-minute freshness windows.
POST /v1/context/admit Context plane admission. Enforces memory tier classification (ephemeral/session/long_term/regulated), provenance validation, DLP classification for long_term writes, step-up for regulated reads, and minimum-necessary invariants.
POST /v1/model/call Model egress plane. Evaluates model provider authorization, data residency constraints, HIPAA-aware prompt safety, and provider budget tracking.
POST /v1/tool/execute Tool plane execution. Evaluates capability registry, protocol adapter rules, CLI shell-injection prevention (command allowlist, max-args, denied-arg-tokens), and step-up requirements.
POST /v1/loop/check Loop plane boundary check. Full 8-state governance state machine with immutable budget limits across 8 dimensions (steps, tool calls, model calls, wall time, egress bytes, model cost, provider failovers, risk score). Supports operator halt and provider unavailability events.
Admin Endpoints
GET /admin/loop/runs List all loop runs with state, usage, and limits. Sorted by last update time.
GET /admin/loop/runs/<id> Per-run detail including governance state, halt reason, immutable limits, and usage snapshot.
POST /admin/loop/runs/<id>/halt Operator halt (human kill switch). Transitions a running loop to HALTED_OPERATOR state. Returns 409 if already terminal. All halt operations are audit-logged.

Listening Modes

  • Dev mode: http://localhost:9090. Plain HTTP for local development and evaluation. No mTLS required. Suitable for make up workflows.
  • SPIFFE mTLS mode: https://localhost:9443. Production mode with SPIFFE mTLS authentication. All clients must present a valid SVID. Required for production deployments.

Request Processing

The following is a detailed walkthrough of what happens when an agent sends a tool call through the gateway. Each step corresponds to a layer in the 13-layer middleware chain.

1. SPIFFE Identity Verification (Layer 3)

The gateway extracts the caller's SPIFFE ID from the mTLS handshake. The SVID (SPIFFE Verifiable Identity Document) is validated against the SPIRE trust bundle. If the SVID is expired, revoked, or from an untrusted domain, the request is rejected with spiffe_auth_required.

2. Audit Logging (Layer 4)

A structured JSONL audit record is written with a unique decision ID. Each record includes a hash of the previous record, forming a tamper-evident chain. The audit log captures: timestamp, SPIFFE ID, requested tool, parameters (redacted), and the eventual decision (allow/deny with reason).

3. Tool Registry Check (Layer 5)

The requested tool is looked up in the Capability Registry. The gateway computes the SHA-256 hash of the tool's current state and compares it to the registered hash. If the hashes do not match, a rug-pull attack is assumed and the request is denied with tool_hash_mismatch. If the tool is not registered at all, the request is denied with tool_not_in_registry.

4. OPA Policy Evaluation (Layer 6)

The gateway constructs an OPA input document containing the caller's SPIFFE ID, the requested tool, the request parameters, session context, and environmental metadata. This document is evaluated against the loaded Rego policy bundle. Policies can express fine-grained rules such as "agent X may call tool Y only with parameter Z during business hours." Denied requests receive authz_policy_denied.

5. DLP Credential Blocking (Layer 7)

The DLP scanner inspects the request payload for sensitive data patterns. Detected patterns include:

  • AWS access keys and secret keys
  • GitHub personal access tokens
  • PEM-encoded private keys and certificates
  • API keys matching common provider formats
  • Personally identifiable information (PII)

If credentials or sensitive data are detected, the request is denied with dlp_credentials_detected. Prompt injection patterns trigger dlp_injection_detected.

6. Session Risk Tracking (Layer 8)

The session context engine accumulates behavioral signals across requests within the same session. It tracks tool invocation patterns, data access sequences, and cumulative risk indicators. If an agent's behavior pattern resembles known exfiltration or lateral movement patterns, the session risk score is elevated.

Layer 8 now includes EscalationScore tracking that detects gradual privilege escalation across sessions. Each action's escalation contribution is calculated as Impact x (4 - Reversibility). The cumulative score is evaluated against three thresholds: Warning (≥ 15), Critical (≥ 25), and Emergency (≥ 40). When the Emergency threshold is reached, the request is denied with escalation_emergency. The current escalation score is propagated via the X-Precinct-Escalation-Score response header for downstream observability.

7. Step-Up Gating (Layer 9)

For high-risk operations (as defined by policy), the step-up gate dispatches a guard model to evaluate whether the operation should proceed. The guard model receives the full request context and returns an independent risk assessment. If the risk exceeds the configured threshold, the request is denied with stepup_required.

Step-up gating now incorporates irreversibility classification as an input signal. Actions are classified into four tiers: Low (read), Medium (create), High (modify), and Critical (delete/destroy). Critical-tier actions automatically trigger step-up evaluation regardless of the current risk score. The data classification tier is propagated via the X-Precinct-Data-Classification response header.

8. Deep Scan (Layer 10)

An asynchronous prompt injection detection layer dispatches the request content to a specialized guard model trained to identify injection attempts, jailbreak patterns, and adversarial inputs. This layer runs asynchronously to minimize latency impact on the fast path.

9. Rate Limit Check (Layer 11)

Per-identity token bucket rate limiting enforced via KeyDB. Each SPIFFE identity has a configurable rate limit budget. When the budget is exhausted, subsequent requests are denied with rate_limit_exceeded until tokens are replenished.

10. Circuit Breaker Check (Layer 12)

The circuit breaker monitors upstream MCP server health. If an upstream is experiencing failures above the configured threshold, the circuit opens and subsequent requests are immediately denied with circuit_breaker_open rather than contributing to a cascade failure.

11. Token Substitution (Layer 13)

The final layer before egress. The gateway scans the outbound request for opaque token references and resolves them to real credentials via SPIKE. The real credentials are injected into the request immediately before it is forwarded to the upstream MCP server. No middleware layer upstream of this point ever sees real credentials.

Error Codes

When the gateway denies a request, it returns a structured error code that enables agents to understand the reason for denial and adapt their behavior. Each error code maps to a specific middleware layer and denial reason.

PRECINCT Gateway structured deny codes
Error Code Layer Description
spiffe_auth_required 3 (SPIFFE Auth) The request lacks a valid SPIFFE identity. The caller must present a valid SVID via mTLS.
authz_policy_denied 6 (OPA Policy) The OPA policy evaluation denied the request. The caller's identity does not have permission to perform the requested operation.
dlp_credentials_detected 7 (DLP Scanning) The request payload contains credentials or sensitive data that must not be transmitted.
stepup_required 9 (Step-Up Gating) The operation requires elevated authorization. The guard model determined the risk exceeds the threshold for automatic approval.
dlp_injection_detected 10 (Deep Scan) The request content contains patterns consistent with prompt injection or adversarial manipulation.
rate_limit_exceeded 11 (Rate Limiting) The caller's rate limit budget is exhausted. The request should be retried after the token bucket replenishes.
circuit_breaker_open 12 (Circuit Breaker) The upstream MCP server is experiencing failures. Requests are being short-circuited to prevent cascade failure.
tool_not_in_registry 5 (Tool Registry) The requested tool is not registered in the Capability Registry. Only registered tools may be invoked.
tool_hash_mismatch 5 (Tool Registry) The tool's current hash does not match its registered hash. A rug-pull attack is suspected. The tool must be re-verified and re-registered.
escalation_emergency 8 (Session Context) The cumulative escalation score has reached the Emergency threshold (≥ 40). The session is considered compromised by gradual escalation and further requests are denied (HTTP 403).
data_source_hash_mismatch 5 (Tool Registry) An external data source's content hash does not match its registered hash and the mutable policy is set to block_on_change. The data source may have been tampered with (HTTP 403).
unregistered_data_source 5 (Tool Registry) The requested data source is not registered in the Data Source Integrity Registry. Only registered data sources may be accessed (HTTP 403).
principal_level_insufficient 6 (OPA Policy) The caller's principal authority level is below the minimum threshold required by the target operation's policy. Elevation or delegation is required (HTTP 403).
irreversible_action_denied 9 (Step-Up Gating) An irreversible action (reversibility score ≥ 2) was blocked pending step-up authorization. The guard model or human operator must approve before the action can proceed (HTTP 403).
discord_signature_invalid 3 (SPIFFE Auth) / Channel Mediation An inbound Discord webhook request failed signature verification. The request did not originate from a verified Discord endpoint (HTTP 401).

Diagnostic Response Headers

The gateway propagates diagnostic metadata via response headers to support downstream observability and policy decisions. These headers are included on responses that traverse the relevant middleware layers.

PRECINCT Gateway diagnostic response headers
Header Source Layer Description
X-Precinct-Escalation-Score 8 (Session Context) The cumulative escalation score for the current session after processing this request. Numeric value; compare against Warning (15), Critical (25), Emergency (40) thresholds.
X-Precinct-Data-Classification 9 (Step-Up Gating) The irreversibility classification tier assigned to this action: low, medium, high, or critical.
X-Precinct-Principal-Level 3 (SPIFFE Auth) Integer (0-5) representing the principal authority level resolved from SPIFFE path patterns. 5 = System, 4 = Operator, 3 = Trusted Agent, 2 = Standard Agent, 1 = Restricted Agent, 0 = Unknown.
X-Precinct-Principal-Role 3 (SPIFFE Auth) String representing the resolved principal role: system, owner, delegated_admin, agent, external_user, or anonymous.
X-Precinct-Reversibility 9 (Step-Up Gating) Integer (0-3) representing the action reversibility score from ClassifyReversibility. 0 = fully reversible, 3 = irreversible. Actions with score ≥ 2 trigger automatic step-up gating.
X-Precinct-Backup-Recommended 9 (Step-Up Gating) Boolean indicating whether a pre-action snapshot is recommended before executing this operation. Set to true for actions with reversibility score ≥ 2 or impact score ≥ 3.

Response Firewall

The gateway does not only inspect inbound requests. Responses from upstream MCP servers also pass through a response firewall before being returned to the agent.

When an upstream tool returns sensitive data (credentials, tokens, connection strings, or other privileged information), the response firewall replaces the raw values with opaque handles. The agent receives a handle that is meaningless outside the gateway context.

If the agent (or a downstream consumer with appropriate identity) needs the original value, it can call the POST /data/dereference endpoint with the handle. The gateway verifies the caller's SPIFFE identity against the access control policy for that handle before returning the original value.

Why Handles?

Handles prevent agents from inadvertently logging, caching, or transmitting sensitive data. Even if an agent's conversation history or memory is compromised, the attacker obtains only opaque references that cannot be resolved without the proper SPIFFE identity and gateway access.

Pluggable Extension Slots

The gateway supports three named extension slots for integrating third-party security services into the middleware chain. Extensions are external HTTP sidecar services that receive a structured request payload, evaluate it, and return an allow/block/flag decision. No gateway code changes are required to add, remove, or reconfigure extensions.

Extension Slot Positions

Each slot is positioned at a carefully chosen point in the 13-layer middleware chain where third-party inspection is both safe and useful. If no extensions are configured for a slot, the middleware is a zero-cost pass-through.

Named extension slots in the PRECINCT middleware chain
Slot Name Position Use Cases
post_authz After OPA Policy (step 6), before DLP Scanner (step 7) Custom RBAC enrichment, tool checkers, pre-scan authorization gates
post_inspection After DLP Scanner (step 7), before Session Context (step 8) Content scanners, markdown validators, format checkers
post_analysis After Deep Scan (step 10), before Rate Limiting (step 11) Final approval gates, aggregated risk decisions, custom blocking logic

Extension Protocol

Extensions communicate with the gateway via HTTP POST. The gateway sends a structured ExtensionRequest payload to each extension's endpoint and expects a structured ExtensionResponse in return.

Request Payload (ExtensionRequest)

{
  "version": "1",
  "request_id": "d-29fa...",
  "trace_id": "abc123...",
  "timestamp": "2026-02-22T14:30:00Z",
  "slot": "post_inspection",
  "request": {
    "method": "tools/call",
    "tool_name": "file_read",
    "body": "<base64-encoded request body>",
    "spiffe_id": "spiffe://trust-domain/agent/xyz",
    "security_flags": [],
    "session_id": "sess-001"
  }
}

Which fields are included in the request object is controlled per-extension via the request_fields configuration. Extensions only receive the data they need.

Response Payload (ExtensionResponse)

{
  "version": "1",
  "decision": "allow",
  "flags": [],
  "reason": "",
  "http_status": 0,
  "error_code": ""
}

Extensions return one of three decisions:

  • allow: The request passes through to the next layer.
  • block: The request is denied. The gateway returns a structured error to the agent using the extension's error_code and http_status if provided.
  • flag: The request continues, but the supplied flags are appended to the request's SecurityFlagsCollector for downstream layers to consider.

Configuration

Extensions are defined in a YAML registry file (config/extensions.yaml). The registry is hot-reloaded via file-system watch (fsnotify). Adding or modifying extensions does not require a gateway restart.

version: "1"
extensions:
  - name: content-scanner
    slot: post_inspection
    enabled: true
    endpoint: http://localhost:8090/scan
    timeout_ms: 2000
    fail_mode: fail_open
    priority: 100
    description: "Scans request content for policy violations"
    filters:
      methods: ["tools/call"]
      tools: []
    request_fields:
      include_body: true
      include_spiffe_id: true
      include_tool_name: true
      include_security_flags: false
    circuit_breaker:
      failure_threshold: 5
      reset_timeout_ms: 30000

Within a slot, extensions execute in priority order (lower value runs first). Filters control which requests are dispatched to each extension; empty filter lists match all requests.

Fail Modes

Each extension declares a fail mode that determines gateway behavior when the extension sidecar is unreachable or returns an error:

  • fail_open: On error, skip this extension and continue processing. The request proceeds as if the extension were not configured. Appropriate for advisory or non-critical extensions.
  • fail_closed: On error, deny the request with extension_unavailable_fail_closed. No request passes through if the extension cannot evaluate it. Appropriate for mandatory compliance checks.

Per-Extension Circuit Breakers

Each extension can optionally configure a circuit breaker to prevent cascading failures. When the number of consecutive failures reaches the failure_threshold, the circuit opens and the extension is skipped (or denied, depending on fail mode) until the reset_timeout_ms has elapsed. After the timeout, the circuit enters half-open state and allows one probe request through.

Reference Implementation

The PRECINCT repository includes a reference content-scanner sidecar that demonstrates the extension protocol. It registers in the post_inspection slot, receives base64-encoded request bodies, applies configurable scanning rules, and returns allow/block/flag decisions. It serves as a starting point for building custom extensions.

Zero-Cost When Unused

If no extensions are configured for a slot, the extension slot middleware is a zero-cost pass-through. There is no performance penalty for having the extension infrastructure in the chain when it is not in use.

Configuration

The gateway is configured through a combination of YAML files, OPA policy bundles, and environment variables. The key configuration surfaces are:

Capability Registry V2

A YAML-based registry that defines all tools and models the gateway is authorized to mediate. Each entry includes the tool's name, upstream endpoint, expected SHA-256 hash, allowed callers, rate limit budget, and any tool-specific policy overrides.

OPA Policy Bundles

Rego policy files organized into bundles. Policies are version-controlled and can be updated at runtime via OPA's bundle API. The gateway reloads policy bundles periodically without requiring a restart.

DLP Rule Configuration

Regular expression patterns and detection rules for the DLP scanning layer. Rules define which patterns constitute credentials, PII, or injection attempts, along with the action to take (block, flag, or log) when a pattern is matched.

Rate Limit Budgets

Per-identity and per-tool rate limit budgets defined as token bucket parameters: bucket capacity, refill rate, and refill interval. Budgets can be overridden per SPIFFE identity to accommodate different agent workloads.

SPIFFE Trust Domain Configuration

Trust domain settings for SPIFFE/SPIRE integration. Defines the trust domain name, SPIRE server endpoint, trust bundle refresh interval, and SVID rotation parameters.

Performance

The gateway is designed around two processing paths with distinct latency profiles. The fast path handles the majority of requests with minimal overhead. The deep path adds guard model evaluation for high-risk requests.

PRECINCT Gateway latency breakdown by processing path
Path Layers Latency Description
Fast path 0–8, 11–13 <5ms Identity verification, authorization, pattern-based DLP detection, rate limiting, circuit breaking, and token substitution. Handles the majority of requests.
Deep path 9–10 200–550ms Guard model dispatch for step-up gating and async prompt injection detection. Triggered conditionally by risk score or policy rules.

Per-Middleware Latency Breakdown

An outer observability wrapper (Request Metrics, step 0) captures timing, size, and routing metadata (<0.1ms overhead). Each enforcement layer below emits timing spans observable in any OTel-compatible backend (Jaeger, Grafana Tempo, Datadog).

Per-middleware latency targets
Layer P50 Latency P99 Latency
1. Request Size Limit <0.05ms <0.1ms
2. Body Capture <0.2ms <0.5ms
3. SPIFFE Auth <0.5ms <1ms
4. Audit Log <0.2ms <0.5ms
5. Tool Registry Verify <0.3ms <1ms
6. OPA Policy <1ms <2ms
7. DLP Scanning <0.5ms <1ms
8. Session Context <0.2ms <0.5ms
9. Step-Up Gating 0ms (skip) 200–500ms (dispatch)
10. Deep Scan 0ms (skip) 200–550ms (dispatch)
11. Rate Limiting <0.2ms <0.5ms
12. Circuit Breaker <0.05ms <0.1ms
13. Token Substitution <0.5ms <1ms
Benchmark Availability

Complete per-middleware benchmark results are available by running make bench in the gateway source tree. Benchmarks exercise each middleware layer independently and in combination, measuring allocation counts and bytes alongside wall-clock time.