cluster

Infrastructure files for Nordgedanken and Midnightthoughts.
git clone git://archive.git.mtrnord.blog/MTRNord/cluster.git
Log | Files | Refs | README

commit 84bcf8d1679ec9aecd3e014fdb9f45b9e2cc1444
parent 869b7d75ab1cf2750db2f505c706f0d052d2c1b5
Author: MTRNord <MTRNord@users.noreply.github.com>
Date:   Sun,  1 Feb 2026 18:54:35 +0100

Set up peertube (hopefully lol)

Signed-off-by: MTRNord <MTRNord@users.noreply.github.com>

Diffstat:
Mapps/talos_cluster/kustomization.yaml | 1+
Aapps/talos_cluster/peertube/deployment.yaml | 342+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps/talos_cluster/peertube/kustomization.yaml | 15+++++++++++++++
Aapps/talos_cluster/peertube/nginx/peertube.conf | 311+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapps/talos_cluster/peertube/pvc.yaml | 25+++++++++++++++++++++++++
Aapps/talos_cluster/peertube/secret.yaml | 43+++++++++++++++++++++++++++++++++++++++++++
6 files changed, 737 insertions(+), 0 deletions(-)

diff --git a/apps/talos_cluster/kustomization.yaml b/apps/talos_cluster/kustomization.yaml @@ -32,3 +32,4 @@ resources: - ./immich - ./convex - ./clickstack + - ./peertube diff --git a/apps/talos_cluster/peertube/deployment.yaml b/apps/talos_cluster/peertube/deployment.yaml @@ -0,0 +1,342 @@ +# Redis deployment for PeerTube +apiVersion: apps/v1 +kind: Deployment +metadata: + name: peertube-redis + namespace: peertube +spec: + replicas: 1 + selector: + matchLabels: + app: peertube-redis + template: + metadata: + labels: + app: peertube-redis + spec: + containers: + - name: redis + image: redis:8-alpine + ports: + - containerPort: 6379 + name: redis + volumeMounts: + - name: redis-data + mountPath: /data + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + volumes: + - name: redis-data + emptyDir: + sizeLimit: 1Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: peertube-redis + namespace: peertube +spec: + selector: + app: peertube-redis + ports: + - name: redis + port: 6379 + targetPort: 6379 +--- +# PeerTube main deployment with nginx sidecar +apiVersion: apps/v1 +kind: Deployment +metadata: + name: peertube + namespace: peertube +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: peertube + template: + metadata: + labels: + app: peertube + spec: + containers: + # Nginx sidecar for S3 caching and static content + - name: nginx + image: nginx:1.27-alpine + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: S3_ENDPOINT + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_ENDPOINT + - name: S3_HOST + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_HOST + volumeMounts: + - name: nginx-config + mountPath: /etc/nginx/templates/default.conf.template + subPath: peertube.conf + readOnly: true + - name: nginx-cache + mountPath: /var/cache/nginx + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + livenessProbe: + httpGet: + path: /api/v1/ping + port: http + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + readinessProbe: + httpGet: + path: /api/v1/ping + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + + # PeerTube application + - name: peertube + image: chocobozzz/peertube:production-bookworm + ports: + - containerPort: 9000 + name: peertube + protocol: TCP + - containerPort: 1935 + name: rtmp + protocol: TCP + env: + # Database configuration (using CNPG cluster) + - name: PEERTUBE_DB_HOSTNAME + value: "pg-cluster-v2-rw.postgres-cluster.svc.cluster.local" + - name: PEERTUBE_DB_USERNAME + value: "peertube" + - name: PEERTUBE_DB_PASSWORD + valueFrom: + secretKeyRef: + name: peertube-secrets + key: POSTGRES_PASSWORD + - name: PEERTUBE_DB_NAME + value: "peertube" + - name: PEERTUBE_DB_SSL + value: "false" + # Redis configuration + - name: PEERTUBE_REDIS_HOSTNAME + value: "peertube-redis" + - name: PEERTUBE_REDIS_PORT + value: "6379" + # Webserver configuration + - name: PEERTUBE_WEBSERVER_HOSTNAME + value: "peertube.mtrnord.blog" + - name: PEERTUBE_WEBSERVER_PORT + value: "443" + - name: PEERTUBE_WEBSERVER_HTTPS + value: "true" + # Trust proxy (envoy gateway and nginx sidecar) + - name: PEERTUBE_TRUST_PROXY + value: '["127.0.0.1", "loopback", "10.0.0.0/8", "100.64.0.0/10"]' + # Secret + - name: PEERTUBE_SECRET + valueFrom: + secretKeyRef: + name: peertube-secrets + key: PEERTUBE_SECRET + # Email configuration (using existing mailserver) + - name: PEERTUBE_SMTP_HOSTNAME + value: "mail.midnightthoughts.space" + - name: PEERTUBE_SMTP_PORT + value: "465" + - name: PEERTUBE_SMTP_USERNAME + valueFrom: + secretKeyRef: + name: peertube-secrets + key: SMTP_USERNAME + - name: PEERTUBE_SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: peertube-secrets + key: SMTP_PASSWORD + - name: PEERTUBE_SMTP_FROM + value: "noreply@peertube.mtrnord.blog" + - name: PEERTUBE_SMTP_TLS + value: "true" + - name: PEERTUBE_SMTP_DISABLE_STARTTLS + value: "true" + - name: PEERTUBE_ADMIN_EMAIL + value: "support@midnightthoughts.space" + # S3 Object Storage Configuration (Hetzner) + - name: PEERTUBE_OBJECT_STORAGE_ENABLED + value: "true" + - name: PEERTUBE_OBJECT_STORAGE_ENDPOINT + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_ENDPOINT + - name: PEERTUBE_OBJECT_STORAGE_REGION + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_REGION + - name: PEERTUBE_OBJECT_STORAGE_CREDENTIALS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_ACCESS_KEY_ID + - name: PEERTUBE_OBJECT_STORAGE_CREDENTIALS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_SECRET_ACCESS_KEY + # S3 Bucket Configuration - Web Videos + - name: PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BUCKET_NAME + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_BUCKET_NAME + - name: PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_PREFIX + value: "web-videos/" + - name: PEERTUBE_OBJECT_STORAGE_WEB_VIDEOS_BASE_URL + value: "https://peertube.mtrnord.blog/static/web-videos" + # S3 Bucket Configuration - Streaming Playlists (HLS) + - name: PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BUCKET_NAME + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_BUCKET_NAME + - name: PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_PREFIX + value: "streaming-playlists/" + - name: PEERTUBE_OBJECT_STORAGE_STREAMING_PLAYLISTS_BASE_URL + value: "https://peertube.mtrnord.blog/static/streaming-playlists" + # S3 Bucket Configuration - User Exports + - name: PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_BUCKET_NAME + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_BUCKET_NAME + - name: PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_PREFIX + value: "user-exports/" + - name: PEERTUBE_OBJECT_STORAGE_USER_EXPORTS_BASE_URL + value: "https://peertube.mtrnord.blog/static/user-exports" + # S3 Bucket Configuration - Original Video Files + - name: PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_BUCKET_NAME + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_BUCKET_NAME + - name: PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_PREFIX + value: "original-video-files/" + - name: PEERTUBE_OBJECT_STORAGE_ORIGINAL_VIDEO_FILES_BASE_URL + value: "https://peertube.mtrnord.blog/static/original-video-files" + # S3 Bucket Configuration - Captions + - name: PEERTUBE_OBJECT_STORAGE_CAPTIONS_BUCKET_NAME + valueFrom: + secretKeyRef: + name: peertube-secrets + key: S3_BUCKET_NAME + - name: PEERTUBE_OBJECT_STORAGE_CAPTIONS_PREFIX + value: "captions/" + - name: PEERTUBE_OBJECT_STORAGE_CAPTIONS_BASE_URL + value: "https://peertube.mtrnord.blog/static/captions" + # S3 ACL settings + - name: PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PUBLIC + value: "public-read" + - name: PEERTUBE_OBJECT_STORAGE_UPLOAD_ACL_PRIVATE + value: "private" + volumeMounts: + - name: data + mountPath: /data + - name: config + mountPath: /config + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "2Gi" + startupProbe: + httpGet: + path: /api/v1/ping + port: peertube + initialDelaySeconds: 30 + periodSeconds: 10 + failureThreshold: 30 + + volumes: + - name: data + persistentVolumeClaim: + claimName: peertube-data + - name: config + persistentVolumeClaim: + claimName: peertube-config + - name: nginx-config + configMap: + name: peertube-nginx + - name: nginx-cache + emptyDir: + sizeLimit: 15Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: peertube + namespace: peertube +spec: + selector: + app: peertube + ports: + - name: http + port: 8080 + targetPort: 8080 + - name: rtmp + port: 1935 + targetPort: 1935 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: peertube + namespace: peertube +spec: + parentRefs: + - name: envoy-gateway + namespace: envoy-gateway + hostnames: + - peertube.mtrnord.blog + rules: + - backendRefs: + - name: peertube + port: 8080 + timeouts: + request: 600s + backendRequest: 0s +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: peertube-policy + namespace: peertube +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: peertube + compression: + - type: Gzip + - type: Brotli diff --git a/apps/talos_cluster/peertube/kustomization.yaml b/apps/talos_cluster/peertube/kustomization.yaml @@ -0,0 +1,15 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: peertube +resources: + - secret.yaml + - pvc.yaml + - deployment.yaml +configMapGenerator: + - name: peertube-nginx + namespace: peertube + files: + - peertube.conf=nginx/peertube.conf + options: + labels: + app: peertube diff --git a/apps/talos_cluster/peertube/nginx/peertube.conf b/apps/talos_cluster/peertube/nginx/peertube.conf @@ -0,0 +1,311 @@ +# PeerTube nginx configuration with S3 caching for Hetzner Object Storage +# Based on official PeerTube S3 cache configuration + +# Cache paths for S3 content +proxy_cache_path /var/cache/nginx/s3 levels=1:2 keys_zone=CACHE-S3:100m inactive=48h max_size=10G; +proxy_cache_path /var/cache/nginx/s3-ts levels=1:2 keys_zone=CACHE-S3-TS:10m inactive=60s max_size=1G; + +upstream peertube_backend { + server 127.0.0.1:9000; +} + +server { + listen 8080; + server_name _; + + access_log /var/log/nginx/peertube.access.log; + error_log /var/log/nginx/peertube.error.log; + + # Performance optimizations + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 30; + client_body_timeout 30s; + client_header_timeout 10s; + send_timeout 10s; + reset_timedout_connection on; + proxy_ignore_client_abort on; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_types text/css application/javascript font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml application/xml; + gzip_min_length 1000; + gzip_buffers 16 8k; + gzip_comp_level 2; + + ## + # PeerTube API and Application + ## + + location @api { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + + client_max_body_size 100k; + + proxy_connect_timeout 10m; + proxy_send_timeout 10m; + proxy_read_timeout 10m; + send_timeout 10m; + + proxy_pass http://peertube_backend; + } + + location / { + try_files /dev/null @api; + } + + # Resumable uploads + location ~ ^/api/v1/videos/(upload-resumable|([^/]+/source/replace-resumable))$ { + client_max_body_size 0; + proxy_request_buffering off; + try_files /dev/null @api; + } + + location ~ ^/api/v1/users/[^/]+/imports/import-resumable$ { + client_max_body_size 0; + proxy_request_buffering off; + try_files /dev/null @api; + } + + # Video uploads + location ~ ^/api/v1/videos/(upload|([^/]+/studio/edit))$ { + limit_except POST HEAD { deny all; } + client_max_body_size 12G; + add_header X-File-Maximum-Size 8G always; + proxy_request_buffering off; + try_files /dev/null @api; + } + + # Runner job updates + location ~ ^/api/v1/runners/jobs/[^/]+/(update|success)$ { + client_max_body_size 0; + proxy_request_buffering off; + try_files /dev/null @api; + } + + # Other media uploads + location ~ ^/api/v1/(videos|video-playlists|video-channels|users/me) { + client_max_body_size 12M; + add_header X-File-Maximum-Size 8M always; + try_files /dev/null @api; + } + + ## + # WebSocket support + ## + + location @api_websocket { + proxy_http_version 1.1; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_pass http://peertube_backend; + } + + location /socket.io { + try_files /dev/null @api_websocket; + } + + location /tracker/socket { + proxy_read_timeout 15m; + try_files /dev/null @api_websocket; + } + + location ~ ^/plugins/[^/]+(/[^/]+)?/ws/ { + try_files /dev/null @api_websocket; + } + + ## + # S3 Cache Proxy - Main media files + # Configured for Hetzner Object Storage + ## + + set $s3_backend '${S3_ENDPOINT}'; + set $s3_host '${S3_HOST}'; + + # Cache S3 files for a long time (filenames change when content updates) + location @s3 { + limit_except GET OPTIONS { + deny all; + } + + resolver 1.1.1.1 8.8.8.8 valid=300s; + resolver_timeout 10s; + + proxy_set_header Host $s3_host; + proxy_set_header Connection ''; + proxy_set_header Authorization ''; + proxy_set_header Range $slice_range; + proxy_hide_header Set-Cookie; + proxy_hide_header 'Access-Control-Allow-Origin'; + proxy_hide_header 'Access-Control-Allow-Methods'; + proxy_hide_header 'Access-Control-Allow-Headers'; + proxy_hide_header x-amz-id-2; + proxy_hide_header x-amz-request-id; + proxy_hide_header x-amz-meta-server-side-encryption; + proxy_hide_header x-amz-server-side-encryption; + proxy_hide_header x-amz-bucket-region; + proxy_hide_header x-amzn-requestid; + proxy_ignore_headers Set-Cookie; + proxy_pass $s3_backend$uri; + proxy_intercept_errors off; + + proxy_cache CACHE-S3; + proxy_cache_valid 200 206 48h; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + slice 1m; + proxy_cache_key $host$uri$is_args$args$slice_range; + proxy_http_version 1.1; + + expires 1y; + add_header Cache-Control public; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + add_header X-Cache-Status $upstream_cache_status; + add_header X-Content-Type-Options nosniff; + add_header Content-Security-Policy "default-src 'none'; form-action 'none'"; + } + + # .ts files are live fragments - cache briefly + location @s3-ts { + limit_except GET OPTIONS { + deny all; + } + + resolver 1.1.1.1 8.8.8.8 valid=300s; + resolver_timeout 10s; + + proxy_set_header Host $s3_host; + proxy_set_header Connection ''; + proxy_set_header Authorization ''; + proxy_set_header Range $slice_range; + proxy_hide_header Set-Cookie; + proxy_hide_header 'Access-Control-Allow-Origin'; + proxy_hide_header 'Access-Control-Allow-Methods'; + proxy_hide_header 'Access-Control-Allow-Headers'; + proxy_hide_header x-amz-id-2; + proxy_hide_header x-amz-request-id; + proxy_hide_header x-amz-meta-server-side-encryption; + proxy_hide_header x-amz-server-side-encryption; + proxy_hide_header x-amz-bucket-region; + proxy_hide_header x-amzn-requestid; + proxy_ignore_headers Set-Cookie; + proxy_pass $s3_backend$uri; + proxy_intercept_errors off; + + proxy_cache CACHE-S3-TS; + proxy_cache_valid 200 206 2m; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + slice 1m; + proxy_cache_key $host$uri$is_args$args$slice_range; + proxy_http_version 1.1; + + expires 1y; + add_header Cache-Control public; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + add_header X-Cache-Status $upstream_cache_status; + add_header X-Content-Type-Options nosniff; + add_header Content-Security-Policy "default-src 'none'; form-action 'none'"; + } + + # M3U8 and JSON files for live videos - don't cache + location @s3_nocache { + limit_except GET OPTIONS { + deny all; + } + + resolver 1.1.1.1 8.8.8.8 valid=300s; + resolver_timeout 10s; + + proxy_set_header Host $s3_host; + proxy_set_header Connection ''; + proxy_set_header Authorization ''; + proxy_set_header Range $http_range; + proxy_hide_header Set-Cookie; + proxy_hide_header 'Access-Control-Allow-Origin'; + proxy_hide_header 'Access-Control-Allow-Methods'; + proxy_hide_header 'Access-Control-Allow-Headers'; + proxy_hide_header x-amz-id-2; + proxy_hide_header x-amz-request-id; + proxy_hide_header x-amz-meta-server-side-encryption; + proxy_hide_header x-amz-server-side-encryption; + proxy_hide_header x-amz-bucket-region; + proxy_hide_header x-amzn-requestid; + proxy_ignore_headers Set-Cookie; + proxy_pass $s3_backend$uri; + proxy_intercept_errors off; + + expires 0; + proxy_cache off; + + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + add_header X-Cache-Status $upstream_cache_status; + add_header X-Content-Type-Options nosniff; + add_header Content-Security-Policy "default-src 'none'; form-action 'none'"; + } + + ## + # Static media routing to S3 cache + ## + + # Private content goes through PeerTube for auth + location ~ ^(/static/(webseed|web-videos|streaming-playlists/hls)/private/)|^/download { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_limit_rate 5M; + proxy_pass http://peertube_backend; + } + + # Public static content - route to S3 cache + location ~ ^/static/(webseed|web-videos|redundancy|streaming-playlists)/ { + limit_rate_after 5M; + set $peertube_limit_rate 5M; + limit_rate $peertube_limit_rate; + + if ($request_method = 'OPTIONS') { + add_header Access-Control-Allow-Origin '*'; + add_header Access-Control-Allow-Methods 'GET, OPTIONS'; + add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + add_header Access-Control-Max-Age 1728000; + add_header Content-Type 'text/plain charset=UTF-8'; + add_header Content-Length 0; + return 204; + } + + if ($request_method = 'GET') { + add_header Access-Control-Allow-Origin '*'; + add_header Access-Control-Allow-Methods 'GET, OPTIONS'; + add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; + } + + # Rewrite paths for S3 + rewrite ^/static/webseed/(.*)$ /web-videos/$1 break; + rewrite ^/static/(.*)$ /$1 break; + + try_files $uri @s3; + } + + # .ts files for live streaming + location ~ ^/static/.*\.ts$ { + rewrite ^/static/(.*)$ /$1 break; + try_files $uri @s3-ts; + } + + # M3U8 and JSON files - no cache + location ~ ^/static/.*\.(json|m3u8)$ { + rewrite ^/static/(.*)$ /$1 break; + try_files $uri @s3_nocache; + } +} diff --git a/apps/talos_cluster/peertube/pvc.yaml b/apps/talos_cluster/peertube/pvc.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: peertube-data + namespace: peertube +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: longhorn +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: peertube-config + namespace: peertube +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: longhorn diff --git a/apps/talos_cluster/peertube/secret.yaml b/apps/talos_cluster/peertube/secret.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: Secret +metadata: + name: peertube-secrets + namespace: peertube +type: Opaque +stringData: + #ENC[AES256_GCM,data:x5GTGfLM1X2OTh4ROERmyfPo6MfDxPrVIlekqoVxf5RtEvE8brA60uO9PlyhHCerUWFmAFb9,iv:Z1cu5L9Xot0EjDh8Ca2DgncHEGdhNT+9D+VEQH1oEHE=,tag:WO5M2G1ALwXtcIJB3PJkaQ==,type:comment] + PEERTUBE_SECRET: ENC[AES256_GCM,data:2AOadpq3ArxzzMcOFpWJMnCV23tKvIT9S6zbwu4P0ZvXClTLx+fHueN0e1atXW6GEptMDAGOkQjbN8pyFMCt/Q==,iv:GuOVzJWe1koB8SM/2syO5oRNO7arjlxNdhYqwooLNHg=,tag:aigMJZnD0/PhITTmbneBfA==,type:str] + #ENC[AES256_GCM,data:0krcszGeYWC3/0c5KMgfLA6IALJLRBk4T4oa93AzcNW4z9M7kox0h28uWN7SmVt63cb2U5fRvXXmyh8tKVIcISTV9Dqxxo7J19yHdss=,iv:fHQqbO0ZGCVxO/5g9pMBZ31sro5PZ+p1L9NKjDFHME4=,tag:bEdPLPM1L9FPIXk66Yc81w==,type:comment] + POSTGRES_PASSWORD: ENC[AES256_GCM,data:5YeR3Uu+ODd96qnmroN9S+VC2Ie/Kbiq8k5xupXnEzCYxDYvQrg3xKU/xxBk4NzQ6GfpB8cUcNrve7E9Srl9,iv:x7XiiqyopyXHbZT27+YHVd6Q+WmCw7pmhIoWSuweKnU=,tag:aa6AaBfVOJH/6vyWkXy3Tw==,type:str] + #ENC[AES256_GCM,data:SMTQUokPK3kx+ZNkLI8iLK2bBrzE0a+3Co0aKX5afV/fYfcEx6h3JQE3,iv:CL+0HFMJv8RTkwkh7sWf9GeS+q4rhqifTsnRd5zwO3M=,tag:38Y2uvmxavd7hAr/0U+Rxw==,type:comment] + SMTP_USERNAME: ENC[AES256_GCM,data:afePsswXPFK0tsKGkfAaS9lEOvpfryo5/o8SMhI1,iv:xr6g18NWPzVnAbAHkD8bBZh+XueSMwPpIbWT0P4/O1Y=,tag:mdnl02VaUE72RmySfC7lWg==,type:str] + SMTP_PASSWORD: ENC[AES256_GCM,data:U8aWsBZMcD2Ec9n0BIGi42odB9qpj6beL/6Hq5hBL6JxiKDtI1vSlrwdoBtn954gsON+Xu2IaWpKXcVijr7vPA==,iv:QfTzIl3JCrGzNyuJdDGV9BWY1cj1Fp5zltgHXOxy9qU=,tag:4t5XlnK2OSNKY/G88ah9JA==,type:str] + #ENC[AES256_GCM,data:qkQxHNk1f7LxXuNCeweTDrOT5bh0nQ13ufHucKxW9cf2Sa8X/Nal3A==,iv:cg8fXasy261RfLrVvneQ3rVxfh95EBpOh9Naxemp90I=,tag:KvuVIN9je48jsKHt/W21GQ==,type:comment] + #ENC[AES256_GCM,data:t/JqW5WvfXoH+ZjAdvjO4SXTl0ggSFrZfEhrwcI5xdfE6qQrpIIfxVoivN5VDvin02Hy3P9jDy8Gj9Gq6d3ydQ==,iv:zlSOPWVV8nlP+hYENf8VB6iM1S7DXW/k0dDm3iGIcNE=,tag:zOOzKDxTQfkTXp8LKMSwMQ==,type:comment] + S3_ENDPOINT: ENC[AES256_GCM,data:8V7LcQHkRisqLpcrTJecoRKE5aJdaaYMvoA+/qo89zbGTQQ=,iv:IKkVAuAc36aKS2EblED+0uICM589IHIL4+NgEC9Fev4=,tag:6vU6zRxnH6Uz94eEi4gJtA==,type:str] + #ENC[AES256_GCM,data:fLgSFxHMpUxVVAR8BFgS698VTXR+YYDxIpAvEviNOTYZmo2tY27dOrToBHnrgQ4h/pPf0mXw8AUcLyigtLI=,iv:Mzdm1b5OFQIOFk47pdWzu++YY2/F5wjfutUrCVqMHXM=,tag:jfuRPiVp/j+Aj+kFT289Bg==,type:comment] + S3_HOST: ENC[AES256_GCM,data:3hw2lRBbDg+gOrx9i0E29Dms4Ft7DNIs9djvijj1bWqb4WSemhwgks2EvRo=,iv:A8R9yEldwsALAEWwBjTonlzSkKkqwOCPHlJYsKvhqyI=,tag:x3jzKkHonHf5DcKl7UkcSQ==,type:str] + S3_REGION: ENC[AES256_GCM,data:JCcxfA==,iv:611n91/fxaKF+6l/c+sPOF/iQpUYmBob0lT6LzhqPVQ=,tag:iAn0Z5nWWHdc8u9PooCGsA==,type:str] + S3_BUCKET_NAME: ENC[AES256_GCM,data:7EY7t9cPlREmVUHn4u/2Vg==,iv:oYos47/HI6M6umx81LEMYaBk0kV5IqbU5AuuW1/5gBg=,tag:YbQJnUXYssc4p8ex7YRfkA==,type:str] + S3_ACCESS_KEY_ID: ENC[AES256_GCM,data:0wSX9rDvDyAAhVZkkSEHjG7kTds=,iv:NnuuP5uh8Ox2nnbW9utt+k0M4dXqyZIN58jldsNmPds=,tag:/bzMM6y4fWkybjyEQdh5YA==,type:str] + S3_SECRET_ACCESS_KEY: ENC[AES256_GCM,data:aNivAmpg0C/dt+XA8l6rmnPdDOxFuOvWUF1LcUcMepR1OsUnLIqLqQ==,iv:scUeY1wCUazQ6dCY4FPdg48gSsRfI+BpWLzuo8H9M38=,tag:X08Et2ovTw5EkOjO+baZMA==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1esjyg2qfy49awv0ptkzvpk425adczjr38m37w2mmcahzc4p8n54sll2nzh + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBicmJZQWVZRFlZNkQwamlw + TXBDMjdxWjdpOS9YWEU0OFJhR1JUN3VsZG5RCnc0cG13UUt1ZXpJbHdaUDVlNVRN + dEdGOWhpNXlSSGU4QVkvMGhicWMyaHcKLS0tIDh4QSsvM0lqOThGc2l4NmM0ZU9Y + MFlKYnhuYjVMU29ES05rM294cUN5TWcKzjgLbVM6baupM7DwELuofeAlXtltL68A + BCyn+w1vzj3CmhVSEx+u8dshlpF1u8NeZe1m20BEDYChg/eNoGFHWg== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-02-01T17:36:57Z" + mac: ENC[AES256_GCM,data:HGvlYWK9VbvmHh/SfruCX/ZiV8V8QS6e+KzrsAnnkGrEYckPbe/IB7NF3RJmDK/fb/B+Y6y1qdrXMygXax6y3tiUyu42H/97Kj6mjDFzTjO0TWbodK68vmLrLM2775IDdz2WMAV0cq03ddVUPkIHI6roRI0QrA1AZAk4xyw2Feg=,iv:eS5MwiWuyottGkqlJOcZtLeWHjnqwOxUg/M3wG58ZpY=,tag:SwFFGZ2rBnuQIEWot+2JPA==,type:str] + pgp: [] + encrypted_regex: ^(apiKey|appUserPassword|otelUserPassword|harborAdminPassword|totpVaultKey|kimaiAppSecret|kimaiAdminPassword|GITHUB_CLIENT_ID|GITHUB_CLIENT_SECRET|GITHUB_PRIVATE_KEY|woosh|root_password|rspamd_password|pgdb_password|matrix_access_token|pgdb_remote_url|hmac_secret_key|adminPassword|adminEmail|jenkinsAdminEmail|securityRealm|gerrit.config|routing_key|DATABASE_URL|SMTP_PASSWORD|SECRET_KEY_BASE|admin_password|extraCommands|key|clickhouseDatabaseURL|databaseURL|client_id|client_secret|secret_key_base|otp_secret|private_key|public_key|primaryKey|deterministicKey|keyDerivationSalt|token|clientId|secretKey|installationId|installationKey|uriOverride|adminToken.value|password.value|sql_password|erlangCookie|AUTHENTICATION_PASSWORD|ROOM_API_SECRET_KEY|adminPassword|configPassword|adminUser|configUser|MAIL_PASSWORD|APP_KEY|api_key|api_secret|keys|livekit_key|livekit_secret|secret_key|admin_pass|admin_email|mariadbPassword|mariadbRootPassword|privateKey|data|stringData|PASSWD|password|pass|postgresPassword|postgresqlPassword|redminePassword|smtpPassword|registration_shared_secret|shared_secret|secret|admin_token|integrationKey|rootPassword|adminPassword|adminUser|adminEmail|emailPassword|secretKey|appId|clientSecret|webhookSecret)$ + version: 3.9.1