API & integration
EVE CoreGuard Integration Guide: Connecting Your LLM Pipeline in Under 30 Minutes
This guide walks through the complete process of integrating EVE CoreGuard's AI governance API into an existing LLM pipeline. Whether you are running a FastAPI service, a batch inference worker, or a real-time chat application, the same integration pattern applies: evaluate the proposed action before it reaches the model, and enforce the decision on the way out. By the end of this guide your pipeline will produce ALLOWED, BLOCKED, or MODIFIED decisions with a signed decision record on every request. For a complete API reference, see the EVE CoreGuard API Reference.
Contents
1. Prerequisites
Before you begin, ensure you have the following:
- Python 3.9+ — the SDK supports CPython 3.9 through 3.13
- An EVE CoreGuard API key — keys have the form
eve_sk_...; request access on the CoreGuard page - The API base URL — the SDK defaults to
https://api.evecore.com;https://api.eveaicore.comreaches the same production platform. Pass a differentbase_urlonly for a private deployment - An action type that is bound to a policy pack — the server chooses the governing pack from your organization and
proposed_action["type"](for exampleloan_approval→lending_v1).policy_setis an optional assertion; see the policy-pack reference. Witheve-coreguard0.2.10 (the version currently on PyPI),evaluate()sendspolicy_set="lending_v1"whenever the argument is omitted, so a non-lending or custom-bound action type comes back as a signed BLOCK (COREGUARD.POLICY_BINDING_MISMATCH). On 0.2.10, pass the bound pack’spolicy_idaspolicy_setexplicitly; 0.2.11 omits it.
2. Installation
Install the EVE CoreGuard Python SDK from PyPI:
pip install eve-coreguard
To pin a range in a requirements.txt or pyproject.toml:
eve-coreguard>=0.1.0,<1.0.0
The SDK has zero required dependencies: HTTP transport uses the Python standard library (urllib). Offline verification of asymmetric (Ed25519 / ECDSA) decision records needs the cryptography package, which the verify extra installs: pip install "eve-coreguard[verify]".
Offline verification: with the verify extra, verify_decision_record() checks a decision record’s content hash and signature against the published public key, without calling the API. Legacy hmac- records verify only with the shared secret, which is not independent third-party verification.
3. Environment Variables
The SDK does not read environment variables itself: every setting is a CoreGuardClient constructor argument (api_key, base_url, timeout, max_retries, raise_on_veto). Keeping the key in the environment is still the right pattern; your code reads it and passes it in. The names below are the convention this guide uses:
# Required — read by YOUR code and passed to CoreGuardClient(api_key=...) COREGUARD_API_KEY=eve_sk_xxxxxxxxxxxxxxxxxxxxxxxx # Optional — passed as base_url=...; omit to use the SDK default COREGUARD_BASE_URL=https://api.evecore.com
Secret management: never commit COREGUARD_API_KEY to source control. Use a secrets manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, or Kubernetes Secrets) and inject it at runtime. CoreGuardClient raises CoreGuardError at construction if api_key is empty, so a missing key fails at startup rather than on the first request.
4. Synchronous Integration Pattern
CoreGuardClient is the SDK’s client. Insert one evaluate() call before each consequential model action in your existing pipeline. A policy refusal is an ordinary result (result.blocked), not an exception; exceptions mean the decision could not be obtained, and you fail closed:
import os from eve_coreguard import CoreGuardClient, CoreGuardError # Create once at module level and reuse. client = CoreGuardClient( api_key=os.environ["COREGUARD_API_KEY"], base_url=os.environ.get("COREGUARD_BASE_URL", "https://api.evecore.com"), ) def process_loan_request(user: dict, action: dict, context: dict) -> dict: # Step 1: evaluate the proposed action before touching the model. # action["type"] (e.g. "loan_approval") selects the bound pack server-side. try: result = client.evaluate( tenant_id="bank_001", user=user, proposed_action=action, context=context, ) except CoreGuardError as e: # No decision was obtained (network, auth, quota) — fail closed. log_error(e) return {"error": "governance_unavailable"} if result.blocked: # Policy refusal — return the rule ids that fired. return {"error": "blocked", "violations": [v.policy_id for v in result.policy_violations]} # Step 2: apply any field edits CoreGuard made before invoking the model. if result.verdict == "MODIFIED": action = apply_modifications(action, result.modifications) # Step 3: invoke your model — governance is complete. llm_response = your_llm_client.generate(action) # Step 4: keep the decision id with your record — it is your evidence handle. return { "response": llm_response, "decision_id": result.decision_id, "decision": result.verdict, }
The returned EvaluationResult carries decision (status, action), risk (score, level, factors), policy_violations (each with policy_id and description), modifications, and audit (the signed record), plus the convenience properties verdict, allowed, blocked, decision_id, signature and policy_version. Store decision_id alongside every decision record.
5. Using EVE CoreGuard from an Async Application
The SDK provides no async client; CoreGuardClient is synchronous. Evaluation is a single HTTP round trip, so in an asyncio app such as FastAPI run the call in a worker thread to keep the event loop free — use asyncio.to_thread() (or FastAPI’s run_in_threadpool):
import os, asyncio from fastapi import FastAPI, HTTPException from eve_coreguard import CoreGuardClient, CoreGuardError app = FastAPI() cg = CoreGuardClient(api_key=os.environ["COREGUARD_API_KEY"]) @app.post("/api/v1/decisions") async def make_decision(payload: dict): # Offload the blocking call to a thread so the event loop stays free. try: result = await asyncio.to_thread( cg.evaluate, tenant_id=payload["tenant_id"], user=payload["user"], proposed_action=payload["proposed_action"], model_output=payload.get("model_output"), context=payload.get("context", {}), ) except CoreGuardError: raise HTTPException(status_code=503, detail="governance unavailable") if result.blocked: raise HTTPException(status_code=422, detail={"status": "BLOCKED", "decision_id": result.decision_id}) return {"status": result.verdict, "risk_level": result.risk.level}
Client lifecycle: create CoreGuardClient once at startup and reuse it. Each evaluate() is an independent HTTPS request over the standard library; the client retries 5xx responses up to max_retries with exponential backoff and does not keep a connection pool.
6. Webhook Setup for Audit Events
Decisions are returned synchronously by evaluate(), with their signed record; there is no per-decision webhook event. Webhooks deliver governance and system events — for example governance.charter.veto, governance.action, system.rate_limit_exceeded and status.incident.opened. List every type with GET /api/sdk/webhooks/event-types. The SDK has no webhook helpers; register endpoints over HTTP.
Registering a Webhook Endpoint
curl -X POST https://api.evecore.com/api/sdk/webhooks \ -H "Authorization: Bearer $COREGUARD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-siem.example.com/coreguard-events", "events": ["governance.charter.veto", "governance.action"] }'
The response returns the endpoint’s signing secret once; store it as COREGUARD_WEBHOOK_SECRET. In production the URL must use https://, and URLs that resolve to private, loopback or other reserved addresses are refused.
Verifying Webhook Signatures
Every delivery carries an X-EVE-Webhook-Signature header: the hex HMAC-SHA256 of the raw request body, keyed with the endpoint secret. X-EVE-Event-Type, X-EVE-Event-ID and X-EVE-Timestamp accompany it. Verify the signature before processing the event:
import hashlib, hmac, json, os from fastapi import Request, HTTPException WEBHOOK_SECRET = os.environ["COREGUARD_WEBHOOK_SECRET"].encode() async def verify_coreguard_webhook(request: Request) -> bytes: body = await request.body() expected = hmac.new(WEBHOOK_SECRET, body, hashlib.sha256).hexdigest() received = request.headers.get("X-EVE-Webhook-Signature", "") if not hmac.compare_digest(expected, received): raise HTTPException(status_code=401, detail="Invalid webhook signature") return body @app.post("/coreguard-events") async def coreguard_webhook(request: Request): body = await verify_coreguard_webhook(request) event = json.loads(body) # Forward to your SIEM or compliance store await siem_client.ingest(event) return {"received": True}
7. Docker Deployment
Pass EVE CoreGuard credentials as environment variables at container runtime. Never bake them into the image:
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # COREGUARD_API_KEY (and optionally COREGUARD_BASE_URL) are injected at runtime — NOT here CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
docker run \ -e COREGUARD_API_KEY="$(vault kv get -field=api_key secret/coreguard)" \ -p 8080:8080 \ your-org/your-llm-service:latest
8. Kubernetes Sidecar Pattern
The Python SDK calls the hosted CoreGuard API; on Kubernetes you inject COREGUARD_API_KEY from a Secret exactly as in the Docker example. The EVE sidecar is a separate, private-preview component and is not an endpoint for the SDK: it is an egress governance forward proxy and Envoy ext_authz backend (HTTP on port 9001, gRPC on 9002) that runs CoreGuard evaluation inside the pod and signs a decision certificate for each request it authorizes. Use it when you want enforcement at the mesh or egress layer rather than in application code. Contact us for the sidecar build and Helm chart.
apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: llm-service image: your-org/llm-service:latest env: - name: COREGUARD_API_KEY valueFrom: secretKeyRef: name: coreguard-credentials key: api-key
9. Error Handling
Policy outcomes are results, not exceptions: a BLOCKED or MODIFIED decision comes back as an EvaluationResult (HTTP 200). Exceptions mean no decision was obtained. All of them subclass CoreGuardError and are importable from eve_coreguard:
| Exception / outcome | Meaning | Recommended Action |
|---|---|---|
result.blocked |
Policy refusal (including an unbound action type or a policy_set mismatch) |
Reject the action; return result.policy_violations to the caller |
result.verdict == "MODIFIED" |
Policy requires changes before proceeding | Apply result.modifications, then continue |
AuthError |
Invalid or expired API key (HTTP 401) | Fail closed; rotate the key and alert |
PaymentRequiredError |
Subscription past due or restricted (HTTP 402) | Fail closed; inspect client.subscription_state |
RateLimitError |
Rate limit exceeded (HTTP 429) | Back off for e.retry_after seconds; queue the request |
PolicySetNotFoundError |
The asserted policy_set is unknown |
Fix the assertion or omit it; check client.list_policies() |
CoreGuardError |
Any other failure, including network errors and timeouts (e.status_code is 0 when no response arrived) |
Fail closed — reject the action; log and alert |
VetoError |
Raised only by verify() when the client was built with raise_on_veto=True |
Do not deliver the verified output |
Fail-closed discipline: EVE CoreGuard is a mandatory gateway, not an optional filter. If the governance layer is unreachable, the action must be rejected — not allowed through on a best-effort basis. Fail-open behavior in a governed pipeline creates an audit gap that regulators treat as a compliance failure.
10. Testing Your Integration
The SDK does not ship a mock client. For unit tests, pass a test double into your code: the integration only depends on evaluate() returning an object with blocked, verdict, policy_violations, modifications and decision_id, and you can build a real EvaluationResult from a response dictionary:
from eve_coreguard import EvaluationResult class StubClient: def __init__(self, response: dict): self._response = response def evaluate(self, **kwargs): return EvaluationResult.from_dict(self._response) def test_blocked_loan_is_rejected(monkeypatch): stub = StubClient({ "decision": {"status": "BLOCKED"}, "policy_violations": [{"policy_id": "DTI_LIMIT", "description": "DTI above limit"}], }) monkeypatch.setattr("myservice.client", stub) result = process_loan_request( user={"id": "u_test"}, action={"type": "loan_approval", "amount": 50000}, context={"credit_score": 720, "debt_to_income": 0.52}, ) assert result["error"] == "blocked" assert "DTI_LIMIT" in result["violations"]
For end-to-end tests, call the real API with a test key and ask for the signed evidence with include_evidence=True. Records signed with an asymmetric key verify offline with the public key alone — no shared secret:
import os from eve_coreguard import CoreGuardClient, verify_decision_record, fetch_public_key_pem client = CoreGuardClient(api_key=os.environ["COREGUARD_API_KEY"]) decision = client.evaluate( tenant_id="bank_001", proposed_action={"type": "loan_approval", "amount": 50000}, context={"credit_score": 720, "debt_to_income": 0.30, "employment_verified": True}, include_evidence=True, ) # One call: fetches /.well-known/eve-pubkey if no key is supplied. check = client.verify_evidence_offline(decision.signed_governance) assert check.valid, check.reason # Or verify a STORED record later, with an explicitly pinned public key: pubkey = fetch_public_key_pem("https://api.evecore.com") check = verify_decision_record(stored_record, public_key_pem=pubkey) assert check.valid and check.independently_verifiable # Legacy hmac- records are shared-secret only (NOT third-party verifiable): # verify_decision_record(stored_record, hmac_key=os.environ["COREGUARD_HMAC_KEY"])
Frequently Asked Questions
How long does it take to integrate EVE CoreGuard into an existing LLM pipeline?
The critical path is short: install the package (pip install eve-coreguard), create a CoreGuardClient with your API key, and insert one evaluate() call before the action you want governed. The SDK handles authentication, serialization and retries of 5xx responses. Webhook delivery, async services and offline evidence verification are additional steps.
What environment variables does EVE CoreGuard require?
None: the SDK reads no environment variables. You pass api_key (and optionally base_url, timeout, max_retries) to CoreGuardClient. This guide reads the key from COREGUARD_API_KEY and the optional base URL from COREGUARD_BASE_URL in application code, and stores the webhook signing secret as COREGUARD_WEBHOOK_SECRET.
Can EVE CoreGuard be integrated into an async Python application?
Yes, through a worker thread. The SDK ships a single synchronous client, CoreGuardClient; there is no async client. In an asyncio app such as FastAPI, call it with asyncio.to_thread() (or FastAPI’s run_in_threadpool) so the event loop stays free. The client retries 5xx responses with exponential backoff.
How do I deploy EVE CoreGuard in a Kubernetes environment?
Your service calls the hosted API through the SDK, with COREGUARD_API_KEY injected from a Kubernetes Secret. For enforcement at the mesh or egress layer instead of in code, the EVE sidecar (private preview) runs as an Envoy ext_authz backend and forward proxy inside the pod; contact us for the build and Helm chart.
Ready to Integrate?
Get your sandbox API key and start testing EVE CoreGuard in your pipeline today. No credit card required — provisioned in under 24 hours.