Kubernetes Deployment Guide¶
AI Guardian can run as a shared daemon in a Kubernetes cluster, providing centralized security enforcement for all AI agents running as pods or on developer workstations that connect remotely.
Architecture¶
┌─────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────────────────────────┐ │
│ │ ai-guardian pod │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ ai-guardian daemon │ │ │
│ │ │ REST API: :63152 │ │ │
│ │ │ Hook socket: :63151 │ │ │
│ │ └────────────────────────┘ │ │
│ └──────────────────────────────┘ │
│ │ │
│ ai-guardian Service (ClusterIP) │
└──────────────┼──────────────────────┘
│ NodePort / LoadBalancer / Route
▼
Host tray client polls
/ask/pending for dialogs
The daemon runs as a long-lived pod. IDE hooks (Claude Code, Cursor, etc.) on developer workstations connect to the cluster-exposed REST API. The host tray polls /ask/pending and shows ask-mode dialogs on the desktop (#1342).
Prerequisites¶
kubectlconfigured for your clusterkustomize(orkubectl>= 1.14 which includes it)- For Kind: Kind and Podman Desktop or Docker Desktop
Quick Start — Kind (Local)¶
1. Create Kind Cluster¶
On macOS, Kind uses Podman/Docker networking that requires explicit port mappings:
On Linux, NodePort ports are accessible directly at localhost:nodePort — the default Kind cluster works without extra config:
2. Deploy¶
This applies the base manifests (Deployment + ClusterIP Service + ConfigMap) with the Kind NodePort overlay.
3. Verify¶
All daemon pods must also carry an ownership label. Use a unique owner value for each user or tenant when sharing a namespace:
Configure the tray with the same owner value. Discovery adds this selector to
every Kubernetes API or kubectl query, so credentials are only requested from
pods owned by that configured identity.
Wait for the pod readiness probe to pass (checks GET /api/status on port 63152).
4. Reach the REST API¶
# Linux — NodePort accessible directly
curl http://localhost:63152/api/status
# macOS — same (extraPortMappings route through Kind's control-plane)
curl http://localhost:63152/api/status
Production Deployment¶
Vanilla Kubernetes (LoadBalancer)¶
The Service becomes type LoadBalancer. Your cloud provider assigns an external IP:
kubectl get svc ai-guardian
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# ai-guardian LoadBalancer 10.96.0.10 34.102.x.x 63152:xxx/TCP,63151:xxx/TCP
Configure the tray to connect at http://<EXTERNAL-IP>:63152.
OpenShift / OCP¶
This adds an OpenShift Route with TLS edge termination. The Route provides a stable hostname:
kubectl get route ai-guardian
# NAME HOST/PORT
# ai-guardian ai-guardian-<namespace>.apps.<cluster-domain>
Configure the tray tray-targets.json with the Route URL:
{
"daemons": [
{
"name": "openshift-daemon",
"url": "https://ai-guardian-<namespace>.apps.<cluster-domain>"
}
]
}
Config Customization¶
The ai-guardian-config ConfigMap provides the daemon's ai-guardian.json. Edit before deploying or patch after:
# Edit the base configmap before apply
vim deploy/kubernetes/base/configmap.yaml
kubectl apply -k deploy/kubernetes/overlays/kind/
# Or patch a running deployment
kubectl create configmap ai-guardian-config \
--from-file=ai-guardian.json=my-config.json \
--dry-run=client -o yaml | kubectl apply -f -
kubectl rollout restart deployment/ai-guardian
The initContainer copies the ConfigMap content to an emptyDir volume at startup, so the daemon can write PID files and session state alongside the config file.
Validating the Ask Dialog Forwarding Scenario (#1342)¶
This validates the pull-model: hooks in pods queue ask requests, the host tray polls and shows dialogs on the developer's desktop.
Step 1 — Deploy and verify¶
kubectl apply -k deploy/kubernetes/overlays/kind/
kubectl wait --for=condition=Ready pod -l app=ai-guardian --timeout=60s
curl http://localhost:63152/api/status
Step 2 — Configure the tray¶
Start the tray on your host machine. The daemon at localhost:63152 is auto-discovered by the container discovery mechanism. Check that it appears as a remote target:
If not auto-discovered, add it to ~/.config/ai-guardian/tray-targets.json:
Step 3 — Register the tray with the daemon¶
The tray registers itself automatically when it discovers a remote daemon. Verify:
Step 4 — Trigger an ask dialog from a hook¶
Configure a hook to use ask action, then trigger it from a pod or local Claude Code session connected to the remote daemon. The flow:
- Hook fires in pod → daemon queues ask request at
/ask/pending - Host tray polls
/ask/pending→ finds the request - Tray shows dialog on host desktop
- User chooses Allow/Block → tray POSTs decision back
- Pod daemon receives decision → hook returns to Claude
Manual test (simulate a queued prompt)¶
# Queue a test prompt
curl -s -X POST http://localhost:63152/api/ask/pending \
-H "Content-Type: application/json" \
-d '{"violation_type":"secret_detected","summary":"Test prompt","fallback_action":"block"}'
# Check the tray picks it up (tray logs or UI)
# Respond
curl -s -X POST http://localhost:63152/api/ask/<prompt_id>/decision \
-H "Content-Type: application/json" \
-d '{"decision":"block"}'
Tray Discovery¶
The tray uses discover_all() in src/ai_guardian/daemon/discovery.py to find:
- Local daemon (Unix socket)
- Container daemons (Podman/Docker label ai-guardian.daemon=true)
- Kubernetes pods via kubectl (label app=ai-guardian)
- Manual targets from tray-targets.json
For the Kind deployment, the NodePort at localhost:63152 is discovered via the container/kubernetes path automatically when the tray has cluster access.
Connecting IDE Hooks to the Cluster Daemon¶
Developer workstations can point their Claude Code (or other IDE) hooks at the cluster daemon instead of running a local daemon.
Add to ~/.claude/settings.json or project .claude/settings.json:
Hooks will send requests to the cluster daemon instead of spawning a local one.