# FlowerCore TTS Reader — Text-to-speech book reader service --- apiVersion: v1 kind: Namespace metadata: name: fc-ttsreader labels: app.kubernetes.io/part-of: flowercore --- # 1Password -> K8s Secret sync for TTS Reader API keys apiVersion: onepassword.com/v1 kind: OnePasswordItem metadata: name: ttsreader-secrets namespace: fc-ttsreader spec: itemPath: "vaults/IAmWorkin/items/FlowerCore TTS Reader" --- apiVersion: apps/v1 kind: Deployment metadata: name: ttsreader-piper namespace: fc-ttsreader labels: app.kubernetes.io/name: ttsreader-piper app.kubernetes.io/part-of: flowercore spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app.kubernetes.io/name: ttsreader-piper template: metadata: labels: app.kubernetes.io/name: ttsreader-piper app.kubernetes.io/part-of: flowercore spec: initContainers: - name: seed-voices image: rhasspy/wyoming-piper:latest command: - python3 - -c args: - | import shutil import ssl from pathlib import Path from urllib.request import urlopen ssl._create_default_https_context = ssl._create_unverified_context files = { "en_US-lessac-high.onnx": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/high/en_US-lessac-high.onnx", "en_US-lessac-high.onnx.json": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/high/en_US-lessac-high.onnx.json", "en_US-lessac-medium.onnx": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx", "en_US-lessac-medium.onnx.json": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx.json", "en_US-amy-medium.onnx": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/amy/medium/en_US-amy-medium.onnx", "en_US-amy-medium.onnx.json": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/amy/medium/en_US-amy-medium.onnx.json", "en_US-john-medium.onnx": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/john/medium/en_US-john-medium.onnx", "en_US-john-medium.onnx.json": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/john/medium/en_US-john-medium.onnx.json", "en_GB-cori-high.onnx": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_GB/cori/high/en_GB-cori-high.onnx", "en_GB-cori-high.onnx.json": "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_GB/cori/high/en_GB-cori-high.onnx.json", } target = Path("/data") target.mkdir(parents=True, exist_ok=True) for name, url in files.items(): path = target / name if path.exists() and path.stat().st_size > 0: print(f"cached {name}", flush=True) continue print(f"downloading {name}", flush=True) with urlopen(url, timeout=180) as response, open(path, "wb") as download_file: shutil.copyfileobj(response, download_file) print(f"ready {name}", flush=True) volumeMounts: - name: data mountPath: /data containers: - name: piper image: rhasspy/wyoming-piper:latest env: - name: PYTHONHTTPSVERIFY value: "0" args: - "--voice" - "en_US-lessac-high" - "--data-dir" - "/data" - "--download-dir" - "/data" ports: - containerPort: 10200 name: wyoming resources: requests: cpu: 250m memory: 256Mi limits: cpu: 1000m memory: 1Gi volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: ttsreader-piper-data --- apiVersion: apps/v1 kind: Deployment metadata: name: ttsreader-web namespace: fc-ttsreader labels: app.kubernetes.io/name: ttsreader-web app.kubernetes.io/part-of: flowercore spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app.kubernetes.io/name: ttsreader-web template: metadata: labels: app.kubernetes.io/name: ttsreader-web app.kubernetes.io/part-of: flowercore annotations: prometheus.io/scrape: "true" prometheus.io/port: "5217" prometheus.io/path: "/metrics" spec: securityContext: fsGroup: 1654 fsGroupChangePolicy: OnRootMismatch containers: - name: web image: localhost/fc-ttsreader-web:v20260423171304 imagePullPolicy: Never ports: - containerPort: 5217 name: http env: - name: ASPNETCORE_URLS value: "http://+:5217" - name: ASPNETCORE_ENVIRONMENT value: "Production" - name: FlowerCore__Database__ConnectionStrings__Sqlite value: "Data Source=/data/ttsreader.db" - name: TtsReader__Audio__OutputRoot value: "/data/audio" - name: TtsReader__Audio__FfmpegPath value: "/usr/bin/ffmpeg" - name: TtsReader__Bible__CorpusRoot value: "/data/corpus-cache/world-english-bible/eng/usx" - name: TtsReader__Jobs__Root value: "/data/jobs" - name: TtsReader__Piper__Host value: "ttsreader-piper.fc-ttsreader.svc.cluster.local" - name: TtsReader__Piper__Port value: "10200" - name: TtsReader__Ollama__BaseUrl value: "http://10.0.57.17:11434" - name: TtsReader__Ollama__DefaultModel value: "gemma3:4b" - name: TtsReader__Ollama__TimeoutSeconds value: "45" - name: TtsReader__Runtime__LogsRoot value: "/data/logs" - name: TtsReader__Runtime__SmokeStatePath value: "/data/ops/smoke-status.json" - name: Auth__ApiKey valueFrom: secretKeyRef: name: ttsreader-secrets key: Auth__ApiKey optional: true - name: Auth__AdminApiKey valueFrom: secretKeyRef: name: ttsreader-secrets key: Auth__AdminApiKey optional: true resources: requests: cpu: 100m memory: 256Mi limits: cpu: 500m memory: 512Mi volumeMounts: - name: data mountPath: /data - name: tmp mountPath: /tmp securityContext: runAsNonRoot: true runAsUser: 1654 runAsGroup: 1654 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL readinessProbe: httpGet: path: /health port: 5217 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /health port: 5217 initialDelaySeconds: 15 periodSeconds: 30 volumes: - name: data persistentVolumeClaim: claimName: ttsreader-data - name: tmp emptyDir: {} --- apiVersion: v1 kind: Service metadata: name: ttsreader-piper namespace: fc-ttsreader spec: selector: app.kubernetes.io/name: ttsreader-piper ports: - port: 10200 targetPort: 10200 name: wyoming --- apiVersion: v1 kind: Service metadata: name: ttsreader-web namespace: fc-ttsreader spec: selector: app.kubernetes.io/name: ttsreader-web ports: - port: 5217 targetPort: 5217 name: http --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ttsreader-piper-data namespace: fc-ttsreader spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 2Gi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ttsreader-data namespace: fc-ttsreader spec: accessModes: - ReadWriteOnce storageClassName: longhorn resources: requests: storage: 5Gi --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: ttsreader-cert namespace: fc-ttsreader spec: secretName: ttsreader-tls issuerRef: name: step-ca-acme kind: ClusterIssuer dnsNames: - ttsreader.iamworkin.lan --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: ttsreader-web namespace: fc-ttsreader spec: entryPoints: - websecure routes: - match: Host(`ttsreader.iamworkin.lan`) kind: Rule services: - name: ttsreader-web port: 5217 tls: secretName: ttsreader-tls