Secure Your Agent

This walkthrough takes you from an existing AI agent to a fully secured PRECINCT deployment. Choose the track that matches your environment: Docker Compose for local development, or Kubernetes for enterprise and production workloads.

Overview

PRECINCT can secure any agent that speaks MCP (Model Context Protocol) without modifying the agent's source code. The gateway sits between your agent and its tools, enforcing 13 layers of security controls transparently. Choose the track that fits your deployment target.

Track A: Home User / Developer

Docker Compose on a laptop or workstation. Ideal for evaluation, demos, and local development.

  • Time: ~15 minutes
  • Requires: Docker Desktop
  • Result: fully functional security stack on localhost

Start Track A →

Track B: Enterprise / Platform Team

Kubernetes for staging and production environments. Full network policies, admission control, and SPIFFE mTLS.

  • Time: ~30 minutes
  • Requires: kubectl, kustomize, running K8s cluster
  • Optional: Helm for values-driven configuration

Start Track B →

Prerequisites

The following table shows what each track requires. All items in Track A are mandatory for Docker Compose. Track B items are required only for Kubernetes deployments.

Prerequisites by deployment track
Requirement Track A (Compose) Track B (K8s)
Docker + Docker Compose Required (v24+ / v2.20+) Required (for image builds)
Go Required (1.22+) Required (1.22+)
Make Required Required
kubectl Not needed Required (1.28+)
kustomize Not needed Required (5.0+)
Helm Not needed Optional (3.12+)

Track A: Docker Compose

This track walks you through adding your agent to the existing PRECINCT Docker Compose stack. Each step builds on the previous one. By the end, your agent will have a cryptographic identity, policy-governed access to tools, and full audit logging.

Step 1: Add your agent service to docker-compose.override.yml

Create a docker-compose.override.yml in the POC/ directory. Docker Compose automatically merges this file with the base docker-compose.yml. Your agent container must share the agentic-net network to reach the gateway and the SPIRE agent socket volume to obtain its SVID.

# POC/docker-compose.override.yml
services:
  my-agent:
    build:
      context: ./path/to/your/agent
      dockerfile: Dockerfile
    container_name: my-agent
    hostname: my-agent
    depends_on:
      mcp-security-gateway:
        condition: service_healthy
    volumes:
      - spire-agent-socket:/tmp/spire-agent/public:ro
    environment:
      - MCP_GATEWAY_URL=http://mcp-security-gateway:9090
      - SPIFFE_ENDPOINT_SOCKET=unix:///tmp/spire-agent/public/api.sock
    networks:
      - agentic-net
    labels:
      - "spiffe-id=my-agent"
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: true
    tmpfs:
      - /tmp

Step 2: Register a SPIRE identity for your agent

Each workload in PRECINCT receives a SPIFFE identity via SPIRE. Add a registration entry so SPIRE will issue an SVID to your agent based on its Docker label.

# Add this to scripts/register-spire-entries.sh (before the final echo)
/opt/spire/bin/spire-server entry create \
  -socketPath /tmp/spire-server/private/api.sock \
  -parentID spiffe://poc.local/agent/local \
  -spiffeID spiffe://poc.local/agents/mcp-client/my-agent/dev \
  -selector docker:label:spiffe-id:my-agent \
  -ttl 3600

Step 3: Write an OPA policy for your agent

Create a Rego policy file that authorizes your agent's SPIFFE ID to invoke specific tools. Place it in config/opa/ so the gateway loads it automatically.

# config/opa/my_agent_policy.rego
package mcp.authz

import rego.v1

# Allow my-agent to call tavily_search and echo tools
allow if {
    input.caller == "spiffe://poc.local/agents/mcp-client/my-agent/dev"
    input.tool in {"tavily_search", "echo"}
}

Step 4: Register tools in the capability registry

If your agent uses tools that are not already registered, add them to config/capability-registry-v2.yaml. Each tool needs a name, description, and SHA-256 hash of its schema.

