Skip to content

feat(ai): deploy omniroute AI gateway and expose in litellm - #4307

Merged
Tanguille merged 2 commits into
mainfrom
feat/omniroute-litellm-gateway
Aug 2, 2026
Merged

feat(ai): deploy omniroute AI gateway and expose in litellm#4307
Tanguille merged 2 commits into
mainfrom
feat/omniroute-litellm-gateway

Conversation

@Tanguille

@Tanguille Tanguille commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Deploy OmniRoute (docker.io/diegosouzapw/omniroute:3.8.49) as a new ai/ app-template HelmRelease — internal ClusterIP only, no ingress.
  • PVC routed through the shared kopiur backup/restore component (matches every other stateful ai/ app).
  • Add a LiteLLMModel (omniroute) pointing at openai/auto — OmniRoute's zero-config smart-routing model, works with no upstream provider keys.

Notes

  • REQUIRE_API_KEY=false internally (ClusterIP-only, litellm is the sole caller) — same unauthenticated-backend convention as the existing sk-sglang-noauth qwen-3.6 entries.
  • Dashboard login (INITIAL_PASSWORD, random, in omniroute-secret) needed once to connect any paid/keyed providers beyond the free auto tier.

Test plan

  • kustomize build kubernetes/apps/ai — builds clean
  • kustomize build kubernetes/apps/ai/omniroute/app — builds clean
  • Flux reconcile + confirm pod healthy, curl /v1/chat/completions via litellm omniroute model alias

Summary by CodeRabbit

  • New Features
    • Added the OmniRoute application to the AI platform.
    • Added routing for automatic OpenAI model requests through OmniRoute.
    • Configured persistent storage, health monitoring, resource limits, and secure non-root operation.
    • Added encrypted configuration for authentication, API access, storage, and initial setup.
    • Enabled automated deployment and readiness management through Flux.

Self-hosted OmniRoute gateway (docker.io/diegosouzapw/omniroute) for
free-tier multi-provider LLM routing, wired into litellm as the
"omniroute" model via its zero-config "auto" routing.
/simplify pass: use the shared kopiur backup/restore component for the
data PVC (matches every other stateful ai/ app) instead of an unbacked
inline PVC, and cut memory/storage requests to what a stateless routing
proxy actually needs.
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Omniroute deployment manifests, encrypted credentials, Flux reconciliation, persistent storage, health settings, and a LiteLLM route to the internal Omniroute service.

Changes

Omniroute deployment

