apiVersion: v1 kind: Namespace metadata: name: intranet --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: intranet-vector-store namespace: intranet spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 1Gi --- apiVersion: v1 kind: ConfigMap metadata: name: intranet-config namespace: intranet data: KnowledgeApiKey: "" TrustedHeaderSharedSecret: "" --- apiVersion: apps/v1 kind: Deployment metadata: name: intranet-web namespace: intranet labels: app: intranet-web spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: intranet-web template: metadata: labels: app: intranet-web spec: # notes-corpus-clone: shallow-clones the Notes docs corpus into an emptyDir so # the IntranetSearch indexer has /srv/flowercore-notes/docs to index. Uses the # trailing-dot FQDN (gitea-clusterip.gitea.svc.cluster.local.) to bypass the # CoreDNS *.iamworkin.lan template that otherwise resolves the in-cluster service # name to the Traefik VIP for musl / ndots:5 pods (search-domain appending). # Cred: gitea-corpus-cred (in-ns secret with the canonical 1P bluejay read cred; # mirrors the imperative gitea-flowercore-notes argocd repo-cred pattern). initContainers: - name: notes-corpus-clone image: alpine/git:2.45.2 imagePullPolicy: IfNotPresent envFrom: - secretRef: name: gitea-corpus-cred env: - name: GIT_LFS_SKIP_SMUDGE value: "1" command: ["/bin/sh", "-c"] args: - 'git clone --depth 1 http://$username:$password@gitea-clusterip.gitea.svc.cluster.local.:3000/bluejay/FlowerCore.Notes.git /srv/flowercore-notes && echo "notes corpus cloned; docs entries:" && ls /srv/flowercore-notes/docs | wc -l' volumeMounts: - name: notes-corpus mountPath: /srv/flowercore-notes containers: - name: intranet-web image: localhost/fc-intranet-web:v20260614-wave5-knowledgefleet-1458b4d imagePullPolicy: Never ports: - containerPort: 5300 name: http env: - name: ASPNETCORE_ENVIRONMENT value: Production - name: ASPNETCORE_URLS value: "http://+:5300" # Embed backend = edge1 Ollama BY IPv4 (10.0.57.17:11434; has # nomic-embed-text). The hostname edge1.iamworkin.lan is UNUSABLE from # cluster pods: it resolves to an unroutable IPv6 (fdbc:56:*) and the # CoreDNS *.iamworkin.lan template maps the name to the Traefik VIP, so # embeds failed with "No route to host". Use a bare pod-routable IPv4. # Backend is BLUEJAY-AI's GPU node (Ollama / Vulkan Iris Xe, INFRA VLAN # 10.0.56.132) which embeds nomic-embed-text in ~160ms vs the edge1 Pi 5's # ~3.2s for the same ~512-token chunk (~20x faster bulk embed), proven # pod-routable from the intranet namespace 2026-06-13. The prior edge1 Pi 5 # backend (10.0.57.17:11434) remains a working fallback if BLUEJAY-AI is # down. Bulk embed runs in the background; /health does not depend on it. # Memory: feedback_pi5_nomic_embed_slow. - name: IntranetSearch__OllamaBaseUrl value: "http://10.0.56.132:11434" # Notes docs corpus IS now mounted at /srv/flowercore-notes (see the # notes-corpus-clone initContainer + notes-corpus-sync sidecar), so the # IntranetSearch indexer is ENABLED. First-boot bulk embed of the corpus # runs in the background via the edge1 Ollama backend above (~6s/chunk on # the Pi 5); /health readiness does not depend on it, so the pod stays Ready. - name: IntranetSearch__Enabled value: "true" # Page-reading override SQLite persistence on the writable PVC at # /data. This backs pronunciation, notes, corrections, and # page-profile metadata across pod restarts. - name: PageReadingOverrides__DatabasePath value: "/data/page-reading-overrides.db" - name: KnowledgeFleetSearch__BaseUrl value: "https://knowledge.iamworkin.lan" - name: KnowledgeFleetSearch__ApiKey valueFrom: configMapKeyRef: name: intranet-config key: KnowledgeApiKey optional: true - name: TrustedHeaderAuthentication__SharedSecret valueFrom: configMapKeyRef: name: intranet-config key: TrustedHeaderSharedSecret optional: true resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "1Gi" cpu: "1000m" livenessProbe: httpGet: path: /health port: 5300 initialDelaySeconds: 30 periodSeconds: 30 readinessProbe: httpGet: path: /health port: 5300 initialDelaySeconds: 10 periodSeconds: 10 volumeMounts: - name: vector-store mountPath: /data - name: notes-corpus mountPath: /srv/flowercore-notes readOnly: true # notes-corpus-sync: keeps the mounted corpus fresh between pod restarts by # pulling the Notes repo every 30 min (best-effort; the initContainer guarantees # a fresh clone at pod start). Reuses the clone's origin (trailing-dot host + creds). - name: notes-corpus-sync image: alpine/git:2.45.2 imagePullPolicy: IfNotPresent envFrom: - secretRef: name: gitea-corpus-cred env: - name: GIT_LFS_SKIP_SMUDGE value: "1" command: ["/bin/sh", "-c"] args: - 'while true; do sleep 1800; git -C /srv/flowercore-notes pull --depth 1 2>&1 | sed "s/^/[notes-corpus-sync] /" || true; done' resources: requests: memory: "32Mi" cpu: "10m" limits: memory: "128Mi" cpu: "200m" volumeMounts: - name: notes-corpus mountPath: /srv/flowercore-notes volumes: - name: vector-store persistentVolumeClaim: claimName: intranet-vector-store - name: notes-corpus emptyDir: {} --- apiVersion: v1 kind: Service metadata: name: intranet-web namespace: intranet spec: selector: app: intranet-web ports: - port: 5300 targetPort: 5300 name: http --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: intranet-tls namespace: intranet spec: secretName: intranet-tls issuerRef: name: step-ca-acme kind: ClusterIssuer dnsNames: - intranet.iamworkin.lan --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: intranet namespace: intranet spec: entryPoints: - websecure routes: - match: Host(`intranet.iamworkin.lan`) kind: Rule services: - name: intranet-web port: 5300 tls: secretName: intranet-tls