# Append to config/capability-registry-v2.yaml under the tools section
- name: my_custom_tool
  description: "Tool provided by my-agent"
  inputSchema:
    type: object
    properties:
      query:
        type: string
    required: ["query"]
  upstream: http://my-tool-server:8080/mcp
  sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

Step 5: Route traffic through the gateway

Configure your agent to send all MCP requests to the PRECINCT gateway instead of directly to tool servers. The gateway URL is http://mcp-security-gateway:9090 from within the Docker network.

# Example: Python agent using the PRECINCT SDK
from mcp_gateway_sdk import GatewayClient

client = GatewayClient(
    gateway_url="http://mcp-security-gateway:9090",
    spiffe_id="spiffe://poc.local/agents/mcp-client/my-agent/dev"
)

# All tool calls now flow through the 13-layer security chain
result = await client.call_tool("tavily_search", {"query": "AI safety"})

Step 6: Verify end-to-end

Start the stack and confirm that your agent's requests are being processed through PRECINCT's security layers.

# Start the full stack (includes your override)
cd POC
make up

# Verify your agent can call tools through the gateway
curl -s http://localhost:9090/ \
  -H "Content-Type: application/json" \
  -H "X-SPIFFE-ID: spiffe://poc.local/agents/mcp-client/my-agent/dev" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq .

# Verify unauthorized SPIFFE IDs are denied
curl -s http://localhost:9090/ \
  -H "Content-Type: application/json" \
  -H "X-SPIFFE-ID: spiffe://poc.local/agents/unknown/attacker" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"tavily_search","arguments":{"query":"test"}},"id":2}' | jq .code
# Expected: "authz_policy_denied"

# Check audit logs for your agent's requests
docker exec mcp-security-gateway cat /tmp/audit.jsonl | jq 'select(.caller_id | contains("my-agent"))'
Track A Complete

Your agent is now secured by PRECINCT. Every request flows through identity verification, policy evaluation, DLP scanning, rate limiting, and tamper-evident audit logging.

Track B: Kubernetes

This track deploys your agent into a Kubernetes cluster alongside the PRECINCT stack. It assumes you have a running cluster with PRECINCT already deployed (via make k8s-up or kustomize).

Step 1: Deploy your agent as a Kubernetes Deployment

Create a Deployment in the tools namespace. Mount the SPIRE agent socket via hostPath so your agent can obtain its SVID.

# infra/eks/my-agent/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-agent
  namespace: tools
  labels:
    app.kubernetes.io/name: my-agent
    app.kubernetes.io/component: agent
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: my-agent
  template:
    metadata:
      labels:
        app.kubernetes.io/name: my-agent
    spec:
      serviceAccountName: my-agent
      containers:
        - name: my-agent
          image: your-registry/my-agent:latest
          env:
            - name: MCP_GATEWAY_URL
              value: "http://mcp-security-gateway.gateway.svc.cluster.local:9090"
            - name: SPIFFE_ENDPOINT_SOCKET
              value: "unix:///run/spire/sockets/agent.sock"
          volumeMounts:
            - name: spire-agent-socket
              mountPath: /run/spire/sockets
              readOnly: true
          securityContext:
            runAsNonRoot: true
            runAsUser: 65532
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
            seccompProfile:
              type: RuntimeDefault
      volumes:
        - name: spire-agent-socket
          hostPath:
            path: /run/spire/sockets
            type: DirectoryOrCreate
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-agent
  namespace: tools

Step 2: Create a SPIRE entry for your agent

Register your agent's SPIFFE ID with SPIRE. On Kubernetes, SPIRE uses the k8s_psat (projected service account token) attestor to identify workloads. For local Docker Desktop clusters using join token attestation, use the agent's SPIFFE ID as the parent.

