Migration Guide
Migrate existing AI applications to OGuardAI with minimal code changes
You do not need to rewrite your application. OGuardAI is designed to slot into existing AI workflows with minimal changes. Most migrations require changing one line of code or adding a thin middleware layer.
Migration Paths
| Current Setup | Migration Path | Effort |
|---|---|---|
| OpenAI / Anthropic API | Set base_url to the OGuardAI proxy and add the proxy key header | 2 lines |
| LangChain agent | Add OGuardAI LangChain adapter | 3 lines |
| Vercel AI SDK | Add OGuardAI Vercel AI adapter | 3 lines |
| Python backend (FastAPI) | Add OGuardAI middleware | 5 lines |
| Node.js backend (Express) | Add OGuardAI middleware | 5 lines |
| Custom HTTP pipeline | Call transform before LLM, rehydrate after | 10 lines |
| RAG pipeline | Use 4-step API (transform, index, search, rehydrate) | Replace ingestion step |
Migration 1: OpenAI Proxy (Lowest Effort)
If your application calls the OpenAI or Anthropic API directly, point its base_url at OGuardAI's proxy and add the X-GuardAI-API-Key header to authenticate to the proxy. Those two lines are the only changes.
Before:
from openai import OpenAI
client = OpenAI(api_key="sk-...")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Help customer Erika Mustermann (erika.mustermann@example.com)"}]
)
# PII sent directly to OpenAIAfter:
from openai import OpenAI
import os
client = OpenAI(
api_key="sk-...",
base_url="http://localhost:8081/v1", # <-- point at the proxy
default_headers={"X-GuardAI-API-Key": os.environ["GUARDAI_PROXY_API_KEY"]}, # <-- authenticate to the proxy
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Help customer Erika Mustermann (erika.mustermann@example.com)"}]
)
# OpenAI sees: "Help customer `{{person:p_001:ad4f97591c16}}` (`{{email:e_001:1bcaef1a4aff}}`)"
# Response is automatically restored before reaching your codeSetup:
Availability: The proxy image below is private today and provided under a written agreement with Oronts. Request access at /request.
# Generate one proxy key; the proxy and the client (above) must present the same value.
export GUARDAI_PROXY_API_KEY=$(openssl rand -hex 32)
# Start the proxy from the published image; --network host serves it on the host's localhost:8081.
docker run --rm --network host \
-e GUARDAI_SESSION_SECRET=$(openssl rand -base64 32) \
-e GUARDAI_PROXY_API_KEY \
ghcr.io/oronts/oguardai/proxy:0.1.0 \
--target https://api.openai.com --policy default --port 8081 \
--single-tenant --rate-limit-enabledRun the client in the same shell where you exported GUARDAI_PROXY_API_KEY, so its
X-GuardAI-API-Key header matches the proxy. A non-dev proxy requires a proxy key
(GUARDAI_PROXY_API_KEY) and a bounded rate limiter (--rate-limit-enabled), and single-tenant auth
needs the --single-tenant acknowledgement. For a throwaway local proxy you can instead set
GUARDAI_PROXY_DEV=1, which waives the key and limiter. See the OpenAI Proxy
guide for the full setup. Streaming, tool calls, and all message roles are handled transparently.
Migration 2: Python Middleware (FastAPI)
Add OGuardAI as FastAPI middleware. Incoming text is tokenized; outgoing responses are restored. Route handler code does not change.
# Add transform/rehydrate calls around your LLM interaction:
from guardai_sdk import OGuardAIClient
guardai = OGuardAIClient(base_url="http://localhost:3000")
@app.post("/chat")
async def chat(message: str):
# Transform: mask PII before sending to LLM
result = guardai.transform(message)
llm_reply = call_llm(result.safe_text)
# Rehydrate: restore PII in the response
restored = guardai.rehydrate(llm_reply, session_state=result.session_state)
return {"reply": restored.restored_text}Migration 3: Node.js Middleware (Express)
Same pattern for Express. Add the middleware; routes stay unchanged.
// Add transform/rehydrate calls around your LLM interaction:
import { OGuardAIClient } from "@oguardai/sdk";
const guardai = new OGuardAIClient({ baseUrl: "http://localhost:3000" });
app.post("/chat", async (req, res) => {
// Transform: mask PII before sending to LLM
const result = await guardai.transform({ input: req.body.message });
const reply = await callLlm(result.safe_text);
// Rehydrate: restore PII in the response
const restored = await guardai.rehydrate({ output: reply, session_state: result.session_state! });
res.json({ reply: restored.restored_text });
});What Changes vs. What Stays the Same
| Aspect | Changes | Stays the Same |
|---|---|---|
| LLM calls | Routed through OGuardAI (proxy or middleware) | Same API, same models, same prompts |
| Application code | 1-5 lines added | Business logic, routes, database queries |
| LLM provider | Receives tokenized text instead of raw PII | Same provider, same billing, same models |
| Output quality | The model receives semantic tokens plus any policy-permitted metadata (gender, formality, language) instead of raw PII; validate output quality against your own inputs | Tone, style, language, format |
| Infrastructure | One new service (OGuardAI binary or container) | Everything else |
| Authentication | Add OGuardAI API key to config | Your existing auth unchanged |
Rollback and Gradual Rollout
Shadow Mode (Staging Only)
Enable shadow mode in a non-production environment to measure detection coverage before you widen the rollout:
transform:
shadow_mode: trueShadow mode does not weaken protection. The full pipeline still runs and safe_text is still tokenized; the response simply carries an extra shadow block for comparison: a keyed HMAC fingerprint of the input (stable only within one running process, so two responses are comparable only there), the original and protected character lengths, and the number of entities masked. The original text is never returned. It is a dev-only diagnostic, and production validation rejects it as a fatal startup error, so run it in staging while you review detection counts.
Recommended Rollout Sequence
- Pick one non-critical AI workflow and route it through OGuardAI in shadow mode in a staging environment.
- Run shadow mode for 1-2 weeks. Review the per-request masked-entity counts and length deltas against your own corpus.
- Set
transform.shadow_mode: falsebefore promoting to production; the server refuses to start otherwise. Monitor for regressions. - Expand to additional workflows with per-workflow policies.
- Add the NER sidecar if you need person/company/location detection beyond regex.
Emergency Rollback
- Proxy mode: Change
base_urlback to the LLM provider URL. One line. - Middleware mode: Remove the middleware line. Redeploy.
- No client-side data migration. By default session state travels in client-held sealed blobs, not a server store, so removing OGuardAI from the request path restores your prior data flow; an optional Redis session backend for multi-replica deployments instead holds that state server-side under a TTL. OGuardAI's own operational stores are separate and persist independently of the rollback: the durable audit log (
audit.backend: file), the revocation and request-replay stores (Redis in a multi-replica deployment), and any RAG corpus. Back up and retire those on your own schedule.