Layer / File(s) Summary
Omniroute workload manifests
kubernetes/apps/ai/omniroute/app/*
Defines the Helm release, service, probes, resource limits, security settings, persistent storage, and SOPS-encrypted secret.
Flux reconciliation wiring
kubernetes/apps/ai/omniroute/ks.yaml, kubernetes/apps/ai/kustomization.yaml
Registers Omniroute with Flux and includes its application Kustomization with repository, readiness, pruning, component, and substitution settings.
LiteLLM routing integration
kubernetes/apps/ai/litellm/instance/models.yaml
Adds the omniroute model route to the internal Omniroute service with a 300-second timeout and zero retries.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Flux
  participant AppKustomization
  participant HelmRelease
  participant OmnirouteService
  participant LiteLLM

  Flux->>AppKustomization: reconcile Omniroute resources
  AppKustomization->>HelmRelease: apply HelmRelease and secret
  HelmRelease->>OmnirouteService: create Omniroute service
  LiteLLM->>OmnirouteService: route openai/auto requests
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the deployment of OmniRoute and its exposure through LiteLLM.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/omniroute-litellm-gateway

Comment @coderabbitai help to get the list of available commands.

@deepsource-io

deepsource-io Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

DeepSource Code Review

We reviewed changes in bd23036...bf8d304 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Aug 2, 2026 4:10p.m. Review ↗
Shell Aug 2, 2026 4:10p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@tanguille-cluster

Copy link
Copy Markdown
@@ (root level) @@
# v1/ServiceAccount/ai/omniroute
! + one document added:
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     app.kubernetes.io/instance: omniroute
+     app.kubernetes.io/managed-by: Helm
+     app.kubernetes.io/name: omniroute
+     helm.toolkit.fluxcd.io/name: omniroute
+     helm.toolkit.fluxcd.io/namespace: ai

@@ (root level) @@
# v1/Service/ai/omniroute
! + one document added:
+ apiVersion: v1
+ kind: Service
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     app.kubernetes.io/instance: omniroute
+     app.kubernetes.io/managed-by: Helm
+     app.kubernetes.io/name: omniroute
+     app.kubernetes.io/service: omniroute
+     helm.toolkit.fluxcd.io/name: omniroute
+     helm.toolkit.fluxcd.io/namespace: ai
+ spec:
+   type: ClusterIP
+   selector:
+     app.kubernetes.io/controller: omniroute
+     app.kubernetes.io/instance: omniroute
+     app.kubernetes.io/name: omniroute
+   ports:
+   - name: http
+     port: 20128
+     protocol: TCP
+     targetPort: 20128

@@ (root level) @@
# apps/v1/Deployment/ai/omniroute
! + one document added:
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+   name: omniroute
+   namespace: ai
+   annotations:
+     reloader.stakater.com/auto: "true"
+   labels:
+     app.kubernetes.io/controller: omniroute
+     app.kubernetes.io/instance: omniroute
+     app.kubernetes.io/managed-by: Helm
+     app.kubernetes.io/name: omniroute
+     helm.toolkit.fluxcd.io/name: omniroute
+     helm.toolkit.fluxcd.io/namespace: ai
+ spec:
+   replicas: 1
+   revisionHistoryLimit: 3
+   selector:
+     matchLabels:
+       app.kubernetes.io/controller: omniroute
+       app.kubernetes.io/instance: omniroute
+       app.kubernetes.io/name: omniroute
+   strategy:
+     type: Recreate
+   template:
+     metadata:
+       labels:
+         app.kubernetes.io/controller: omniroute
+         app.kubernetes.io/instance: omniroute
+         app.kubernetes.io/name: omniroute
+     spec:
+       automountServiceAccountToken: false
+       containers:
+       - name: app
+         image: "docker.io/diegosouzapw/omniroute:3.8.49"
+         env:
+         - name: DATA_DIR
+           value: /app/data
+         - name: PORT
+           value: "20128"
+         - name: REQUIRE_API_KEY
+           value: "false"
+         - name: TZ
+           value: Europe/Brussels
+         livenessProbe:
+           failureThreshold: 3
+           initialDelaySeconds: 0
+           periodSeconds: 10
+           tcpSocket:
+             port: 20128
+           timeoutSeconds: 1
+         readinessProbe:
+           failureThreshold: 3
+           initialDelaySeconds: 0
+           periodSeconds: 10
+           tcpSocket:
+             port: 20128
+           timeoutSeconds: 1
+         resources:
+           limits:
+             cpu: 1000m
+             memory: 512Mi
+           requests:
+             cpu: 100m
+             memory: 128Mi
+         volumeMounts:
+         - name: data
+           mountPath: /app/data
+         envFrom:
+         - secretRef:
+             name: omniroute-secret
+       dnsPolicy: ClusterFirst
+       enableServiceLinks: false
+       hostIPC: false
+       hostNetwork: false
+       hostPID: false
+       securityContext:
+         fsGroup: 1000
+         runAsGroup: 1000
+         runAsNonRoot: true
+         runAsUser: 1000
+       serviceAccountName: omniroute
+       volumes:
+       - name: data
+         persistentVolumeClaim:
+           claimName: omniroute

@tanguille-cluster

Copy link
Copy Markdown
@@ (root level) @@
# litellm.home-operations.com/v1alpha1/LiteLLMModel/ai/omniroute
! + one document added:
+ apiVersion: litellm.home-operations.com/v1alpha1
+ kind: LiteLLMModel
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     kustomize.toolkit.fluxcd.io/name: litellm
+     kustomize.toolkit.fluxcd.io/namespace: ai
+ spec:
+   info:
+     mode: chat
+   modelName: omniroute
+   params:
+     additional:
+       num_retries: 0
+       timeout: 300
+     apiBase: "http://omniroute.ai.svc.cluster.local:20128/v1"
+     apiKey: sk-omniroute-noauth
+     model: openai/auto
+   proxyRef: litellm

@@ (root level) @@
# helm.toolkit.fluxcd.io/v2/HelmRelease/ai/omniroute
! + one document added:
+ apiVersion: helm.toolkit.fluxcd.io/v2
+ kind: HelmRelease
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     kustomize.toolkit.fluxcd.io/name: omniroute
+     kustomize.toolkit.fluxcd.io/namespace: ai
+ spec:
+   chartRef:
+     name: app-template
+     kind: OCIRepository
+   install:
+     crds: CreateReplace
+   interval: 30m
+   rollback:
+     cleanupOnFail: true
+   upgrade:
+     cleanupOnFail: true
+     crds: CreateReplace
+     remediation:
+       remediateLastFailure: true
+       retries: 2
+     strategy:
+       name: RemediateOnFailure
+   values:
+     controllers:
+       omniroute:
+         annotations:
+           reloader.stakater.com/auto: "true"
+         containers:
+           app:
+             resources:
+               limits:
+                 cpu: 1000m
+                 memory: 512Mi
+               requests:
+                 cpu: 100m
+                 memory: 128Mi
+             env:
+               DATA_DIR: /app/data
+               PORT: 20128
+               REQUIRE_API_KEY: "false"
+               TZ: Europe/Brussels
+             image:
+               repository: docker.io/diegosouzapw/omniroute
+               tag: "3.8.49"
+             probes:
+               liveness:
+                 enabled: true
+               readiness:
+                 enabled: true
+             envFrom:
+             - secretRef:
+                 name: "{{ .Release.Name }}-secret"
+     defaultPodOptions:
+       securityContext:
+         fsGroup: 1000
+         runAsGroup: 1000
+         runAsNonRoot: true
+         runAsUser: 1000
+     persistence:
+       data:
+         existingClaim: omniroute
+         globalMounts:
+         - path: /app/data
+     service:
+       app:
+         ports:
+           http:
+             port: 20128

@@ (root level) @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/ai/omniroute
! + one document added:
+ apiVersion: kopiur.home-operations.com/v1alpha1
+ kind: SnapshotPolicy
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     kustomize.toolkit.fluxcd.io/name: omniroute
+     kustomize.toolkit.fluxcd.io/namespace: ai
+ spec:
+   compression:
+     compressor: zstd
+   credentialProjection:
+     enabled: true
+   mover:
+     securityContext:
+       capabilities:
+         add: []
+       runAsGroup: 1000
+       runAsUser: 1000
+   repository:
+     name: kopia-nas
+     kind: ClusterRepository
+   retention:
+     keepDaily: 7
+     keepHourly: 24
+   sources:
+   - pvc:
+       name: omniroute
+     sourcePathOverride: /data
+   verification:
+     quick:
+       schedule:
+         cron: "0 3 * * 0"
+         jitter: 3h
+   volumeSnapshotClassName: csi-ceph-blockpool

@@ (root level) @@
# kopiur.home-operations.com/v1alpha1/SnapshotSchedule/ai/omniroute
! + one document added:
+ apiVersion: kopiur.home-operations.com/v1alpha1
+ kind: SnapshotSchedule
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     kustomize.toolkit.fluxcd.io/name: omniroute
+     kustomize.toolkit.fluxcd.io/namespace: ai
+ spec:
+   policyRef:
+     name: omniroute
+   schedule:
+     cron: "H * * * *"

@@ (root level) @@
# v1/PersistentVolumeClaim/ai/omniroute
! + one document added:
+ apiVersion: v1
+ kind: PersistentVolumeClaim
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     kustomize.toolkit.fluxcd.io/name: omniroute
+     kustomize.toolkit.fluxcd.io/namespace: ai
+ spec:
+   resources:
+     requests:
+       storage: 2Gi
+   storageClassName: ceph-block
+   accessModes:
+   - ReadWriteOnce
+   dataSourceRef:
+     name: omniroute-restore
+     apiGroup: kopiur.home-operations.com
+     kind: Restore

@@ (root level) @@
# kopiur.home-operations.com/v1alpha1/Restore/ai/omniroute-restore
! + one document added:
+ apiVersion: kopiur.home-operations.com/v1alpha1
+ kind: Restore
+ metadata:
+   name: omniroute-restore
+   namespace: ai
+   labels:
+     kustomize.toolkit.fluxcd.io/name: omniroute
+     kustomize.toolkit.fluxcd.io/namespace: ai
+ spec:
+   source:
+     fromPolicy:
+       name: omniroute
+   target:
+     populator: {}
+   credentialProjection:
+     enabled: true
+   policy:
+     onMissingSnapshot: Continue
+   mover:
+     securityContext:
+       runAsGroup: 1000
+       runAsUser: 1000

@@ (root level) @@
# kustomize.toolkit.fluxcd.io/v1/Kustomization/ai/omniroute
! + one document added:
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+   name: omniroute
+   namespace: ai
+   labels:
+     kustomize.toolkit.fluxcd.io/name: cluster-apps
+     kustomize.toolkit.fluxcd.io/namespace: flux-system
+ spec:
+   components:
+   - ../../../../components/kopiur
+   decryption:
+     provider: sops
+     secretRef:
+       name: sops-age
+   deletionPolicy: WaitForTermination
+   interval: 30m
+   patches:
+   - patch: |
+       apiVersion: helm.toolkit.fluxcd.io/v2
+       kind: HelmRelease
+       metadata:
+         name: _
+       spec:
+         install:
+           crds: CreateReplace
+         rollback:
+           cleanupOnFail: true
+         upgrade:
+           cleanupOnFail: true
+           crds: CreateReplace
+           strategy:
+             name: RemediateOnFailure
+           remediation:
+             remediateLastFailure: true
+             retries: 2
+     target:
+       kind: HelmRelease
+       group: helm.toolkit.fluxcd.io
+   path: ./kubernetes/apps/ai/omniroute/app
+   postBuild:
+     substitute:
+       APP: omniroute
+       KOPIUR_PGID: "1000"
+       KOPIUR_PUID: "1000"
+       PVC_CAPACITY: 2Gi
+     substituteFrom:
+     - name: cluster-settings
+       kind: ConfigMap
+       optional: false
+     - name: cluster-secrets
+       kind: Secret
+       optional: false
+   prune: true
+   retryInterval: 2m
+   sourceRef:
+     name: flux-system
+     kind: GitRepository
+     namespace: flux-system
+   targetNamespace: ai
+   timeout: 5m
+   wait: true

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
kubernetes/apps/ai/omniroute/app/helmrelease.yaml (3)

19-22: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Pin the image with a digest.

The image tag 3.8.49 has no digest pin. The hermes HelmRelease in this repo pins images by tag and digest (tag: v2026.7.30@sha256:...). A mutable tag on Docker Hub can be repointed to a different image without a corresponding manifest change, weakening supply-chain guarantees.

Add the @sha256:... digest for docker.io/diegosouzapw/omniroute:3.8.49.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/apps/ai/omniroute/app/helmrelease.yaml` around lines 19 - 22,
Update the image tag in the omniroute HelmRelease to append the verified SHA-256
digest for docker.io/diegosouzapw/omniroute:3.8.49, preserving the existing
repository and tag while pinning the deployment by tag and digest.

34-38: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider a custom health check instead of the default probe.

Liveness and readiness only set enabled: true, without a custom/httpGet spec, unlike the hermes convention of an explicit httpGet check on a status endpoint. This likely falls back to a bare TCP check on the container port, which confirms the port is open but not that the app is actually healthy.

OmniRoute documents internal health-check plumbing (health endpoints, DB connectivity checks), so an HTTP-based probe may be available.
Local Health Check: Monitors internal service availability and database connectivity.

Verify whether OmniRoute exposes a stable health endpoint (for example under /api/health or similar) and use it for custom: true probes, matching the hermes pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/apps/ai/omniroute/app/helmrelease.yaml` around lines 34 - 38,
Update the liveness and readiness probe configuration in the HelmRelease to use
OmniRoute’s documented stable health endpoint, confirming the endpoint path
first. Configure both probes with custom HTTP GET checks matching the hermes
convention, including the appropriate port and health path, instead of relying
on enabled-only default probes.

14-51: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add the missing pod and container security controls.

omniroute does not disable service-account-token mounting, set seccompProfile.type to RuntimeDefault, or set allowPrivilegeEscalation: false. Add these controls to match the repository baseline. Do not enable readOnlyRootFilesystem based only on the Docker guidance for read-only volume mounts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/apps/ai/omniroute/app/helmrelease.yaml` around lines 14 - 51,
Update the omniroute pod security configuration under defaultPodOptions to
disable service-account-token mounting and set seccompProfile.type to
RuntimeDefault, and add allowPrivilegeEscalation: false to the app container’s
securityContext. Do not enable readOnlyRootFilesystem.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@kubernetes/apps/ai/omniroute/app/helmrelease.yaml`:
- Around line 27-30: Add a CiliumNetworkPolicy for the omniroute workload that
restricts ingress to only the litellm pods, while preserving the required
traffic for the unauthenticated /v1 API. Use selectors matching omniroute as the
protected endpoint and litellm as the sole allowed client, and include the
necessary namespace and port constraints.

---

Nitpick comments:
In `@kubernetes/apps/ai/omniroute/app/helmrelease.yaml`:
- Around line 19-22: Update the image tag in the omniroute HelmRelease to append
the verified SHA-256 digest for docker.io/diegosouzapw/omniroute:3.8.49,
preserving the existing repository and tag while pinning the deployment by tag
and digest.
- Around line 34-38: Update the liveness and readiness probe configuration in
the HelmRelease to use OmniRoute’s documented stable health endpoint, confirming
the endpoint path first. Configure both probes with custom HTTP GET checks
matching the hermes convention, including the appropriate port and health path,
instead of relying on enabled-only default probes.
- Around line 14-51: Update the omniroute pod security configuration under
defaultPodOptions to disable service-account-token mounting and set
seccompProfile.type to RuntimeDefault, and add allowPrivilegeEscalation: false
to the app container’s securityContext. Do not enable readOnlyRootFilesystem.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 119a0732-3fab-4a69-b117-d5906866b3bf

📥 Commits

Reviewing files that changed from the base of the PR and between bd23036 and bf8d304.

📒 Files selected for processing (6)
  • kubernetes/apps/ai/kustomization.yaml
  • kubernetes/apps/ai/litellm/instance/models.yaml
  • kubernetes/apps/ai/omniroute/app/helmrelease.yaml
  • kubernetes/apps/ai/omniroute/app/kustomization.yaml
  • kubernetes/apps/ai/omniroute/app/secret.sops.yaml
  • kubernetes/apps/ai/omniroute/ks.yaml
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Tanguille/LLMKube (auto-detected)
  • Tanguille/toolhive (auto-detected)
  • Tanguille/pr-reviewer-action (auto-detected)
  • Tanguille/2x-R9700-RDNA4-GFX1201-sglang-inference (auto-detected)

Comment on lines +27 to +30
# Internal-only ClusterIP — litellm is the sole caller, same
# unauthenticated-backend + non-empty-apiKey convention as the
# qwen-3.6 SGLang model (LiteLLMModel requires apiKey non-empty).
REQUIRE_API_KEY: "false"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- target manifest ---'
cat -n kubernetes/apps/ai/omniroute/app/helmrelease.yaml | sed -n '1,90p'
printf '%s\n' '--- NetworkPolicy manifests and references ---'
find kubernetes -type f \( -name '*.yaml' -o -name '*.yml' \) -print0 |
  xargs -0 grep -nH -E 'kind:[[:space:]]*NetworkPolicy|networkpolicies|omniroute|litellm' || true
printf '%s\n' '--- app tree ---'
find kubernetes/apps/ai -maxdepth 4 -type f -print | sort

Repository: Tanguille/cluster

Length of output: 12622


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
for p in Path("kubernetes").rglob("*.yaml"):
    text = p.read_text(errors="replace")
    if "kind: NetworkPolicy" in text or "networking.k8s.io/v1" in text:
        print(f"{p}:")
        for i, line in enumerate(text.splitlines(), 1):
            if "NetworkPolicy" in line or "networking.k8s.io/v1" in line or "namespaceSelector" in line or "podSelector" in line or "policyTypes" in line or "ingress:" in line:
                print(f"  {i}: {line}")
PY
printf '%s\n' '--- Flux/Kustomization context for AI apps ---'
find kubernetes/apps/ai -maxdepth 5 -type f -print | sort

Repository: Tanguille/cluster

Length of output: 5748


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- cluster-wide network controls and policy references ---'
rg -n -i --glob '!*.sops.yaml' \
  'NetworkPolicy|network-policy|networkpolicy|default.?deny|CiliumNetworkPolicy|GlobalNetworkPolicy|calico|cilium|kube.?proxy|network.?policy' \
  kubernetes talos .github 2>/dev/null || true
printf '%s\n' '--- LiteLLM and OmniRoute workload/service configuration ---'
cat -n kubernetes/apps/ai/litellm/instance/proxy.yaml | sed -n '1,110p'
cat -n kubernetes/apps/ai/litellm/instance/models.yaml | sed -n '55,78p'
cat -n kubernetes/apps/ai/omniroute/app/kustomization.yaml
cat -n kubernetes/apps/ai/omniroute/ks.yaml

Repository: Tanguille/cluster

Length of output: 12843


🏁 Script executed:

#!/bin/bash
set -e
for f in \
  kubernetes/apps/kube-system/network-policies/app/deny-apiserver-egress.yaml \
  kubernetes/apps/kube-system/network-policies/app/database-ingress.yaml \
  kubernetes/apps/kube-system/network-policies/app/kustomization.yaml \
  kubernetes/apps/kube-system/network-policies/ks.yaml \
  kubernetes/apps/kube-system/kustomization.yaml
do
  if [ -f "$f" ]; then
    printf '\n--- %s ---\n' "$f"
    cat -n "$f"
  fi
done
printf '\n--- all policy selectors and namespace selectors ---\n'
rg -n -A8 -B3 'kind: CiliumNetworkPolicy|endpointSelector:|namespaceSelector:|matchLabels:|enableDefaultDeny:' \
  kubernetes/apps/kube-system/network-policies kubernetes --glob '*.yaml' 2>/dev/null || true

Repository: Tanguille/cluster

Length of output: 50374


Add a CiliumNetworkPolicy for omniroute.

No policy applies to the ai namespace. With REQUIRE_API_KEY set to "false", any pod that can reach the omniroute Service can call its unauthenticated /v1 API. Restrict ingress to the litellm pods.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/apps/ai/omniroute/app/helmrelease.yaml` around lines 27 - 30, Add
a CiliumNetworkPolicy for the omniroute workload that restricts ingress to only
the litellm pods, while preserving the required traffic for the unauthenticated
/v1 API. Use selectors matching omniroute as the protected endpoint and litellm
as the sole allowed client, and include the necessary namespace and port
constraints.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

AI Automated Review

Analysis engine: qwen-3.6-fast@http://litellm.ai.svc.cluster.local/v1 (openai)

Recommendation: Approve with minor notes for post-merge verification.

This PR adds the omniroute AI gateway as a new app in the ai namespace, wired through Flux Kustomization and exposed to LiteLLM via a LiteLLMModel CR. The implementation follows established repository conventions for app-template HelmReleases, SOPS secrets, and kopiur backup integration.

Change-by-change findings

kubernetes/apps/ai/kustomization.yaml

  • Adds ./omniroute/ks.yaml to resources list. Consistent with existing patterns (hermes, litellm, memini, etc.).

kubernetes/apps/ai/litellm/instance/models.yaml

  • Adds LiteLLMModel named omniroute pointing at openai/auto on http://omniroute.ai.svc.cluster.local:20128/v1.
  • Uses apiKey: sk-omniroute-noauth to satisfy LiteLLM's OpenAI provider requirement while OmniRoute runs with REQUIRE_API_KEY=false. This matches the documented convention used by qwen-3.6 SGLang models (sk-sglang-noauth).
  • Sets timeout: 300 and num_retries: 0, which is reasonable for a routing proxy that delegates to upstream providers.

kubernetes/apps/ai/omniroute/app/helmrelease.yaml

  • Deploys docker.io/diegosouzapw/omniroute:3.8.49 (latest release as of 2026-07-30).
  • Internal-only ClusterIP service on port 20128; no Ingress or external exposure.
  • Security context: runAsNonRoot: true, UID/GID 1000 — consistent with other ai/ apps.
  • Resources: 100m–1 CPU, 128Mi–512Mi memory — reasonable for a lightweight routing proxy.
  • Persistence via existing PVC omniroute mounted at /app/data.
  • Secrets loaded from omniroute-secret via envFrom.

kubernetes/apps/ai/omniroute/app/kustomization.yaml

  • Standard Kustomization referencing helmrelease.yaml and secret.sops.yaml.

kubernetes/apps/ai/omniroute/app/secret.sops.yaml

  • SOPS-encrypted secret with JWT_SECRET, API_KEY_SECRET, STORAGE_ENCRYPTION_KEY, and INITIAL_PASSWORD.
  • Uses AGE encryption with recipient matching cluster key (age1pq1f...), consistent with repo standards.
  • mac_only_encrypted: true and SOPS v3.13.3 — standard for this repo.
  • No plaintext secrets committed; all values are encrypted.

kubernetes/apps/ai/omniroute/ks.yaml

  • Flux Kustomization targeting ./kubernetes/apps/ai/omniroute/app.
  • Includes ../../../../components/kopiur component for backup/restore, matching other stateful ai/ apps.
  • Post-build substitution sets PVC_CAPACITY: 2Gi, KOPIUR_PUID: "1000", KOPIUR_PGID: "1000" — consistent with the container's UID 1000.

Standards Compliance

  • Conventional commit title: feat(ai): deploy omniroute AI gateway and expose in litellm — compliant.
  • App-template pattern: Follows bjw-s-labs/app-template schema, uses chartRef to OCIRepository, matches peer apps (hermes, memini, odysseus).
  • SOPS encryption: All secrets encrypted with AGE; no plaintext keys or passwords in diff. Complies with AGENTS.md "Never commit secrets or age.key" rule.
  • Security context: runAsNonRoot: true, explicit UID/GID — compliant.
  • Kopiur integration: Uses shared component with post-build substitution, matching learned-preferences convention.
  • No direct cluster edits: All changes via GitOps manifests.

Must-Check Items

  1. Validate manifest against target cluster version: Manifests use standard Flux/Kustomize APIs (helm.toolkit.fluxcd.io/v2, kustomize.toolkit.fluxcd.io/v1) compatible with current Flux installations. No version-specific features detected.
  2. Check for resource quota / limit changes: New app requests 100m CPU / 128Mi memory, limits 1 CPU / 512Mi. No existing quotas modified. Verify namespace-level quotas accommodate this if enforced.
  3. Review for path traversal vulnerabilities: OmniRoute is an AI routing proxy, not a file-serving application. No user-controlled paths exposed in manifests. Risk flag noted but not applicable to this change type.
  4. Test with edge-case paths (null bytes, symlinks): Not applicable — no filesystem path handling in manifests.
  5. Verify secrets are not logged or exposed in diffs: All secret values are SOPS-encrypted (ENC[AES256_GCM,...]). No plaintext secrets in diff. ✅
  6. Check secret rotation impact: New secret, no rotation mechanism defined yet. Initial password (INITIAL_PASSWORD) is random and intended for one-time dashboard setup. Document rotation procedure when needed.

Unknowns / Needs Verification

  • OmniRoute v3.8.49 compatibility: Image tag 3.8.49 corresponds to the latest GitHub release (2026-07-30). No breaking changes identified in release history, but upstream release notes were not fetched. If concerns arise, verify against OmniRoute releases.
  • PVC provisioning: The omniroute PVC is referenced via existingClaim: omniroute. Ensure the PVC exists or is created by the kopiur component. The kopiur component typically creates PVCS via post-build substitution, so this should be handled.
  • Flux reconciliation: PR body notes Flux reconcile + health check as pending test step. Verify pod reaches Ready state and LiteLLM can reach http://omniroute.ai.svc.cluster.local:20128/v1/chat/completions after merge.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline findings from the automated review (summary in the sticky comment).

Comment thread kubernetes/apps/ai/omniroute/app/helmrelease.yaml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant