feat: implement 15 production items (SSE, security, observability, features, infra)
Performance: - SSE dashboard streaming endpoint (GET /v1/admin/status/stream) - Web Worker for markdown rendering (offload from main thread) - IndexedDB chat persistence (replace localStorage, 500msg support) Security: - CSRF protection middleware (Origin/Referer validation) - Content Security Policy + security headers middleware - API key rotation endpoint (POST /v1/admin/keys/rotate) Observability: - OpenTelemetry tracing with graceful NoOp fallback - Structured error codes (FAGI-xxxx taxonomy with ErrorResponse schema) - Audit log export (CSV + JSON at /v1/admin/audit/export/*) Features: - Multi-session management hook (parallel conversations) - Conversation export (markdown/JSON/text download + clipboard) - Head customization UI (enable/disable + weight sliders for 12 heads) Infrastructure: - Kubernetes Helm chart (Deployment, Service, HPA, Ingress) - Database migration versioning (generate, verify commands) - Blue-green deployment manifests (color-based traffic switching) Tests: 598 Python + 56 frontend = 654 total, 0 ruff errors Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
This commit is contained in:
13
k8s/Chart.yaml
Normal file
13
k8s/Chart.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: v2
|
||||
name: fusionagi
|
||||
description: FusionAGI Dvadasa 12-headed multi-agent orchestration system
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.1.0"
|
||||
keywords:
|
||||
- ai
|
||||
- multi-agent
|
||||
- orchestration
|
||||
- fusionagi
|
||||
maintainers:
|
||||
- name: FusionAGI Team
|
||||
125
k8s/templates/bluegreen.yaml
Normal file
125
k8s/templates/bluegreen.yaml
Normal file
@@ -0,0 +1,125 @@
|
||||
{{- if .Values.bluegreen.enabled }}
|
||||
# Blue-Green Deployment Strategy
|
||||
#
|
||||
# Two full deployments (blue/green) run simultaneously.
|
||||
# A Service selector switches traffic between them.
|
||||
#
|
||||
# Workflow:
|
||||
# 1. Deploy new version to inactive color (e.g., green)
|
||||
# 2. Run health checks and smoke tests
|
||||
# 3. Switch Service selector to green
|
||||
# 4. Monitor; rollback by switching back to blue
|
||||
#
|
||||
# Usage:
|
||||
# helm upgrade --set bluegreen.active=green fusionagi ./k8s
|
||||
# helm upgrade --set bluegreen.active=blue fusionagi ./k8s # rollback
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api-blue
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
color: blue
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
color: blue
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
color: blue
|
||||
spec:
|
||||
containers:
|
||||
- name: api
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.bluegreen.blueTag | default .Values.image.tag }}"
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
env:
|
||||
- name: DEPLOYMENT_COLOR
|
||||
value: blue
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.healthCheck.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.healthCheck.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources.api | nindent 12 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api-green
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
color: green
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
color: green
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
color: green
|
||||
spec:
|
||||
containers:
|
||||
- name: api
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.bluegreen.greenTag | default .Values.image.tag }}"
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
env:
|
||||
- name: DEPLOYMENT_COLOR
|
||||
value: green
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.healthCheck.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.healthCheck.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources.api | nindent 12 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api-bluegreen
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: 8000
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
color: {{ .Values.bluegreen.active | default "blue" }}
|
||||
{{- end }}
|
||||
91
k8s/templates/deployment.yaml
Normal file
91
k8s/templates/deployment.yaml
Normal file
@@ -0,0 +1,91 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
spec:
|
||||
containers:
|
||||
- name: api
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
protocol: TCP
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
- name: FUSIONAGI_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.secrets.apiKey.existingSecret }}
|
||||
key: {{ .Values.secrets.apiKey.key }}
|
||||
- name: FUSIONAGI_POSTGRES_DSN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.secrets.postgresDsn.existingSecret }}
|
||||
key: {{ .Values.secrets.postgresDsn.key }}
|
||||
- name: FUSIONAGI_REDIS_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.secrets.redisUrl.existingSecret }}
|
||||
key: {{ .Values.secrets.redisUrl.key }}
|
||||
{{- with .Values.healthCheck.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.healthCheck.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources.api | nindent 12 }}
|
||||
---
|
||||
{{- if .Values.frontend.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-frontend
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: frontend
|
||||
spec:
|
||||
replicas: {{ .Values.frontend.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}
|
||||
component: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.resources.frontend | nindent 12 }}
|
||||
{{- end }}
|
||||
29
k8s/templates/hpa.yaml
Normal file
29
k8s/templates/hpa.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ .Release.Name }}-api
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
37
k8s/templates/service.yaml
Normal file
37
k8s/templates/service.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: 8000
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: {{ .Release.Name }}
|
||||
component: api
|
||||
---
|
||||
{{- if .Values.frontend.enabled }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-frontend
|
||||
labels:
|
||||
app: {{ .Release.Name }}
|
||||
component: frontend
|
||||
spec:
|
||||
type: {{ .Values.frontendService.type }}
|
||||
ports:
|
||||
- port: {{ .Values.frontendService.port }}
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: {{ .Release.Name }}
|
||||
component: frontend
|
||||
{{- end }}
|
||||
119
k8s/values.yaml
Normal file
119
k8s/values.yaml
Normal file
@@ -0,0 +1,119 @@
|
||||
# FusionAGI Helm Chart values
|
||||
|
||||
replicaCount: 2
|
||||
|
||||
image:
|
||||
repository: fusionagi/api
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "latest"
|
||||
|
||||
frontend:
|
||||
enabled: true
|
||||
replicaCount: 2
|
||||
image:
|
||||
repository: fusionagi/frontend
|
||||
tag: "latest"
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8000
|
||||
|
||||
frontendService:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "120"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
|
||||
hosts:
|
||||
- host: fusionagi.local
|
||||
paths:
|
||||
- path: /v1
|
||||
pathType: Prefix
|
||||
backend: api
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend: frontend
|
||||
|
||||
resources:
|
||||
api:
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
frontend:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 70
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
postgresql:
|
||||
enabled: true
|
||||
auth:
|
||||
database: fusionagi
|
||||
username: fusionagi
|
||||
existingSecret: fusionagi-db-secret
|
||||
primary:
|
||||
persistence:
|
||||
size: 10Gi
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
architecture: standalone
|
||||
auth:
|
||||
enabled: false
|
||||
master:
|
||||
persistence:
|
||||
size: 2Gi
|
||||
|
||||
env:
|
||||
FUSIONAGI_DB_BACKEND: postgres
|
||||
FUSIONAGI_WORKERS: "4"
|
||||
FUSIONAGI_RATE_LIMIT: "120"
|
||||
FUSIONAGI_LOG_LEVEL: info
|
||||
|
||||
secrets:
|
||||
apiKey:
|
||||
existingSecret: fusionagi-api-secret
|
||||
key: api-key
|
||||
postgresDsn:
|
||||
existingSecret: fusionagi-db-secret
|
||||
key: dsn
|
||||
redisUrl:
|
||||
existingSecret: fusionagi-redis-secret
|
||||
key: url
|
||||
|
||||
bluegreen:
|
||||
enabled: false
|
||||
active: blue
|
||||
blueTag: "latest"
|
||||
greenTag: "latest"
|
||||
|
||||
healthCheck:
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
Reference in New Issue
Block a user