diff --git a/cluster/platform/openviking/deployment-maelstrom.yaml b/cluster/platform/openviking/deployment-maelstrom.yaml new file mode 100644 index 0000000..e36d415 --- /dev/null +++ b/cluster/platform/openviking/deployment-maelstrom.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: maelstrom-ui + namespace: openviking +spec: + replicas: 1 + selector: + matchLabels: { app: maelstrom-ui } + template: + metadata: + labels: { app: maelstrom-ui } + spec: + containers: + - name: maelstrom-ui + image: the-seas.local.mk-labs.cloud/library/maelstrom-ui:v0.3.17-1 + ports: [{ containerPort: 80 }] + env: + - name: MAELSTROM_UI_KEY + valueFrom: + secretKeyRef: + name: maelstrom-ui-credentials + key: maelstrom_ui_key + resources: + requests: { cpu: 50m, memory: 64Mi } + limits: { cpu: 200m, memory: 128Mi } +--- +apiVersion: v1 +kind: Service +metadata: + name: maelstrom-ui + namespace: openviking +spec: + selector: { app: maelstrom-ui } + ports: + - port: 80 + targetPort: 80 diff --git a/cluster/platform/openviking/externalsecret-maelstrom.yaml b/cluster/platform/openviking/externalsecret-maelstrom.yaml new file mode 100644 index 0000000..90ae8c2 --- /dev/null +++ b/cluster/platform/openviking/externalsecret-maelstrom.yaml @@ -0,0 +1,30 @@ +# ExternalSecret - maelstrom-ui scoped API key +# Per inbox/ryan/2026-08-14-maelstrom-key-approval.md: Ryan approved pre-seeding +# via ExternalSecret (option b) since Ryan is the sole user of the pilot. +# Scoped key (resources/search/tasks read-only, no bot/admin) minted by Nick Fury, +# see system/inbox/agents/nick-fury/2026-08-14-maelstrom-ui-key-mint-complete.md +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: maelstrom-ui-credentials + namespace: openviking + annotations: + argocd.argoproj.io/sync-wave: "-1" + description: "Scoped read-only API key for maelstrom (Web Studio UI)" +spec: + refreshInterval: "1h" + secretStoreRef: + kind: ClusterSecretStore + name: onepassword-connect + target: + name: maelstrom-ui-credentials + creationPolicy: Owner + template: + engineVersion: v2 + data: + maelstrom_ui_key: "{{ .MAELSTROM_UI_KEY }}" + data: + - secretKey: MAELSTROM_UI_KEY + remoteRef: + key: openviking + property: maelstrom-ui-key diff --git a/cluster/platform/openviking/ingress-maelstrom.yaml b/cluster/platform/openviking/ingress-maelstrom.yaml new file mode 100644 index 0000000..fa1d910 --- /dev/null +++ b/cluster/platform/openviking/ingress-maelstrom.yaml @@ -0,0 +1,25 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: maelstrom-ui + namespace: openviking + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + ingressClassName: nginx + tls: + - hosts: + - maelstrom.local.mk-labs.cloud + secretName: maelstrom-ui-tls + rules: + - host: maelstrom.local.mk-labs.cloud + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: maelstrom-ui + port: + number: 80 diff --git a/cluster/platform/openviking/maelstrom-ui/Dockerfile b/cluster/platform/openviking/maelstrom-ui/Dockerfile new file mode 100644 index 0000000..4087591 --- /dev/null +++ b/cluster/platform/openviking/maelstrom-ui/Dockerfile @@ -0,0 +1,25 @@ +# maelstrom-ui — OpenViking Web Studio static frontend +# Built per finalized plan: inbox/ryan/2026-08-14-maelstrom-ui-deployment-plan.md (Option B) +# +# Source: volcengine/openviking upstream, subdirectory web-studio/, pinned to +# commit 3cd1d4e9acdfcc2567fd78da95339c3b18936c1c (2026-08-14). +# NOT vendored into this repo (GitOps manifests stay app-source-free) — the +# build context is the upstream web-studio/ directory checked out at that +# commit. Image built + pushed manually for this deployment; see +# system/inbox/agents/peter-parker/ for the build log if promoted to CI. +# +# Stage 1: build the SPA +FROM node:22-alpine AS build +WORKDIR /app +COPY web-studio/package.json web-studio/package-lock.json* web-studio/pnpm-lock.yaml* ./ +RUN if [ -f pnpm-lock.yaml ]; then corepack enable && corepack prepare pnpm@latest --activate && pnpm install --no-frozen-lockfile && pnpm approve-builds --all || true; \ + else npm ci; fi +COPY web-studio/ . +RUN if [ -f pnpm-lock.yaml ]; then pnpm run build; else npm run build; fi + +# Stage 2: serve with nginx, proxying /api/, /health, /ready to the openviking Service. +# /bot/ is deliberately NOT proxied (bot.enabled: false, defense-in-depth per plan §2). +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 diff --git a/cluster/platform/openviking/maelstrom-ui/nginx.conf b/cluster/platform/openviking/maelstrom-ui/nginx.conf new file mode 100644 index 0000000..66b815f --- /dev/null +++ b/cluster/platform/openviking/maelstrom-ui/nginx.conf @@ -0,0 +1,34 @@ +# maelstrom-ui nginx config +# Per plan §2: proxy /api/, /health, /ready to the openviking Service. +# /bot/ is deliberately NOT proxied — bot stays disabled (scope decision #3); +# any Web Studio call to /bot/v1/* 404s at this layer instead of reaching a disabled backend. +server { + listen 80; + server_name _; + + location /api/ { + proxy_pass http://openviking.openviking.svc.cluster.local:1933/api/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /health { + proxy_pass http://openviking.openviking.svc.cluster.local:1933/health; + proxy_set_header Host $host; + } + + location /ready { + proxy_pass http://openviking.openviking.svc.cluster.local:1933/ready; + proxy_set_header Host $host; + } + + # bot stays disabled — no proxy for /bot/, static 404 by default nginx behavior. + + location / { + root /usr/share/nginx/html; + index index.html; + try_files $uri $uri/ /index.html; + } +}