# For k8s_psat attestation (EKS, GKE, AKS):
kubectl exec -n spire-system spire-server-0 -- \
  /opt/spire/bin/spire-server entry create \
  -spiffeID spiffe://agentic-ref-arch.poc/ns/tools/sa/my-agent \
  -parentID spiffe://agentic-ref-arch.poc/spire/agent/k8s_psat/default \
  -selector k8s:ns:tools \
  -selector k8s:sa:my-agent \
  -ttl 3600

# For local Docker Desktop (join_token attestation):
kubectl exec -n spire-system spire-server-0 -- \
  /opt/spire/bin/spire-server entry create \
  -spiffeID spiffe://agentic-ref-arch.poc/ns/tools/sa/my-agent \
  -parentID spiffe://agentic-ref-arch.poc/agent/local \
  -selector k8s:ns:tools \
  -selector k8s:sa:my-agent \
  -ttl 3600

Step 3: Write an OPA policy

Add your agent's authorization policy to the gateway ConfigMap. The SPIFFE ID in Kubernetes uses the trust domain agentic-ref-arch.poc and the namespace/service account path format.

# Add to gateway-config ConfigMap (or create a new .rego file)
package mcp.authz

import rego.v1

# Allow my-agent (K8s SPIFFE ID) to call specific tools
allow if {
    input.caller == "spiffe://agentic-ref-arch.poc/ns/tools/sa/my-agent"
    input.tool in {"tavily_search", "echo"}
}

Step 4: Register tools via ConfigMap

Update the gateway-config ConfigMap to include any new tools your agent requires. The ConfigMap is mounted into the gateway pod at /config/.

# Edit the gateway-config ConfigMap directly
kubectl edit configmap gateway-config -n gateway

# Or update the kustomization overlay file and re-apply
kustomize build infra/eks/overlays/local/ | kubectl apply -f -

Step 5: Apply a NetworkPolicy

Restrict your agent's network egress so it can only reach the gateway. This prevents agents from bypassing PRECINCT by calling tool servers directly.

# infra/eks/my-agent/networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-agent-egress
  namespace: tools
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: my-agent
  policyTypes:
    - Egress
  egress:
    # Allow traffic to the gateway only
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: gateway
          podSelector:
            matchLabels:
              app.kubernetes.io/name: mcp-security-gateway
      ports:
        - protocol: TCP
          port: 9090
    # Allow DNS resolution
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
    # Allow SPIRE agent socket (same node)
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: spire-system

Step 6: Verify via port-forward

Forward the gateway port and send test requests to confirm your agent can reach the gateway and that policies are enforced.

# Apply your manifests
kubectl apply -f infra/eks/my-agent/

# Port-forward the gateway
kubectl port-forward -n gateway svc/mcp-security-gateway 9090:9090 &

# Test your agent's SPIFFE ID
curl -s http://localhost:9090/ \
  -H "Content-Type: application/json" \
  -H "X-SPIFFE-ID: spiffe://agentic-ref-arch.poc/ns/tools/sa/my-agent" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' | jq .

# Run the E2E demo
make demo-k8s
Cloud-Specific Notes

EKS: Use IRSA (IAM Roles for Service Accounts) for cloud resource access. SPIRE uses the k8s_psat node attestor with projected service account tokens. ALB Ingress replaces NodePort.
GKE: Workload Identity Federation maps Kubernetes service accounts to Google Cloud service accounts. Use GKE Autopilot for managed node pools.
AKS: Azure AD Workload Identity provides pod-level identity. Use Azure CNI for NetworkPolicy enforcement.

Track B Complete

Your agent is secured in Kubernetes with SPIFFE mTLS, OPA policy evaluation, NetworkPolicy isolation, and production-grade audit logging.

Under the Hood

When your agent sends a request to the PRECINCT gateway, it passes through a 13-layer middleware chain. Each layer enforces a specific security control. The request must pass every layer to reach the upstream tool server. Here is the complete request journey:

