diff --git a/casa-explorer-ui/src/components/mas/index.ts b/casa-explorer-ui/src/components/mas/index.ts
index 7103f82b..be56047d 100644
--- a/casa-explorer-ui/src/components/mas/index.ts
+++ b/casa-explorer-ui/src/components/mas/index.ts
@@ -16,4 +16,3 @@
export {MASTable, MASDataTable, createMASColumns} from './mas-list';
export {MASInfoTab, MASAppsTab, MASScopesTab, MASTracesTab, MASDenyConditionsTab, MASAppsTable} from './mas-details';
-export {MASGraphView} from './mas-graph';
diff --git a/casa-explorer-ui/src/components/mas/mas-details/mas-apps-table.tsx b/casa-explorer-ui/src/components/mas/mas-details/mas-apps-table.tsx
index 577a8207..5668ca82 100644
--- a/casa-explorer-ui/src/components/mas/mas-details/mas-apps-table.tsx
+++ b/casa-explorer-ui/src/components/mas/mas-details/mas-apps-table.tsx
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import {useCallback, useMemo, useState} from 'react';
+import {lazy, Suspense, useCallback, useMemo, useState} from 'react';
import type React from 'react';
import {useNavigate} from 'react-router-dom';
import {PATHS} from '@/router/paths';
@@ -42,7 +42,7 @@ import {Button} from '@/components/ui/button';
import {Card} from '@/components/ui/card';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle} from '@/components/ui/dialog';
import {Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetClose} from '@/components/ui/sheet';
-import {MASGraphView} from '@/components/mas/mas-graph';
+const MASGraphView = lazy(() => import('@/components/mas/mas-graph/mas-graph-view').then(m => ({default: m.MASGraphView})));
import {toast} from 'sonner';
import type {MAS} from '@/types/mas.types';
import type {App, AppType, Tool} from '@/types/app.types';
@@ -348,13 +348,15 @@ export function MASAppsTable({mas, apps}: MASAppsTableProps) {
{view === 'graph' && (
-
+
+
+
)}
diff --git a/casa-explorer-ui/src/router/router.tsx b/casa-explorer-ui/src/router/router.tsx
index 9d908228..17461bad 100644
--- a/casa-explorer-ui/src/router/router.tsx
+++ b/casa-explorer-ui/src/router/router.tsx
@@ -14,27 +14,31 @@
* limitations under the License.
*/
+import {lazy, Suspense} from 'react';
import {createBrowserRouter} from 'react-router-dom';
import {AppLayout} from '@/components/app-layout';
-import {DashboardPage} from '@/pages/dashboard-page';
-import {MASPage} from '@/pages/mas/mas-page';
-import {MASDetailPage} from '@/pages/mas/mas-detail-page';
-import {AuthRequestsPage} from '@/pages/auth-requests/auth-requests-page';
-import {AuthRequestDetailPage} from '@/pages/auth-requests/auth-request-detail-page';
-import {NotFoundPage} from '@/pages/not-found-page';
import {PATHS} from './paths';
+const DashboardPage = lazy(() => import('@/pages/dashboard-page').then(m => ({default: m.DashboardPage})));
+const MASPage = lazy(() => import('@/pages/mas/mas-page').then(m => ({default: m.MASPage})));
+const MASDetailPage = lazy(() => import('@/pages/mas/mas-detail-page').then(m => ({default: m.MASDetailPage})));
+const AuthRequestsPage = lazy(() => import('@/pages/auth-requests/auth-requests-page').then(m => ({default: m.AuthRequestsPage})));
+const AuthRequestDetailPage = lazy(() => import('@/pages/auth-requests/auth-request-detail-page').then(m => ({default: m.AuthRequestDetailPage})));
+const NotFoundPage = lazy(() => import('@/pages/not-found-page').then(m => ({default: m.NotFoundPage})));
+
+const S = ({children}: {children: React.ReactNode}) => {children};
+
export const router = createBrowserRouter([
{
path: PATHS.dashboard,
element: ,
children: [
- {index: true, element: },
- {path: PATHS.mas.list, element: },
- {path: PATHS.mas.detailPattern, element: },
- {path: PATHS.authRequests.list, element: },
- {path: PATHS.authRequests.detailPattern, element: },
- {path: '*', element: }
+ {index: true, element: },
+ {path: PATHS.mas.list, element: },
+ {path: PATHS.mas.detailPattern, element: },
+ {path: PATHS.authRequests.list, element: },
+ {path: PATHS.authRequests.detailPattern, element: },
+ {path: '*', element: }
]
}
]);
diff --git a/casa-explorer-ui/vite.config.ts b/casa-explorer-ui/vite.config.ts
index 57a63a53..ac5c7fc9 100644
--- a/casa-explorer-ui/vite.config.ts
+++ b/casa-explorer-ui/vite.config.ts
@@ -27,6 +27,27 @@ export default defineConfig({
'@': path.resolve(__dirname, './src')
}
},
+ build: {
+ chunkSizeWarningLimit: 1600,
+ rollupOptions: {
+ output: {
+ manualChunks(id) {
+ if (!id.includes('node_modules')) return;
+ if (id.includes('elkjs') || id.includes('@xyflow')) return 'vendor-flow';
+ if (id.includes('recharts') || id.includes('/d3-') || id.includes('/d3/')) return 'vendor-charts';
+ if (id.includes('@dnd-kit')) return 'vendor-dnd';
+ if (id.includes('@tanstack')) return 'vendor-query';
+ if (id.includes('@tabler') || id.includes('lucide-react')) return 'vendor-icons';
+ if (id.includes('react-hook-form') || id.includes('@hookform') || id.includes('/zod/')) return 'vendor-forms';
+ if (
+ id.includes('@radix-ui') || id.includes('radix-ui') ||
+ id.includes('/cmdk/') || id.includes('/vaul/')
+ ) return 'vendor-radix';
+ if (id.includes('react-router') || id.includes('react-dom') || id.match(/\/react\//) || id.includes('/sonner/') || id.includes('next-themes')) return 'vendor-react';
+ }
+ }
+ }
+ },
server: {
port: 1234,
open: true
diff --git a/demo_new/helm/templates/banking-data-agent/deployment.yaml b/demo_new/helm/templates/banking-data-agent/deployment.yaml
index 17d08a48..43d4bdbc 100644
--- a/demo_new/helm/templates/banking-data-agent/deployment.yaml
+++ b/demo_new/helm/templates/banking-data-agent/deployment.yaml
@@ -12,43 +12,45 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+{{- range list .Values.bankingDataAgentA .Values.bankingDataAgentB }}
+---
apiVersion: apps/v1
kind: Deployment
metadata:
- name: {{ .Values.bankingDataAgent.serviceName }}
- namespace: {{ .Release.Namespace }}
+ name: {{ .serviceName }}
+ namespace: {{ $.Release.Namespace }}
labels:
- app: {{ .Values.bankingDataAgent.serviceName }}
+ app: {{ .serviceName }}
version: v1
spec:
- replicas: {{ .Values.bankingDataAgent.replicas }}
+ replicas: {{ .replicas }}
selector:
matchLabels:
- app: {{ .Values.bankingDataAgent.serviceName }}
+ app: {{ .serviceName }}
template:
metadata:
labels:
- app: {{ .Values.bankingDataAgent.serviceName }}
+ app: {{ .serviceName }}
version: v1
spec:
- {{- if .Values.bankingDataAgent.docker.registry }}
+ {{- if .docker.registry }}
imagePullSecrets:
- name: regcred
{{- end }}
containers:
- - name: {{ .Values.bankingDataAgent.serviceName }}
- image: {{ if .Values.bankingDataAgent.docker.registry }}{{ .Values.bankingDataAgent.docker.registry }}/{{ end }}{{ .Values.bankingDataAgent.docker.image }}:{{ .Values.bankingDataAgent.tagversion }}
- imagePullPolicy: {{ if .Values.bankingDataAgent.docker.registry }}Always{{ else }}Never{{ end }}
+ - name: {{ .serviceName }}
+ image: {{ if .docker.registry }}{{ .docker.registry }}/{{ end }}{{ .docker.image }}:{{ .tagversion }}
+ imagePullPolicy: {{ if .docker.registry }}Always{{ else }}Never{{ end }}
securityContext:
runAsNonRoot: true
runAsUser: 999
allowPrivilegeEscalation: false
ports:
- - containerPort: {{ .Values.bankingDataAgent.servicePort }}
+ - containerPort: {{ .servicePort }}
name: http
env:
- name: MCP_SERVER_URL
- value: {{ .Values.bankingDataAgent.mcpServerUrl | quote }}
+ value: {{ .mcpServerUrl | quote }}
- name: OPENAI_API_BASE
valueFrom:
secretKeyRef:
@@ -66,3 +68,4 @@ spec:
limits:
memory: "512Mi"
cpu: "500m"
+{{- end }}
diff --git a/demo_new/helm/templates/banking-data-agent/service.yaml b/demo_new/helm/templates/banking-data-agent/service.yaml
index 5f6b329b..0d747f71 100644
--- a/demo_new/helm/templates/banking-data-agent/service.yaml
+++ b/demo_new/helm/templates/banking-data-agent/service.yaml
@@ -12,17 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+{{- range list .Values.bankingDataAgentA .Values.bankingDataAgentB }}
+---
apiVersion: v1
kind: Service
metadata:
- name: {{ .Values.bankingDataAgent.serviceName }}
- namespace: {{ .Release.Namespace }}
+ name: {{ .serviceName }}
+ namespace: {{ $.Release.Namespace }}
labels:
- app: {{ .Values.bankingDataAgent.serviceName }}
+ app: {{ .serviceName }}
spec:
selector:
- app: {{ .Values.bankingDataAgent.serviceName }}
+ app: {{ .serviceName }}
ports:
- name: http
- port: {{ .Values.bankingDataAgent.servicePort }}
+ port: {{ .servicePort }}
targetPort: http
+{{- end }}
diff --git a/demo_new/helm/templates/mas.yaml b/demo_new/helm/templates/mas.yaml
index 8250a703..c4a7ce67 100644
--- a/demo_new/helm/templates/mas.yaml
+++ b/demo_new/helm/templates/mas.yaml
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Safe MAS: banking-assistant-safe, banking-data-agent, banking-payments-agent, banking-beneficiary-agent, banking-mcp.
+# Safe MAS: banking-assistant-safe, banking-data-agent-a, banking-payments-agent-a, banking-beneficiary-agent-safe, banking-mcp-a.
apiVersion: casa.io/v1alpha1
kind: MultiAgentSystem
metadata:
@@ -23,7 +23,7 @@ spec:
enabledToolChecks: {{ toYaml .Values.masSafe.enabledToolChecks | nindent 4 }}
llm_host: {{ .Values.masSafe.llm_host }}
apps:
- - name: {{ .Values.bankingAssistantSafe.serviceName }}
+ - name: {{ .Values.bankingAssistantSafe.appName }}
type: agent
baseUrl:
host: {{ .Values.bankingAssistantSafe.serviceName }}:{{ .Values.bankingAssistantSafe.servicePort }}
@@ -31,23 +31,23 @@ spec:
kubernetesWorkloadName: {{ .Values.bankingAssistantSafe.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.bankingDataAgent.serviceName }}
+ - name: {{ .Values.bankingDataAgentA.appName }}
type: agent
baseUrl:
- host: {{ .Values.bankingDataAgent.serviceName }}:{{ .Values.bankingDataAgent.servicePort }}
+ host: {{ .Values.bankingDataAgentA.serviceName }}:{{ .Values.bankingDataAgentA.servicePort }}
scheme: http
- kubernetesWorkloadName: {{ .Values.bankingDataAgent.serviceName }}
+ kubernetesWorkloadName: {{ .Values.bankingDataAgentA.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.bankingPaymentsAgent.serviceName }}
+ - name: {{ .Values.bankingPaymentsAgentA.appName }}
type: agent
baseUrl:
- host: {{ .Values.bankingPaymentsAgent.serviceName }}:{{ .Values.bankingPaymentsAgent.servicePort }}
+ host: {{ .Values.bankingPaymentsAgentA.serviceName }}:{{ .Values.bankingPaymentsAgentA.servicePort }}
scheme: http
- kubernetesWorkloadName: {{ .Values.bankingPaymentsAgent.serviceName }}
+ kubernetesWorkloadName: {{ .Values.bankingPaymentsAgentA.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.bankingBeneficiaryAgent.serviceName }}
+ - name: {{ .Values.bankingBeneficiaryAgent.appName }}
type: agent
baseUrl:
host: {{ .Values.bankingBeneficiaryAgent.serviceName }}:{{ .Values.bankingBeneficiaryAgent.servicePort }}
@@ -55,17 +55,17 @@ spec:
kubernetesWorkloadName: {{ .Values.bankingBeneficiaryAgent.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.mcp.serviceName }}
+ - name: {{ .Values.mcpA.appName }}
type: mcp_server
baseUrl:
- host: {{ .Values.mcp.serviceName }}:{{ .Values.mcp.servicePort }}
+ host: {{ .Values.mcpA.serviceName }}:{{ .Values.mcpA.servicePort }}
scheme: http
- kubernetesWorkloadName: {{ .Values.mcp.serviceName }}
+ kubernetesWorkloadName: {{ .Values.mcpA.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
---
-# Compromised MAS: banking-assistant-compromised, banking-beneficiary-agent-compromised, banking-data-agent, banking-payments-agent, banking-mcp.
+# Compromised MAS: banking-assistant-compromised, banking-beneficiary-agent-compromised, banking-data-agent-b, banking-payments-agent-b, banking-mcp-b.
apiVersion: casa.io/v1alpha1
kind: MultiAgentSystem
metadata:
@@ -84,23 +84,23 @@ spec:
kubernetesWorkloadName: {{ .Values.bankingAssistantCompromised.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.bankingDataAgent.serviceName }}
+ - name: {{ .Values.bankingDataAgentB.appName }}
type: agent
baseUrl:
- host: {{ .Values.bankingDataAgent.serviceName }}:{{ .Values.bankingDataAgent.servicePort }}
+ host: {{ .Values.bankingDataAgentB.serviceName }}:{{ .Values.bankingDataAgentB.servicePort }}
scheme: http
- kubernetesWorkloadName: {{ .Values.bankingDataAgent.serviceName }}
+ kubernetesWorkloadName: {{ .Values.bankingDataAgentB.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.bankingPaymentsAgent.serviceName }}
+ - name: {{ .Values.bankingPaymentsAgentB.appName }}
type: agent
baseUrl:
- host: {{ .Values.bankingPaymentsAgent.serviceName }}:{{ .Values.bankingPaymentsAgent.servicePort }}
+ host: {{ .Values.bankingPaymentsAgentB.serviceName }}:{{ .Values.bankingPaymentsAgentB.servicePort }}
scheme: http
- kubernetesWorkloadName: {{ .Values.bankingPaymentsAgent.serviceName }}
+ kubernetesWorkloadName: {{ .Values.bankingPaymentsAgentB.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.bankingBeneficiaryAgentCompromised.serviceName }}
+ - name: {{ .Values.bankingBeneficiaryAgentCompromised.appName }}
type: agent
baseUrl:
host: {{ .Values.bankingBeneficiaryAgentCompromised.serviceName }}:{{ .Values.bankingBeneficiaryAgentCompromised.servicePort }}
@@ -108,11 +108,11 @@ spec:
kubernetesWorkloadName: {{ .Values.bankingBeneficiaryAgentCompromised.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
- - name: {{ .Values.mcp.serviceName }}
+ - name: {{ .Values.mcpB.appName }}
type: mcp_server
baseUrl:
- host: {{ .Values.mcp.serviceName }}:{{ .Values.mcp.servicePort }}
+ host: {{ .Values.mcpB.serviceName }}:{{ .Values.mcpB.servicePort }}
scheme: http
- kubernetesWorkloadName: {{ .Values.mcp.serviceName }}
+ kubernetesWorkloadName: {{ .Values.mcpB.serviceName }}
httpRequestSchema:
promptFieldJsonPath: "{.conversation}"
diff --git a/demo_new/helm/templates/mcp/deployment.yaml b/demo_new/helm/templates/mcp/deployment.yaml
index 6621d930..cd6f211f 100644
--- a/demo_new/helm/templates/mcp/deployment.yaml
+++ b/demo_new/helm/templates/mcp/deployment.yaml
@@ -12,39 +12,41 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+{{- range list .Values.mcpA .Values.mcpB }}
+---
apiVersion: apps/v1
kind: Deployment
metadata:
- name: {{ .Values.mcp.serviceName }}
- namespace: {{ .Release.Namespace }}
+ name: {{ .serviceName }}
+ namespace: {{ $.Release.Namespace }}
labels:
- app: {{ .Values.mcp.serviceName }}
+ app: {{ .serviceName }}
version: v1
spec:
- replicas: {{ .Values.mcp.replicas }}
+ replicas: {{ .replicas }}
selector:
matchLabels:
- app: {{ .Values.mcp.serviceName }}
+ app: {{ .serviceName }}
template:
metadata:
labels:
- app: {{ .Values.mcp.serviceName }}
+ app: {{ .serviceName }}
version: v1
spec:
- {{- if .Values.mcp.docker.registry }}
+ {{- if .docker.registry }}
imagePullSecrets:
- name: regcred
{{- end }}
containers:
- - name: {{ .Values.mcp.serviceName }}
- image: {{ if .Values.mcp.docker.registry }}{{ .Values.mcp.docker.registry }}/{{ end }}{{ .Values.mcp.docker.image }}:{{ .Values.mcp.tagversion }}
- imagePullPolicy: {{ if .Values.mcp.docker.registry }}Always{{ else }}Never{{ end }}
+ - name: {{ .serviceName }}
+ image: {{ if .docker.registry }}{{ .docker.registry }}/{{ end }}{{ .docker.image }}:{{ .tagversion }}
+ imagePullPolicy: {{ if .docker.registry }}Always{{ else }}Never{{ end }}
securityContext:
runAsNonRoot: true
runAsUser: 999
allowPrivilegeEscalation: false
ports:
- - containerPort: {{ .Values.mcp.servicePort }}
+ - containerPort: {{ .servicePort }}
name: http
resources:
requests:
@@ -53,3 +55,4 @@ spec:
limits:
memory: "256Mi"
cpu: "250m"
+{{- end }}
diff --git a/demo_new/helm/templates/mcp/service.yaml b/demo_new/helm/templates/mcp/service.yaml
index 5a00cd5f..fd78dbb0 100644
--- a/demo_new/helm/templates/mcp/service.yaml
+++ b/demo_new/helm/templates/mcp/service.yaml
@@ -12,17 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+{{- range list .Values.mcpA .Values.mcpB }}
+---
apiVersion: v1
kind: Service
metadata:
- name: {{ .Values.mcp.serviceName }}
- namespace: {{ .Release.Namespace }}
+ name: {{ .serviceName }}
+ namespace: {{ $.Release.Namespace }}
labels:
- app: {{ .Values.mcp.serviceName }}
+ app: {{ .serviceName }}
spec:
selector:
- app: {{ .Values.mcp.serviceName }}
+ app: {{ .serviceName }}
ports:
- name: http
- port: {{ .Values.mcp.servicePort }}
+ port: {{ .servicePort }}
targetPort: http
+{{- end }}
diff --git a/demo_new/helm/templates/payments-agent/deployment.yaml b/demo_new/helm/templates/payments-agent/deployment.yaml
index 3bf5de80..e4b030a1 100644
--- a/demo_new/helm/templates/payments-agent/deployment.yaml
+++ b/demo_new/helm/templates/payments-agent/deployment.yaml
@@ -12,43 +12,45 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+{{- range list .Values.bankingPaymentsAgentA .Values.bankingPaymentsAgentB }}
+---
apiVersion: apps/v1
kind: Deployment
metadata:
- name: {{ .Values.bankingPaymentsAgent.serviceName }}
- namespace: {{ .Release.Namespace }}
+ name: {{ .serviceName }}
+ namespace: {{ $.Release.Namespace }}
labels:
- app: {{ .Values.bankingPaymentsAgent.serviceName }}
+ app: {{ .serviceName }}
version: v1
spec:
- replicas: {{ .Values.bankingPaymentsAgent.replicas }}
+ replicas: {{ .replicas }}
selector:
matchLabels:
- app: {{ .Values.bankingPaymentsAgent.serviceName }}
+ app: {{ .serviceName }}
template:
metadata:
labels:
- app: {{ .Values.bankingPaymentsAgent.serviceName }}
+ app: {{ .serviceName }}
version: v1
spec:
- {{- if .Values.bankingPaymentsAgent.docker.registry }}
+ {{- if .docker.registry }}
imagePullSecrets:
- name: regcred
{{- end }}
containers:
- - name: {{ .Values.bankingPaymentsAgent.serviceName }}
- image: {{ if .Values.bankingPaymentsAgent.docker.registry }}{{ .Values.bankingPaymentsAgent.docker.registry }}/{{ end }}{{ .Values.bankingPaymentsAgent.docker.image }}:{{ .Values.bankingPaymentsAgent.tagversion }}
- imagePullPolicy: {{ if .Values.bankingPaymentsAgent.docker.registry }}Always{{ else }}Never{{ end }}
+ - name: {{ .serviceName }}
+ image: {{ if .docker.registry }}{{ .docker.registry }}/{{ end }}{{ .docker.image }}:{{ .tagversion }}
+ imagePullPolicy: {{ if .docker.registry }}Always{{ else }}Never{{ end }}
securityContext:
runAsNonRoot: true
runAsUser: 999
allowPrivilegeEscalation: false
ports:
- - containerPort: {{ .Values.bankingPaymentsAgent.servicePort }}
+ - containerPort: {{ .servicePort }}
name: http
env:
- name: MCP_SERVER_URL
- value: {{ .Values.bankingPaymentsAgent.mcpServerUrl | quote }}
+ value: {{ .mcpServerUrl | quote }}
- name: OPENAI_API_BASE
valueFrom:
secretKeyRef:
@@ -66,3 +68,4 @@ spec:
limits:
memory: "512Mi"
cpu: "500m"
+{{- end }}
diff --git a/demo_new/helm/templates/payments-agent/service.yaml b/demo_new/helm/templates/payments-agent/service.yaml
index d97dd6fa..29a330b7 100644
--- a/demo_new/helm/templates/payments-agent/service.yaml
+++ b/demo_new/helm/templates/payments-agent/service.yaml
@@ -12,17 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+{{- range list .Values.bankingPaymentsAgentA .Values.bankingPaymentsAgentB }}
+---
apiVersion: v1
kind: Service
metadata:
- name: {{ .Values.bankingPaymentsAgent.serviceName }}
- namespace: {{ .Release.Namespace }}
+ name: {{ .serviceName }}
+ namespace: {{ $.Release.Namespace }}
labels:
- app: {{ .Values.bankingPaymentsAgent.serviceName }}
+ app: {{ .serviceName }}
spec:
selector:
- app: {{ .Values.bankingPaymentsAgent.serviceName }}
+ app: {{ .serviceName }}
ports:
- name: http
- port: {{ .Values.bankingPaymentsAgent.servicePort }}
+ port: {{ .servicePort }}
targetPort: http
+{{- end }}
diff --git a/demo_new/helm/values.yaml b/demo_new/helm/values.yaml
index 1bbd4ba8..faed7a85 100644
--- a/demo_new/helm/values.yaml
+++ b/demo_new/helm/values.yaml
@@ -15,131 +15,172 @@
namespace: casa-dev
masSafe:
- name: "CASA Banking Demo Safe"
- enabledToolChecks:
- - DETERMINISTIC_TOOL_SELECTED
- - AI_POWERED_TOOL_MATCH
- llm_host: ""
+ name: "CASA Banking Demo Safe"
+ enabledToolChecks:
+ - DETERMINISTIC_TOOL_SELECTED
+ - AI_POWERED_TOOL_MATCH
+ llm_host: ""
masCompromised:
- name: "CASA Banking Demo Compromised"
- enabledToolChecks:
- - DETERMINISTIC_TOOL_SELECTED
- - AI_POWERED_TOOL_MATCH
- llm_host: ""
+ name: "CASA Banking Demo Compromised"
+ enabledToolChecks:
+ - DETERMINISTIC_TOOL_SELECTED
+ - AI_POWERED_TOOL_MATCH
+ llm_host: ""
bankingAssistantSafe:
- replicas: 1
- serviceName: banking-assistant-safe
- servicePort: 8082
- compromisedMode: "false"
- bankingDataAgentUrl: "http://banking-data-agent:8083"
- paymentsAgentUrl: "http://banking-payments-agent:8084"
- beneficiaryAgentUrl: "http://banking-beneficiary-agent:8085"
- docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo-new/banking-assistant
- suffix: ''
- tagversion: latest
+ replicas: 1
+ serviceName: banking-assistant-safe
+ appName: banking-assistant
+ servicePort: 8082
+ compromisedMode: "false"
+ bankingDataAgentUrl: "http://banking-data-agent-a:8083"
+ paymentsAgentUrl: "http://banking-payments-agent-a:8084"
+ beneficiaryAgentUrl: "http://banking-beneficiary-agent-safe:8085"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/banking-assistant
+ suffix: ""
+ tagversion: latest
bankingAssistantCompromised:
- replicas: 1
- serviceName: banking-assistant-compromised
- servicePort: 8082
- compromisedMode: "true"
- bankingDataAgentUrl: "http://banking-data-agent:8083"
- paymentsAgentUrl: "http://banking-payments-agent:8084"
- beneficiaryAgentUrl: "http://banking-beneficiary-agent-compromised:8085"
- docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo-new/banking-assistant
- suffix: ''
- tagversion: latest
+ replicas: 1
+ serviceName: banking-assistant-compromised
+ servicePort: 8082
+ compromisedMode: "true"
+ bankingDataAgentUrl: "http://banking-data-agent-b:8083"
+ paymentsAgentUrl: "http://banking-payments-agent-b:8084"
+ beneficiaryAgentUrl: "http://banking-beneficiary-agent-compromised:8085"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/banking-assistant
+ suffix: ""
+ tagversion: latest
+
+bankingDataAgentA:
+ replicas: 1
+ serviceName: banking-data-agent-a
+ appName: data-agent
+ servicePort: 8083
+ mcpServerUrl: "http://banking-mcp-a:3000/mcp"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/banking-data-agent
+ suffix: ""
+ tagversion: latest
-bankingDataAgent:
- replicas: 1
- serviceName: banking-data-agent
- servicePort: 8083
- mcpServerUrl: "http://banking-mcp:3000/mcp"
- docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo-new/banking-data-agent
- suffix: ''
- tagversion: latest
+bankingDataAgentB:
+ replicas: 1
+ serviceName: banking-data-agent-b
+ appName: data-agent
+ servicePort: 8083
+ mcpServerUrl: "http://banking-mcp-b:3000/mcp"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/banking-data-agent
+ suffix: ""
+ tagversion: latest
-bankingPaymentsAgent:
- replicas: 1
- serviceName: banking-payments-agent
- servicePort: 8084
- mcpServerUrl: "http://banking-mcp:3000/mcp"
- docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo-new/payments-agent
- suffix: ''
- tagversion: latest
+bankingPaymentsAgentA:
+ replicas: 1
+ serviceName: banking-payments-agent-a
+ appName: payments-agent
+ servicePort: 8084
+ mcpServerUrl: "http://banking-mcp-a:3000/mcp"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/payments-agent
+ suffix: ""
+ tagversion: latest
+
+bankingPaymentsAgentB:
+ replicas: 1
+ serviceName: banking-payments-agent-b
+ appName: payments-agent
+ servicePort: 8084
+ mcpServerUrl: "http://banking-mcp-b:3000/mcp"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/payments-agent
+ suffix: ""
+ tagversion: latest
bankingBeneficiaryAgent:
- replicas: 1
- serviceName: banking-beneficiary-agent
- servicePort: 8085
- compromisedMode: "false"
- mcpServerUrl: "http://banking-mcp:3000/mcp"
- docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo-new/banking-beneficiary-agent
- suffix: ''
- tagversion: latest
+ replicas: 1
+ serviceName: banking-beneficiary-agent-safe
+ appName: beneficiary-agent
+ servicePort: 8085
+ compromisedMode: "false"
+ mcpServerUrl: "http://banking-mcp-a:3000/mcp"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/banking-beneficiary-agent
+ suffix: ""
+ tagversion: latest
bankingBeneficiaryAgentCompromised:
- replicas: 1
- serviceName: banking-beneficiary-agent-compromised
- servicePort: 8085
- compromisedMode: "true"
- mcpServerUrl: "http://banking-mcp:3000/mcp"
- docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo-new/banking-beneficiary-agent
- suffix: ''
- tagversion: latest
-
-mcp:
- replicas: 1
- serviceName: banking-mcp
- servicePort: 3000
- docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo/mcp
- suffix: ''
- tagversion: latest
+ replicas: 1
+ serviceName: banking-beneficiary-agent-compromised
+ appName: beneficiary-agent-compromised
+ servicePort: 8085
+ compromisedMode: "true"
+ mcpServerUrl: "http://banking-mcp-b:3000/mcp"
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo-new/banking-beneficiary-agent
+ suffix: ""
+ tagversion: latest
-chatUis:
- - name: safe
+mcpA:
+ replicas: 1
+ serviceName: banking-mcp-a
+ appName: banking-mcp
+ servicePort: 3000
docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo/chat-ui
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo/mcp
+ suffix: ""
tagversion: latest
- agentUrl: /safe-agent
- ingress:
- enabled: false
- className: "nginx"
- apiDomainName: ""
- domainPrefix: "chat-safe"
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt
- - name: compromised
+
+mcpB:
+ replicas: 1
+ serviceName: banking-mcp-b
+ appName: banking-mcp
+ servicePort: 3000
docker:
- registry: ghcr.io/outshift-open
- image: outshift-casa/demo/chat-ui
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo/mcp
+ suffix: ""
tagversion: latest
- agentUrl: /compromised-agent
- ingress:
- enabled: false
- className: "nginx"
- apiDomainName: ""
- domainPrefix: "chat-compromised"
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt
+
+chatUis:
+ - name: safe
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo/chat-ui
+ tagversion: latest
+ agentUrl: /safe-agent
+ ingress:
+ enabled: false
+ className: "nginx"
+ apiDomainName: ""
+ domainPrefix: "chat-safe"
+ annotations:
+ cert-manager.io/cluster-issuer: letsencrypt
+ - name: compromised
+ docker:
+ registry: ghcr.io/outshift-open
+ image: outshift-casa/demo/chat-ui
+ tagversion: latest
+ agentUrl: /compromised-agent
+ ingress:
+ enabled: false
+ className: "nginx"
+ apiDomainName: ""
+ domainPrefix: "chat-compromised"
+ annotations:
+ cert-manager.io/cluster-issuer: letsencrypt
llmCredentials:
- apiBaseUrl: ""
- apiKey: ""
+ apiBaseUrl: ""
+ apiKey: ""
diff --git a/deployments/helm/casa-runtime/templates/ui-explorer/configmap.yaml b/deployments/helm/casa-runtime/templates/ui-explorer/configmap.yaml
index 8bd5b9b0..9a054dfc 100644
--- a/deployments/helm/casa-runtime/templates/ui-explorer/configmap.yaml
+++ b/deployments/helm/casa-runtime/templates/ui-explorer/configmap.yaml
@@ -51,6 +51,7 @@ data:
location /api/ {
proxy_pass {{ include "casa-runtime.authServiceUrl" . }}/;
proxy_http_version 1.1;
+ proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/deployments/helm/casa-runtime/values.yaml b/deployments/helm/casa-runtime/values.yaml
index 2ef0b66f..07aa29fa 100644
--- a/deployments/helm/casa-runtime/values.yaml
+++ b/deployments/helm/casa-runtime/values.yaml
@@ -103,7 +103,8 @@ uiExplorer:
nginx:
# When true, nginx proxies /api/ → auth-service (port 8000).
# Build the UI image with VITE_API_BASE_URL=/api when this is enabled.
- # When false, the UI image must be built with the full auth-service URL.
+ # When false (minikube), build with VITE_API_BASE_URL=http://api. and
+ # enable authService.ingress so the browser calls the auth-service directly.
apiProxyEnabled: true
# ─── PostgreSQL (auth-service backend) ───────────────────────────────────────
diff --git a/scripts/dev/local-setup-standalone.sh b/scripts/dev/local-setup-standalone.sh
index 17621e5d..004ca335 100755
--- a/scripts/dev/local-setup-standalone.sh
+++ b/scripts/dev/local-setup-standalone.sh
@@ -111,7 +111,9 @@ log "Step 4 — Build control plane images"
docker build -f deployments/docker/Dockerfile -t casa-auth-server:local .
docker build -f deployments/docker/Dockerfile.keycloak -t casa-auth-server-keycloak:local deployments/docker
docker build -f deployments/docker/Dockerfile.operator -t casa-operator:local .
-docker build --no-cache -f deployments/docker/Dockerfile.ui -t casa-auth-server-ui:local .
+docker build --no-cache -f deployments/docker/Dockerfile.ui \
+ --build-arg VITE_API_BASE_URL=http://api.casa.outshift.ai \
+ -t casa-auth-server-ui:local .
docker build -f deployments/docker/Dockerfile.extauth -t ext-auth-service:local .
log "Step 4 — Build demo images"
@@ -164,7 +166,11 @@ helm upgrade --install casa-dev \
--set uiExplorer.ingress.className=nginx \
--set uiExplorer.ingress.apiDomainName=casa.outshift.ai \
--set uiExplorer.ingress.domainPrefix=explorer \
- --set-string 'uiExplorer.ingress.annotations.nginx\.ingress\.kubernetes\.io/ssl-redirect=false' \
+ --set uiExplorer.nginx.apiProxyEnabled=false \
+ --set authService.ingress.enabled=true \
+ --set authService.ingress.className=nginx \
+ --set authService.ingress.apiDomainName=casa.outshift.ai \
+ --set authService.ingress.domainPrefix=api \
--set keycloak.image.repository=casa-auth-server-keycloak \
--set keycloak.image.tag=local \
--set keycloak.image.pullPolicy=Never \
@@ -233,7 +239,6 @@ helm upgrade --install casa-demo \
--set 'chatUis[0].ingress.className=nginx' \
--set 'chatUis[0].ingress.apiDomainName=casa.outshift.ai' \
--set 'chatUis[0].ingress.domainPrefix=chat-safe' \
- --set-string 'chatUis[0].ingress.annotations.nginx\.ingress\.kubernetes\.io/ssl-redirect=false' \
--set 'chatUis[1].name=compromised' \
--set 'chatUis[1].docker.registry=' \
--set 'chatUis[1].docker.image=demo-chat-ui' \
@@ -243,7 +248,6 @@ helm upgrade --install casa-demo \
--set 'chatUis[1].ingress.className=nginx' \
--set 'chatUis[1].ingress.apiDomainName=casa.outshift.ai' \
--set 'chatUis[1].ingress.domainPrefix=chat-compromised' \
- --set-string 'chatUis[1].ingress.annotations.nginx\.ingress\.kubernetes\.io/ssl-redirect=false' \
--set "llmCredentials.apiBaseUrl=${CASA_LLM_API_BASE_URL}" \
--set "llmCredentials.apiKey=${CASA_LLM_API_KEY}" \
--set 'masSafe.name=CASA Demo Safe' \
@@ -257,6 +261,39 @@ helm upgrade --install casa-demo \
--set 'masCompromised.enabledToolChecks[2]=AI_POWERED_TOOL_MATCH' \
--set "masCompromised.llm_host=${CASA_LLM_HOST}"
+log "Step 6b. — Patching ingress annotations (minikube-specific)"
+kubectl annotate ingress \
+ casa-dev-ui-explorer \
+ -n "$NAMESPACE" \
+ "nginx.ingress.kubernetes.io/ssl-redirect=false" \
+ "nginx.ingress.kubernetes.io/proxy-buffer-size=256k" \
+ "nginx.ingress.kubernetes.io/proxy-buffers-number=8" \
+ "nginx.ingress.kubernetes.io/proxy-body-size=20m" \
+ "nginx.ingress.kubernetes.io/proxy-read-timeout=300" \
+ "nginx.ingress.kubernetes.io/proxy-send-timeout=300" \
+ --overwrite
+
+kubectl annotate ingress \
+ casa-dev-auth-service \
+ -n "$NAMESPACE" \
+ "nginx.ingress.kubernetes.io/ssl-redirect=false" \
+ "nginx.ingress.kubernetes.io/proxy-buffering=off" \
+ "nginx.ingress.kubernetes.io/proxy-buffer-size=128k" \
+ "nginx.ingress.kubernetes.io/proxy-read-timeout=300" \
+ "nginx.ingress.kubernetes.io/proxy-send-timeout=300" \
+ --overwrite
+
+kubectl annotate ingress \
+ casa-demo-chat-ui-safe \
+ casa-demo-chat-ui-compromised \
+ -n "$NAMESPACE" \
+ "nginx.ingress.kubernetes.io/ssl-redirect=false" \
+ "nginx.ingress.kubernetes.io/proxy-buffering=off" \
+ "nginx.ingress.kubernetes.io/proxy-buffer-size=128k" \
+ "nginx.ingress.kubernetes.io/proxy-read-timeout=300" \
+ "nginx.ingress.kubernetes.io/proxy-send-timeout=300" \
+ --overwrite
+
# ---------------------------------------------------------------------------
# 7. Verify
# ---------------------------------------------------------------------------
@@ -293,11 +330,11 @@ kubectl port-forward -n "$NAMESPACE" svc/casa-dev-postgres-auth 5432:5432 &
kubectl port-forward -n "$NAMESPACE" svc/casa-dev-keycloak 8080:8080 &
kubectl port-forward -n "$NAMESPACE" svc/jaeger 16686:16686 &
-# nginx ingress controller — exposes all 3 UIs on port 80 (requires sudo on macOS)
+# nginx ingress controller — exposes all UIs on port 80 (requires sudo on macOS)
sudo -n kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 80:80 &
log "Step 8 — Updating /etc/hosts for ingress hostnames"
-HOSTS_LINE="127.0.0.1 explorer.casa.outshift.ai chat-safe.casa.outshift.ai chat-compromised.casa.outshift.ai"
+HOSTS_LINE="127.0.0.1 explorer.casa.outshift.ai api.casa.outshift.ai chat-safe.casa.outshift.ai chat-compromised.casa.outshift.ai"
if grep -q "casa.outshift.ai" /etc/hosts; then
sudo sed -i '' '/casa\.outshift\.ai/d' /etc/hosts
fi
@@ -311,7 +348,8 @@ echo " Keycloak → http://localhost:8080"
echo " Jaeger traces → http://localhost:16686"
echo ""
echo "UIs available via nginx ingress:"
-echo " Explorer UI → http://explorer.casa.outshift.ai"
+echo " Explorer UI → http://explorer.casa.outshift.ai"
+echo " Auth API → http://api.casa.outshift.ai"
echo " Safe chat UI → http://chat-safe.casa.outshift.ai"
echo " Compromised UI → http://chat-compromised.casa.outshift.ai"
echo ""
diff --git a/scripts/local-setup-standalone-new.sh b/scripts/local-setup-standalone-new.sh
new file mode 100755
index 00000000..db028e51
--- /dev/null
+++ b/scripts/local-setup-standalone-new.sh
@@ -0,0 +1,423 @@
+#!/usr/bin/env bash
+# local-setup-standalone-new.sh — Self-contained minikube bootstrap for CASA banking demo
+# No external files required — all helm values are inlined.
+# Uses nginx ingress controller directly (no Python proxy or pfctl needed).
+#
+# Usage:
+# bash scripts/local-setup-standalone-new.sh # banking demo (Istio + sidecars)
+# bash scripts/local-setup-standalone-new.sh --no-istio # no-Istio mode (no enforcement)
+# bash scripts/local-setup-standalone-new.sh reset # wipe data and reinstall
+# bash scripts/local-setup-standalone-new.sh --no-istio reset # wipe + reinstall without Istio
+#
+# Prerequisites: minikube, istioctl, helm, kubectl, docker, crane
+# brew install minikube istioctl helm kubectl crane
+# Prerequisites (--no-istio only): minikube, helm, kubectl, docker
+# brew install minikube helm kubectl
+set -euo pipefail
+
+# ---------------------------------------------------------------------------
+# Required env vars (export before running):
+# CASA_LLM_HOST OpenAI-compatible API hostname (e.g. litellm.prod.outshift.ai)
+# CASA_LLM_API_KEY API key for the LLM service
+#
+# Optional env vars:
+# CASA_LLM_MODEL_ID LLM model for auth checks (default: bedrock/global.anthropic.claude-sonnet-4-6)
+# CASA_LLM_PIPELINE_MODEL_ID Pipeline model (default: azure/gpt-4o)
+# CASA_LLM_JWT_TOKEN JWT token for LLM service (defaults to CASA_LLM_API_KEY)
+# ---------------------------------------------------------------------------
+: "${CASA_LLM_HOST:?CASA_LLM_HOST must be set (e.g. litellm.prod.outshift.ai)}"
+: "${CASA_LLM_API_KEY:?CASA_LLM_API_KEY must be set}"
+
+CASA_LLM_MODEL_ID="${CASA_LLM_MODEL_ID:-bedrock/global.anthropic.claude-sonnet-4-6}"
+CASA_LLM_PIPELINE_MODEL_ID="${CASA_LLM_PIPELINE_MODEL_ID:-azure/gpt-4o}"
+CASA_LLM_JWT_TOKEN="${CASA_LLM_JWT_TOKEN:-$CASA_LLM_API_KEY}"
+CASA_LLM_API_BASE_URL="http://${CASA_LLM_HOST}"
+
+NAMESPACE="casa-dev"
+
+# ---------------------------------------------------------------------------
+# Parse flags
+# ---------------------------------------------------------------------------
+WITH_ISTIO=true
+DESIRED_MEMORY=7168
+POSITIONAL_ARGS=()
+for arg in "$@"; do
+ case "$arg" in
+ --no-istio) WITH_ISTIO=false ;;
+ *) POSITIONAL_ARGS+=("$arg") ;;
+ esac
+done
+set -- "${POSITIONAL_ARGS[@]+"${POSITIONAL_ARGS[@]}"}"
+
+log() { echo ""; echo "==> $*"; }
+
+# ---------------------------------------------------------------------------
+# Reset mode: wipe helm releases + PVCs then continue with fresh install
+# ---------------------------------------------------------------------------
+if [[ "${1:-}" == "reset" ]]; then
+ log "Reset — stripping MAS finalizers to unblock deletion"
+ kubectl get multiagentsystems.casa.io -n "$NAMESPACE" -o name 2>/dev/null | \
+ xargs -I{} kubectl patch {} -n "$NAMESPACE" --type=json \
+ -p='[{"op":"remove","path":"/metadata/finalizers"}]' 2>/dev/null || true
+
+ log "Reset — stripping CASAPolicy finalizers to unblock deletion"
+ kubectl get casapolicies.casa.io -n "$NAMESPACE" -o name 2>/dev/null | \
+ xargs -I{} kubectl patch {} -n "$NAMESPACE" --type=json \
+ -p='[{"op":"remove","path":"/metadata/finalizers"}]' 2>/dev/null || true
+
+ log "Reset — uninstalling helm releases"
+ helm uninstall casa-banking -n "$NAMESPACE" 2>/dev/null || true
+ helm uninstall casa-dev -n "$NAMESPACE" 2>/dev/null || true
+
+ log "Reset — deleting PVCs"
+ kubectl delete pvc --all -n "$NAMESPACE" 2>/dev/null || true
+
+ log "Reset — done, continuing with fresh install…"
+fi
+
+# ---------------------------------------------------------------------------
+# 1. Start Minikube
+# ---------------------------------------------------------------------------
+log "Step 1 — Start Minikube"
+CURRENT_MEMORY=$(python3 -c "import json; d=json.load(open('$HOME/.minikube/machines/minikube/config.json')); print(d.get('MemSize',0))" 2>/dev/null || echo "0")
+if [ "$CURRENT_MEMORY" != "0" ] && [ "$CURRENT_MEMORY" != "$DESIRED_MEMORY" ]; then
+ echo " memory mismatch (current: ${CURRENT_MEMORY}MB, desired: ${DESIRED_MEMORY}MB) — deleting cluster"
+ minikube delete 2>/dev/null || true
+fi
+
+if minikube status --format='{{.Host}}' 2>/dev/null | grep -q "Running"; then
+ echo " minikube already running with ${DESIRED_MEMORY}MB, skipping start"
+else
+ if $WITH_ISTIO; then
+ minikube start \
+ --driver=docker \
+ --memory=$DESIRED_MEMORY \
+ --cpus=6 \
+ --addons=registry,ingress
+ else
+ minikube start \
+ --driver=docker \
+ --memory=$DESIRED_MEMORY \
+ --cpus=6 \
+ --addons=ingress
+ fi
+fi
+
+minikube addons enable ingress 2>/dev/null || true
+
+if $WITH_ISTIO; then
+ minikube addons enable registry 2>/dev/null || true
+ log "Step 1 — Port-forward in-cluster registry (localhost:5000)"
+ pkill -f "kubectl port-forward.*kube-system.*svc/registry" 2>/dev/null || true
+ kubectl port-forward -n kube-system svc/registry 5000:80 &
+ sleep 2
+fi
+
+# ---------------------------------------------------------------------------
+# 2. Install Istio (--with-istio only)
+# ---------------------------------------------------------------------------
+if $WITH_ISTIO; then
+ log "Step 2 — Install Istio (minimal profile)"
+ istioctl install --set profile=minimal -y
+else
+ log "Step 2 — Skipping Istio install (--no-istio mode)"
+fi
+
+# ---------------------------------------------------------------------------
+# 3. Create namespace
+# ---------------------------------------------------------------------------
+log "Step 3 — Create namespace $NAMESPACE"
+kubectl create namespace "$NAMESPACE" 2>/dev/null || echo " namespace already exists"
+if $WITH_ISTIO; then
+ kubectl label namespace "$NAMESPACE" istio-injection=enabled --overwrite
+fi
+
+# ---------------------------------------------------------------------------
+# 4. Build Docker images
+# ---------------------------------------------------------------------------
+log "Step 4 — Point shell at minikube Docker daemon"
+eval "$(minikube docker-env)"
+
+log "Step 4 — Build control plane images"
+docker build -f deployments/docker/Dockerfile -t casa-auth-server:local .
+docker build -f deployments/docker/Dockerfile.keycloak -t casa-auth-server-keycloak:local deployments/docker
+docker build -f deployments/docker/Dockerfile.operator -t casa-operator:local .
+docker build --no-cache -f deployments/docker/Dockerfile.ui \
+ --build-arg VITE_API_BASE_URL=http://api.casa.outshift.ai \
+ -t casa-auth-server-ui:local .
+docker build -f deployments/docker/Dockerfile.extauth -t ext-auth-service:local .
+
+log "Step 4 — Build shared demo images (mcp + chat-ui, used by banking demo)"
+docker build -f demo/src/mcp/Dockerfile -t demo-mcp:local demo/src/mcp
+docker build -f demo/src/chat-ui/Dockerfile -t demo-chat-ui:local demo/src/chat-ui
+
+log "Step 4 — Build banking demo images"
+docker build -f demo_new/src/banking-assistant/Dockerfile -t demo-new-banking-assistant:local demo_new/src/banking-assistant
+docker build -f demo_new/src/banking-data-agent/Dockerfile -t demo-new-banking-data-agent:local demo_new/src/banking-data-agent
+docker build -f demo_new/src/payments-agent/Dockerfile -t demo-new-banking-payments-agent:local demo_new/src/payments-agent
+docker build -f demo_new/src/banking-beneficiary-agent/Dockerfile -t demo-new-banking-beneficiary-agent:local demo_new/src/banking-beneficiary-agent
+
+if $WITH_ISTIO; then
+ log "Step 4 — Build and push WasmPlugin OCI images"
+ docker build -f sidecar/llm_proxy/Dockerfile -t llm-proxy:local sidecar/llm_proxy
+ docker build -f sidecar/traceparent_injector/Dockerfile -t traceparent-injector:local sidecar/traceparent_injector
+
+ docker save llm-proxy:local -o /tmp/llm-proxy.tar
+ docker save traceparent-injector:local -o /tmp/traceparent-injector.tar
+ crane push --insecure /tmp/llm-proxy.tar localhost:5000/llm-proxy:local
+ crane push --insecure /tmp/traceparent-injector.tar localhost:5000/traceparent-injector:local
+ rm /tmp/llm-proxy.tar /tmp/traceparent-injector.tar
+else
+ log "Step 4 — Skipping WasmPlugin builds (--no-istio mode)"
+fi
+
+# ---------------------------------------------------------------------------
+# 5. Deploy control plane
+# ---------------------------------------------------------------------------
+log "Step 5 — Add helm repositories"
+helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts 2>/dev/null || true
+helm repo add jaegertracing https://jaegertracing.github.io/helm-charts 2>/dev/null || true
+helm repo add bitnami https://charts.bitnami.com/bitnami 2>/dev/null || true
+helm repo update
+
+log "Step 5 — Build helm dependencies"
+if $WITH_ISTIO; then
+ helm dependency build deployments/helm/sidecar
+fi
+helm dependency build deployments/helm/casa-runtime
+
+log "Step 5 — Install/upgrade casa-runtime"
+HELM_SIDECAR_ARGS=()
+if $WITH_ISTIO; then
+ HELM_SIDECAR_ARGS=(
+ --set sidecar.enabled=true
+ --set sidecar.authServerHost=""
+ --set sidecar.image.repository=ext-auth-service
+ --set sidecar.image.tag=local
+ --set sidecar.image.pullPolicy=Never
+ --set sidecar.llm_proxy.image.repository=registry.kube-system.svc.cluster.local/llm-proxy
+ --set sidecar.llm_proxy.image.tag=local
+ --set sidecar.llm_proxy.image.pullPolicy=IfNotPresent
+ --set sidecar.traceparent_injector.image.repository=registry.kube-system.svc.cluster.local/traceparent-injector
+ --set sidecar.traceparent_injector.image.tag=local
+ --set sidecar.traceparent_injector.image.pullPolicy=IfNotPresent
+ )
+else
+ HELM_SIDECAR_ARGS=(--set sidecar.enabled=false)
+fi
+
+helm upgrade --install casa-dev \
+ deployments/helm/casa-runtime \
+ -n "$NAMESPACE" \
+ --create-namespace \
+ --set authService.image.repository=casa-auth-server \
+ --set authService.image.tag=local \
+ --set authService.image.pullPolicy=Never \
+ --set authService.database.password=postgres \
+ --set authService.idp.adminPassword=admin \
+ --set "authService.openai.apiBaseUrl=${CASA_LLM_API_BASE_URL}" \
+ --set "authService.openai.jwtToken=${CASA_LLM_JWT_TOKEN}" \
+ --set "authService.openai.llmApiBaseUrl=${CASA_LLM_API_BASE_URL}" \
+ --set "authService.openai.llmApiKey=${CASA_LLM_API_KEY}" \
+ --set "authService.openai.modelId=${CASA_LLM_MODEL_ID}" \
+ --set "authService.openai.pipelineModelId=${CASA_LLM_PIPELINE_MODEL_ID}" \
+ --set authService.externalSecrets.enabled=false \
+ --set authService.ingress.enabled=true \
+ --set authService.ingress.className=nginx \
+ --set authService.ingress.apiDomainName=casa.outshift.ai \
+ --set authService.ingress.domainPrefix=api \
+ --set uiExplorer.image.repository=casa-auth-server-ui \
+ --set uiExplorer.image.tag=local \
+ --set uiExplorer.image.pullPolicy=Never \
+ --set uiExplorer.ingress.enabled=true \
+ --set uiExplorer.ingress.className=nginx \
+ --set uiExplorer.ingress.apiDomainName=casa.outshift.ai \
+ --set uiExplorer.ingress.domainPrefix=explorer \
+ --set uiExplorer.nginx.apiProxyEnabled=false \
+ --set keycloak.image.repository=casa-auth-server-keycloak \
+ --set keycloak.image.tag=local \
+ --set keycloak.image.pullPolicy=Never \
+ --set keycloak.hostname=localhost \
+ --set keycloak.hostnamePort=8080 \
+ --set operator.image.repository=casa-operator \
+ --set operator.image.tag=local \
+ --set operator.image.pullPolicy=Never \
+ --set postgresAuth.persistence.storageClass="" \
+ --set postgresKeycloak.persistence.storageClass="" \
+ "${HELM_SIDECAR_ARGS[@]}"
+
+# ---------------------------------------------------------------------------
+# Wait for control plane pods to be ready
+# ---------------------------------------------------------------------------
+log "Waiting for control plane pods to be Ready (timeout 10 min)…"
+kubectl wait pod \
+ --for=condition=Ready \
+ --selector='app.kubernetes.io/instance=casa-dev' \
+ -n "$NAMESPACE" \
+ --timeout=600s
+
+# ---------------------------------------------------------------------------
+# 6. Deploy banking demo (demo_new)
+# ---------------------------------------------------------------------------
+log "Step 6 — Install/upgrade banking demo"
+helm upgrade --install casa-banking \
+ demo_new/helm \
+ -n "$NAMESPACE" \
+ --set bankingAssistantSafe.docker.registry="" \
+ --set bankingAssistantSafe.docker.image=demo-new-banking-assistant \
+ --set bankingAssistantSafe.tagversion=local \
+ --set bankingAssistantCompromised.docker.registry="" \
+ --set bankingAssistantCompromised.docker.image=demo-new-banking-assistant \
+ --set bankingAssistantCompromised.tagversion=local \
+ --set bankingDataAgentA.docker.registry="" \
+ --set bankingDataAgentA.docker.image=demo-new-banking-data-agent \
+ --set bankingDataAgentA.tagversion=local \
+ --set bankingDataAgentB.docker.registry="" \
+ --set bankingDataAgentB.docker.image=demo-new-banking-data-agent \
+ --set bankingDataAgentB.tagversion=local \
+ --set bankingPaymentsAgentA.docker.registry="" \
+ --set bankingPaymentsAgentA.docker.image=demo-new-banking-payments-agent \
+ --set bankingPaymentsAgentA.tagversion=local \
+ --set bankingPaymentsAgentB.docker.registry="" \
+ --set bankingPaymentsAgentB.docker.image=demo-new-banking-payments-agent \
+ --set bankingPaymentsAgentB.tagversion=local \
+ --set bankingBeneficiaryAgent.docker.registry="" \
+ --set bankingBeneficiaryAgent.docker.image=demo-new-banking-beneficiary-agent \
+ --set bankingBeneficiaryAgent.tagversion=local \
+ --set bankingBeneficiaryAgentCompromised.docker.registry="" \
+ --set bankingBeneficiaryAgentCompromised.docker.image=demo-new-banking-beneficiary-agent \
+ --set bankingBeneficiaryAgentCompromised.tagversion=local \
+ --set mcpA.docker.registry="" \
+ --set mcpA.docker.image=demo-mcp \
+ --set mcpA.tagversion=local \
+ --set mcpB.docker.registry="" \
+ --set mcpB.docker.image=demo-mcp \
+ --set mcpB.tagversion=local \
+ --set 'chatUis[0].name=safe' \
+ --set 'chatUis[0].docker.registry=' \
+ --set 'chatUis[0].docker.image=demo-chat-ui' \
+ --set 'chatUis[0].tagversion=local' \
+ --set 'chatUis[0].agentUrl=/safe-agent' \
+ --set 'chatUis[0].ingress.enabled=true' \
+ --set 'chatUis[0].ingress.className=nginx' \
+ --set 'chatUis[0].ingress.apiDomainName=casa.outshift.ai' \
+ --set 'chatUis[0].ingress.domainPrefix=banking-safe' \
+ --set 'chatUis[1].name=compromised' \
+ --set 'chatUis[1].docker.registry=' \
+ --set 'chatUis[1].docker.image=demo-chat-ui' \
+ --set 'chatUis[1].tagversion=local' \
+ --set 'chatUis[1].agentUrl=/compromised-agent' \
+ --set 'chatUis[1].ingress.enabled=true' \
+ --set 'chatUis[1].ingress.className=nginx' \
+ --set 'chatUis[1].ingress.apiDomainName=casa.outshift.ai' \
+ --set 'chatUis[1].ingress.domainPrefix=banking-compromised' \
+ --set "llmCredentials.apiBaseUrl=${CASA_LLM_API_BASE_URL}" \
+ --set "llmCredentials.apiKey=${CASA_LLM_API_KEY}" \
+ --set masSafe.name="CASA Banking Demo Safe" \
+ --set 'masSafe.enabledToolChecks[0]=DETERMINISTIC_TOOL_SELECTED' \
+ --set 'masSafe.enabledToolChecks[1]=AI_POWERED_TOOL_MATCH' \
+ --set "masSafe.llm_host=${CASA_LLM_HOST}" \
+ --set masCompromised.name="CASA Banking Demo Compromised" \
+ --set 'masCompromised.enabledToolChecks[0]=DETERMINISTIC_TOOL_SELECTED' \
+ --set 'masCompromised.enabledToolChecks[1]=AI_POWERED_TOOL_MATCH' \
+ --set "masCompromised.llm_host=${CASA_LLM_HOST}"
+
+# ---------------------------------------------------------------------------
+# 6b. Patch ingress annotations (minikube-specific buffer/timeout tuning)
+# ---------------------------------------------------------------------------
+log "Step 6b — Patching ingress annotations (minikube-specific)"
+kubectl annotate ingress \
+ casa-dev-ui-explorer \
+ -n "$NAMESPACE" \
+ "nginx.ingress.kubernetes.io/ssl-redirect=false" \
+ "nginx.ingress.kubernetes.io/proxy-buffer-size=256k" \
+ "nginx.ingress.kubernetes.io/proxy-buffers-number=8" \
+ "nginx.ingress.kubernetes.io/proxy-body-size=20m" \
+ "nginx.ingress.kubernetes.io/proxy-read-timeout=300" \
+ "nginx.ingress.kubernetes.io/proxy-send-timeout=300" \
+ --overwrite
+
+kubectl annotate ingress \
+ casa-dev-auth-service \
+ -n "$NAMESPACE" \
+ "nginx.ingress.kubernetes.io/ssl-redirect=false" \
+ "nginx.ingress.kubernetes.io/proxy-buffering=off" \
+ "nginx.ingress.kubernetes.io/proxy-buffer-size=128k" \
+ "nginx.ingress.kubernetes.io/proxy-read-timeout=300" \
+ "nginx.ingress.kubernetes.io/proxy-send-timeout=300" \
+ --overwrite
+
+kubectl annotate ingress \
+ casa-banking-chat-ui-safe \
+ casa-banking-chat-ui-compromised \
+ -n "$NAMESPACE" \
+ "nginx.ingress.kubernetes.io/ssl-redirect=false" \
+ "nginx.ingress.kubernetes.io/proxy-buffering=off" \
+ "nginx.ingress.kubernetes.io/proxy-buffer-size=128k" \
+ "nginx.ingress.kubernetes.io/proxy-read-timeout=300" \
+ "nginx.ingress.kubernetes.io/proxy-send-timeout=300" \
+ --overwrite
+
+# ---------------------------------------------------------------------------
+# 7. Verify
+# ---------------------------------------------------------------------------
+log "Step 7 — Pod status"
+kubectl get pods -n "$NAMESPACE"
+
+log "Step 7 — MultiAgentSystem status"
+kubectl get multiagentsystems.casa.io -n "$NAMESPACE" 2>/dev/null || echo " (no MAS resources yet)"
+
+log "Step 7 — CASAPolicy status"
+kubectl get casapolicies.casa.io -n "$NAMESPACE" 2>/dev/null || echo " (no CASAPolicy resources yet)"
+
+# ---------------------------------------------------------------------------
+# Wait for all pods to be ready
+# ---------------------------------------------------------------------------
+log "Waiting for all pods to be Ready (timeout 5 min)…"
+kubectl wait pod \
+ --for=condition=Ready \
+ --all \
+ -n "$NAMESPACE" \
+ --timeout=300s
+
+# ---------------------------------------------------------------------------
+# 8. Port-forwards
+# ---------------------------------------------------------------------------
+log "Step 8 — Starting port-forwards"
+
+# Cache sudo credentials upfront (needed for port 80 binding + /etc/hosts)
+sudo -v
+
+pkill -f "kubectl port-forward.*$NAMESPACE" 2>/dev/null || true
+sudo pkill -f "kubectl port-forward.*ingress-nginx" 2>/dev/null || true
+sleep 1
+
+kubectl port-forward -n "$NAMESPACE" svc/casa-dev-auth-service 8000:8000 &
+kubectl port-forward -n "$NAMESPACE" svc/casa-dev-postgres-auth 5432:5432 &
+kubectl port-forward -n "$NAMESPACE" svc/casa-dev-keycloak 8080:8080 &
+kubectl port-forward -n "$NAMESPACE" svc/jaeger 16686:16686 &
+
+# nginx ingress controller — exposes all UIs on port 80 (requires sudo on macOS)
+sudo -n kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 80:80 &
+
+log "Step 8 — Updating /etc/hosts for ingress hostnames"
+HOSTS_LINE="127.0.0.1 explorer.casa.outshift.ai api.casa.outshift.ai banking-safe.casa.outshift.ai banking-compromised.casa.outshift.ai"
+if grep -q "casa.outshift.ai" /etc/hosts; then
+ sudo sed -i '' '/casa\.outshift\.ai/d' /etc/hosts
+fi
+echo "$HOSTS_LINE" | sudo -n tee -a /etc/hosts > /dev/null
+echo " /etc/hosts updated"
+
+echo ""
+echo "Port-forwards started (background jobs):"
+echo " Auth server API → http://localhost:8000"
+echo " Keycloak → http://localhost:8080"
+echo " Jaeger traces → http://localhost:16686"
+echo ""
+echo "UIs available via nginx ingress (port 80):"
+echo " CASA Explorer UI → http://explorer.casa.outshift.ai"
+echo " Auth API → http://api.casa.outshift.ai"
+echo " Banking safe chat UI → http://banking-safe.casa.outshift.ai"
+echo " Banking compromised UI → http://banking-compromised.casa.outshift.ai"
+echo ""
+echo "Done. Run 'jobs' to see background port-forwards."
diff --git a/src/casa_auth_server/telemetry/tracer_repository.py b/src/casa_auth_server/telemetry/tracer_repository.py
index 04616966..f4e014d9 100644
--- a/src/casa_auth_server/telemetry/tracer_repository.py
+++ b/src/casa_auth_server/telemetry/tracer_repository.py
@@ -21,7 +21,7 @@
from uuid import UUID, uuid4
from pydantic import BaseModel, ConfigDict
-from sqlalchemy import DateTime, Integer, String, case, literal, union_all
+from sqlalchemy import DateTime, Index, Integer, String, case, literal, text, union_all
from sqlalchemy import cast as sa_cast
from sqlmodel import JSON, Column, Field, Session, SQLModel, asc, desc, func, select
@@ -49,6 +49,12 @@ class Trace(SQLModel, table=True): # type: ignore[call-arg]
event_type: str
event: dict[str, Any] = Field(sa_column=Column(JSON))
+ __table_args__ = (
+ Index("ix_trace_event_mas_id", text("(event->>'mas_id')")),
+ Index("ix_trace_created_at", "created_at"),
+ Index("ix_trace_event_type", "event_type"),
+ )
+
class TraceList(BaseModel):
"""Paginated list of events."""