13-layer middleware chain request flow
Layer Name Purpose Deny Code
1 Request Size Limiter Rejects oversized payloads before parsing request_too_large
2 JSON-RPC Validator Validates JSON-RPC 2.0 envelope structure invalid_jsonrpc
3 SPIFFE Authentication Validates caller identity (X-SPIFFE-ID header or mTLS SVID) spiffe_auth_required
4 Audit Logger Creates hash-chained, tamper-evident audit record --
5 Tool Registry Validates tool exists and schema hash matches tool_not_in_registry
6 OPA Policy Evaluation Authorizes caller + tool + arguments against Rego policy authz_policy_denied
7 DLP Scanner Detects credentials, PII, and secrets in request body dlp_credentials_detected
8 Session Tracking Maintains per-identity session state and cumulative risk score --
9 Step-Up Authentication Triggers re-authentication if risk score exceeds threshold stepup_required
10 Deep Scan (Guard Model) LLM-based prompt injection and jailbreak detection deep_scan_denied
11 Rate Limiter Per-identity token-bucket rate limiting via KeyDB rate_limit_exceeded
12 Circuit Breaker Prevents cascade failures when upstream is degraded circuit_breaker_open
13 Token Substitution Replaces token references with secrets from SPIKE Nexus --

After passing all 13 layers, the request is forwarded to the upstream tool server via MCP Streamable HTTP. The response follows the reverse path: the audit logger records the outcome, and the DLP scanner checks the response body for data exfiltration attempts.

Troubleshooting

Network Connectivity

Symptom: Agent cannot reach the gateway; connection refused or timeout.

  • Compose: Verify your agent is on the agentic-net network. Run docker network inspect agentic-security-network to confirm.
  • K8s: Check that the gateway Service is accessible from your agent's namespace. Use kubectl exec -n tools my-agent-pod -- wget -qO- http://mcp-security-gateway.gateway.svc.cluster.local:9090/health to test.
  • Ensure no NetworkPolicy is blocking egress from your agent to the gateway.

SPIRE Timing / SVID Fetch Delays

Symptom: Agent starts but gets spiffe_auth_required errors for the first 5-10 seconds.

  • SVID fetch on first request takes 5-10 seconds while the SPIRE agent attests the workload and delivers the certificate. Add a startup delay or retry loop.
  • In Compose, ensure spire-entry-registrar has completed successfully before your agent starts. Use depends_on: spire-entry-registrar: condition: service_completed_successfully.
  • In K8s, the SPIRE DaemonSet must be healthy before your pods start. Add an init container that waits for the SPIRE socket: [ -S /run/spire/sockets/agent.sock ].

OPA Policy Denials

Symptom: Requests return authz_policy_denied.

  • Verify that the SPIFFE ID in your Rego policy exactly matches the one registered in SPIRE. Case and path segments matter.
  • Check the tool name in the policy matches the tools/call request's params.name field.
  • Test your policy locally with opa eval: opa eval -d config/opa/ -i input.json "data.mcp.authz.allow"

DLP False Positives

Symptom: Legitimate requests blocked with dlp_credentials_detected.

  • The DLP scanner uses regex patterns to detect AWS keys, API tokens, and other credentials. If your payload contains strings that match these patterns (e.g., base64-encoded data), it may trigger a false positive.
  • Set DLP_INJECTION_POLICY=flag (instead of block) to log violations without blocking. Review the audit log to tune patterns.
  • For testing, you can temporarily disable DLP by setting the enforcement profile to dev (not recommended for production).

Next Steps

Your agent is secured. Here are the recommended next steps to deepen your integration and prepare for production.

Integration Guide

Detailed reference for identity registration, tool configuration, policy authoring, and traffic routing patterns.

Read the Integration Guide →

SDKs

First-class Python and Go SDKs that handle SPIFFE identity injection, structured error handling, and gateway communication.

Explore the SDKs →

Case Study

See how a real agentic AI application was secured by PRECINCT without any code changes.

Read the Case Study →

For Auditors

Compliance framework mappings for SOC 2, NIST 800-53, and ISO 27001. Evidence collection and control documentation.

Read the Auditors Guide →