Compare commits
1 Commits
main
...
codex/s60-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90599b0413 |
@@ -201,6 +201,8 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: andrew-web
|
app: andrew-web
|
||||||
|
annotations:
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
@@ -225,12 +227,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
@@ -265,7 +273,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`bluejay.dev`) || Host(`www.bluejay.dev`)
|
- match: (Host(`bluejay.dev`) || Host(`www.bluejay.dev`)) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: andrew-web
|
- name: andrew-web
|
||||||
|
|||||||
@@ -201,6 +201,8 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: dustin-web
|
app: dustin-web
|
||||||
|
annotations:
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
@@ -225,12 +227,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
@@ -265,7 +273,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`timeforta.co`) || Host(`www.timeforta.co`)
|
- match: (Host(`timeforta.co`) || Host(`www.timeforta.co`)) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: dustin-web
|
- name: dustin-web
|
||||||
|
|||||||
@@ -201,6 +201,8 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: erik-web
|
app: erik-web
|
||||||
|
annotations:
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
@@ -225,12 +227,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
@@ -265,7 +273,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`erckak.dev`) || Host(`www.erckak.dev`)
|
- match: (Host(`erckak.dev`) || Host(`www.erckak.dev`)) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: erik-web
|
- name: erik-web
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/healthz"
|
|
||||||
prometheus.io/path: /metrics/prometheus
|
prometheus.io/path: /metrics/prometheus
|
||||||
prometheus.io/port: "5000"
|
prometheus.io/port: "5000"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
@@ -56,7 +54,6 @@ spec:
|
|||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
- envFrom:
|
- envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: aistation-web-config
|
name: aistation-web-config
|
||||||
@@ -170,26 +167,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: aistation-web-tls
|
secretName: aistation-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose aistation-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: aistation-web-public
|
|
||||||
# namespace: fc-aistation
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`aistation.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: aistation-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: aistation-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -112,8 +112,6 @@ spec:
|
|||||||
app.kubernetes.io/name: chat-web
|
app.kubernetes.io/name: chat-web
|
||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/healthz"
|
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics/prometheus"
|
prometheus.io/path: "/metrics/prometheus"
|
||||||
@@ -130,7 +128,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 8080
|
containerPort: 8080
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
envFrom:
|
envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: chat-web-config
|
name: chat-web-config
|
||||||
|
|||||||
@@ -51,26 +51,3 @@ spec:
|
|||||||
port: 8080
|
port: 8080
|
||||||
tls:
|
tls:
|
||||||
secretName: remotedesktop-web-tls
|
secretName: remotedesktop-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose remotedesktop-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: remotedesktop-web-public
|
|
||||||
# namespace: fc-desktop
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`desktop.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: remotedesktop-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: remotedesktop-web
|
|
||||||
# port: 8080
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ spec:
|
|||||||
flowercore.io/tenant-id: system
|
flowercore.io/tenant-id: system
|
||||||
flowercore.io/created-by: bluejay-infra
|
flowercore.io/created-by: bluejay-infra
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/healthz"
|
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
@@ -69,7 +67,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 8080
|
containerPort: 8080
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
|
|||||||
@@ -30,26 +30,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: dms-web-tls
|
secretName: dms-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose dms-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: dms-web-public
|
|
||||||
# namespace: fc-dms
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`dms.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: dms-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: dms-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: fc-landing
|
app: fc-landing
|
||||||
|
annotations:
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
@@ -227,12 +229,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
@@ -298,7 +306,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`flowercore.io`) || Host(`www.flowercore.io`)
|
- match: (Host(`flowercore.io`) || Host(`www.flowercore.io`)) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: fc-landing
|
- name: fc-landing
|
||||||
@@ -316,7 +324,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- web
|
- web
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`flowercore.io`) || Host(`www.flowercore.io`)
|
- match: (Host(`flowercore.io`) || Host(`www.flowercore.io`)) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: fc-landing
|
- name: fc-landing
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/health"
|
|
||||||
prometheus.io/path: /metrics/prometheus
|
prometheus.io/path: /metrics/prometheus
|
||||||
prometheus.io/port: "5000"
|
prometheus.io/port: "5000"
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
@@ -56,7 +54,6 @@ spec:
|
|||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
- envFrom:
|
- envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: library-web-config
|
name: library-web-config
|
||||||
@@ -170,26 +167,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: library-web-tls
|
secretName: library-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose library-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: library-web-public
|
|
||||||
# namespace: fc-library
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`library.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: library-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: library-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -83,8 +83,6 @@ spec:
|
|||||||
app.kubernetes.io/name: fc-llm-bridge
|
app.kubernetes.io/name: fc-llm-bridge
|
||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/healthz"
|
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
@@ -118,7 +116,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
@@ -284,26 +281,3 @@ spec:
|
|||||||
port: 8080
|
port: 8080
|
||||||
tls:
|
tls:
|
||||||
secretName: fc-llm-bridge-tls
|
secretName: fc-llm-bridge-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose fc-llm-bridge publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: fc-llm-bridge-public
|
|
||||||
# namespace: fc-llm-bridge
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`llm-bridge.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: fc-llm-bridge-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: fc-llm-bridge
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -30,26 +30,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: menuboard-web-tls
|
secretName: menuboard-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose menuboard-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: menuboard-web-public
|
|
||||||
# namespace: fc-menuboard
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`menuboard.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: menuboard-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: menuboard-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ spec:
|
|||||||
labels:
|
labels:
|
||||||
app: messageboard-web
|
app: messageboard-web
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/health"
|
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics/prometheus"
|
prometheus.io/path: "/metrics/prometheus"
|
||||||
@@ -54,7 +52,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
envFrom:
|
envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: messageboard-web-config
|
name: messageboard-web-config
|
||||||
@@ -144,26 +141,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: messageboard-web-tls
|
secretName: messageboard-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose messageboard-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: messageboard-web-public
|
|
||||||
# namespace: fc-messageboard
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`messageboard.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: messageboard-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: messageboard-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -30,26 +30,3 @@ spec:
|
|||||||
port: 5300
|
port: 5300
|
||||||
tls:
|
tls:
|
||||||
secretName: mysql-web-tls
|
secretName: mysql-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose mysql-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: mysql-web-public
|
|
||||||
# namespace: fc-mysql
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`mysql.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: mysql-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: mysql-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -30,26 +30,3 @@ spec:
|
|||||||
port: 5400
|
port: 5400
|
||||||
tls:
|
tls:
|
||||||
secretName: php-web-tls
|
secretName: php-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose php-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: php-web-public
|
|
||||||
# namespace: fc-php
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`php.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: php-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: php-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -30,26 +30,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: presentations-web-tls
|
secretName: presentations-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose presentations-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: presentations-web-public
|
|
||||||
# namespace: fc-presentations
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`presentations.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: presentations-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: presentations-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ spec:
|
|||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/healthz"
|
|
||||||
kubectl.kubernetes.io/restartedAt: "2026-06-02T01:34:08-05:00"
|
kubectl.kubernetes.io/restartedAt: "2026-06-02T01:34:08-05:00"
|
||||||
prometheus.io/path: /metrics/prometheus
|
prometheus.io/path: /metrics/prometheus
|
||||||
prometheus.io/port: "5000"
|
prometheus.io/port: "5000"
|
||||||
@@ -57,7 +55,6 @@ spec:
|
|||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
- envFrom:
|
- envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: retail-web-config
|
name: retail-web-config
|
||||||
@@ -171,26 +168,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: retail-web-tls
|
secretName: retail-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose retail-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: retail-web-public
|
|
||||||
# namespace: fc-retail
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`retail.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: retail-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: retail-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -30,26 +30,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: scoreboard-web-tls
|
secretName: scoreboard-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose scoreboard-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: scoreboard-web-public
|
|
||||||
# namespace: fc-scoreboard
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`scoreboard.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: scoreboard-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: scoreboard-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -37,26 +37,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: segmentdisplay-web-tls
|
secretName: segmentdisplay-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose segmentdisplay-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: segmentdisplay-web-public
|
|
||||||
# namespace: fc-segmentdisplay
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`segmentdisplay.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: segmentdisplay-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: segmentdisplay-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -46,26 +46,3 @@ spec:
|
|||||||
services:
|
services:
|
||||||
- name: signage-web
|
- name: signage-web
|
||||||
port: 5190
|
port: 5190
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose signage-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: signage-web-public
|
|
||||||
# namespace: fc-signage
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`signage.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: signage-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: signage-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -97,7 +97,6 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: piper
|
- name: piper
|
||||||
image: rhasspy/wyoming-piper:latest
|
image: rhasspy/wyoming-piper:latest
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
env:
|
env:
|
||||||
- name: PYTHONHTTPSVERIFY
|
- name: PYTHONHTTPSVERIFY
|
||||||
value: "0"
|
value: "0"
|
||||||
@@ -524,8 +523,6 @@ spec:
|
|||||||
app.kubernetes.io/name: ttsreader-web
|
app.kubernetes.io/name: ttsreader-web
|
||||||
app.kubernetes.io/part-of: flowercore
|
app.kubernetes.io/part-of: flowercore
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/health"
|
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "5217"
|
prometheus.io/port: "5217"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
@@ -765,26 +762,3 @@ spec:
|
|||||||
port: 5217
|
port: 5217
|
||||||
tls:
|
tls:
|
||||||
secretName: ttsreader-tls
|
secretName: ttsreader-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose ttsreader-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: ttsreader-web-public
|
|
||||||
# namespace: fc-ttsreader
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`ttsreader.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: ttsreader-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: ttsreader-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -52,9 +52,6 @@ spec:
|
|||||||
app: updatecenter-web
|
app: updatecenter-web
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/"
|
|
||||||
labels:
|
labels:
|
||||||
app: updatecenter-web
|
app: updatecenter-web
|
||||||
spec:
|
spec:
|
||||||
@@ -66,7 +63,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: http://+:8080
|
value: http://+:8080
|
||||||
|
|||||||
@@ -201,6 +201,8 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: fit-web
|
app: fit-web
|
||||||
|
annotations:
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
@@ -225,12 +227,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
@@ -265,7 +273,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`flowerinsider.xyz`) || Host(`www.flowerinsider.xyz`)
|
- match: (Host(`flowerinsider.xyz`) || Host(`www.flowerinsider.xyz`)) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: fit-web
|
- name: fit-web
|
||||||
|
|||||||
@@ -257,6 +257,8 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: flowercore-web
|
app: flowercore-web
|
||||||
|
annotations:
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
@@ -281,12 +283,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`gitea.flowercore.io`)
|
- match: Host(`gitea.flowercore.io`) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: gitea-http
|
- name: gitea-http
|
||||||
|
|||||||
@@ -90,8 +90,6 @@ spec:
|
|||||||
app.kubernetes.io/name: knowledge-web
|
app.kubernetes.io/name: knowledge-web
|
||||||
app.kubernetes.io/part-of: bluejay-infra
|
app.kubernetes.io/part-of: bluejay-infra
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/healthz"
|
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics"
|
prometheus.io/path: "/metrics"
|
||||||
@@ -119,7 +117,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
@@ -289,26 +286,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: knowledge-tls
|
secretName: knowledge-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose knowledge-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: knowledge-web-public
|
|
||||||
# namespace: knowledge
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`knowledge.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: knowledge-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: knowledge-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`webmail.flowercore.io`)
|
- match: Host(`webmail.flowercore.io`) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: mail-webmail
|
- name: mail-webmail
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`element.flowercore.io`)
|
- match: Host(`element.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: element-web
|
- name: element-web
|
||||||
@@ -497,7 +497,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`matrix.flowercore.io`)
|
- match: Host(`matrix.flowercore.io`) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: synapse
|
- name: synapse
|
||||||
|
|||||||
@@ -216,24 +216,19 @@ data:
|
|||||||
- job_name: "pimanager-app"
|
- job_name: "pimanager-app"
|
||||||
scrape_interval: 15s
|
scrape_interval: 15s
|
||||||
metrics_path: /metrics
|
metrics_path: /metrics
|
||||||
scheme: https
|
|
||||||
tls_config:
|
|
||||||
insecure_skip_verify: true
|
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ["piez.iamworkin.lan"]
|
- targets: ["10.0.58.25:5000"]
|
||||||
labels:
|
labels:
|
||||||
instance: "piez"
|
instance: "piez"
|
||||||
service: "signalcontrol"
|
service: "pimanager"
|
||||||
vlan: "home"
|
vlan: "home"
|
||||||
device: "pi4-ezconnect"
|
device: "pi4-ezconnect"
|
||||||
rig: "signal-b"
|
- targets: ["10.0.58.113:5200"]
|
||||||
- targets: ["pirelay.iamworkin.lan"]
|
|
||||||
labels:
|
labels:
|
||||||
instance: "pirelay"
|
instance: "pirelay"
|
||||||
service: "signalcontrol"
|
service: "pimanager"
|
||||||
vlan: "home"
|
vlan: "home"
|
||||||
device: "pi3-ks0212"
|
device: "pi3-ks0212"
|
||||||
rig: "signal-a"
|
|
||||||
|
|
||||||
# Epson ET-3750 EcoTank Printer SNMP
|
# Epson ET-3750 EcoTank Printer SNMP
|
||||||
- job_name: "snmp-printer"
|
- job_name: "snmp-printer"
|
||||||
@@ -493,12 +488,6 @@ data:
|
|||||||
- "https://desktop.iamworkin.lan/"
|
- "https://desktop.iamworkin.lan/"
|
||||||
- "https://print.iamworkin.lan/healthz" # root 401 behind API key auth; /healthz anonymous 200
|
- "https://print.iamworkin.lan/healthz" # root 401 behind API key auth; /healthz anonymous 200
|
||||||
- "https://dns.iamworkin.lan/healthz" # root auth-gated by OIDC; /healthz anonymous 200
|
- "https://dns.iamworkin.lan/healthz" # root auth-gated by OIDC; /healthz anonymous 200
|
||||||
- "https://signalcontrol.iamworkin.lan/health" # FlowerCore.SignalControl Pi control plane
|
|
||||||
- "https://flowercore.iamworkin.lan/healthz" # FlowerCore landing
|
|
||||||
- "https://replay.iamworkin.lan/healthz" # FlowerCore.Signage replay surface
|
|
||||||
- "https://worldbuilder.iamworkin.lan/healthz" # FlowerCore.WorldBuilder
|
|
||||||
- "https://updates.iamworkin.lan/api/v1/manifests/_schema" # UpdateCenter plural LAN alias
|
|
||||||
- "https://updatecenter-internal.iamworkin.lan/api/v1/manifests/_schema" # internal UC schema route
|
|
||||||
- "https://chat.iamworkin.lan/healthz" # OIDC staged; keep blackbox off root before enforcement flips
|
- "https://chat.iamworkin.lan/healthz" # OIDC staged; keep blackbox off root before enforcement flips
|
||||||
- "https://dist.iamworkin.lan/healthz" # root/admin auth-gated by OIDC; /healthz anonymous 200
|
- "https://dist.iamworkin.lan/healthz" # root/admin auth-gated by OIDC; /healthz anonymous 200
|
||||||
- "https://dms.iamworkin.lan/healthz" # future OIDC posture; health route is already anonymous/live
|
- "https://dms.iamworkin.lan/healthz" # future OIDC posture; health route is already anonymous/live
|
||||||
@@ -922,13 +911,12 @@ data:
|
|||||||
# of idle and SNMP times out, so 5m for: would page nightly. A
|
# of idle and SNMP times out, so 5m for: would page nightly. A
|
||||||
# genuine printer outage (jam, disconnected) lasts well over 30m.
|
# genuine printer outage (jam, disconnected) lasts well over 30m.
|
||||||
- alert: EpsonPrinterDown
|
- alert: EpsonPrinterDown
|
||||||
expr: (max_over_time(up{job="snmp-printer"}[35m]) == bool 0) == 1 and (hour() >= 13 or hour() < 1)
|
expr: up{job="snmp-printer"} == 0
|
||||||
for: 30m
|
for: 30m
|
||||||
labels:
|
labels:
|
||||||
severity: info
|
severity: warning
|
||||||
alert_channel: irc
|
|
||||||
annotations:
|
annotations:
|
||||||
summary: "Epson ET-3750 SNMP unreachable during waking hours (30m)"
|
summary: "Epson ET-3750 SNMP unreachable for >30m (likely actual fault, not sleep)"
|
||||||
|
|
||||||
- alert: SynologyDiskLow
|
- alert: SynologyDiskLow
|
||||||
expr: hrStorageUsed{job="snmp-nas"} / hrStorageSize{job="snmp-nas"} * 100 > 85
|
expr: hrStorageUsed{job="snmp-nas"} / hrStorageSize{job="snmp-nas"} * 100 > 85
|
||||||
|
|||||||
@@ -134,6 +134,8 @@ spec:
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: pki-web
|
app: pki-web
|
||||||
|
annotations:
|
||||||
|
flowercore.io/healthz-auth-policy: "allow-anonymous"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
@@ -158,12 +160,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: 80
|
port: 80
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 3
|
initialDelaySeconds: 3
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
@@ -201,6 +209,7 @@ spec:
|
|||||||
dnsNames:
|
dnsNames:
|
||||||
- pki.iamworkin.lan
|
- pki.iamworkin.lan
|
||||||
---
|
---
|
||||||
|
# Internal-only route: if a public twin is ever operator-approved, gate it with Host(`<public-host>`) && (Method(`GET`) || Method(`HEAD`)).
|
||||||
# Traefik IngressRoute
|
# Traefik IngressRoute
|
||||||
apiVersion: traefik.io/v1alpha1
|
apiVersion: traefik.io/v1alpha1
|
||||||
kind: IngressRoute
|
kind: IngressRoute
|
||||||
|
|||||||
@@ -114,9 +114,6 @@ spec:
|
|||||||
app: telephony-web
|
app: telephony-web
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/health"
|
|
||||||
labels:
|
labels:
|
||||||
app: telephony-web
|
app: telephony-web
|
||||||
spec:
|
spec:
|
||||||
@@ -164,7 +161,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 5100
|
- containerPort: 5100
|
||||||
name: http
|
name: http
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
env:
|
env:
|
||||||
- name: Telephony__Twilio__AccountSid
|
- name: Telephony__Twilio__AccountSid
|
||||||
valueFrom:
|
valueFrom:
|
||||||
@@ -211,12 +207,18 @@ spec:
|
|||||||
httpGet:
|
httpGet:
|
||||||
path: /health
|
path: /health
|
||||||
port: 5100
|
port: 5100
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /health
|
path: /health
|
||||||
port: 5100
|
port: 5100
|
||||||
|
httpHeaders:
|
||||||
|
- name: X-Forwarded-Proto
|
||||||
|
value: https
|
||||||
initialDelaySeconds: 10
|
initialDelaySeconds: 10
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
volumes:
|
volumes:
|
||||||
@@ -260,12 +262,12 @@ spec:
|
|||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- kind: Rule
|
- kind: Rule
|
||||||
match: Host(`telephony.flowercore.io`)
|
match: Host(`telephony.flowercore.io`) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
services:
|
services:
|
||||||
- name: telephony-web
|
- name: telephony-web
|
||||||
port: 5100
|
port: 5100
|
||||||
- kind: Rule
|
- kind: Rule
|
||||||
match: Host(`telephony.iamwork.in`)
|
match: Host(`telephony.iamwork.in`) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
services:
|
services:
|
||||||
- name: telephony-web
|
- name: telephony-web
|
||||||
port: 5100
|
port: 5100
|
||||||
@@ -391,3 +393,4 @@ spec:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
basicAuth:
|
basicAuth:
|
||||||
secret: traefik-dashboard-auth
|
secret: traefik-dashboard-auth
|
||||||
---
|
---
|
||||||
# Dashboard IngressRoute
|
# Internal-only route: if a public twin is ever operator-approved, gate it with Host(`<public-host>`) && (Method(`GET`) || Method(`HEAD`)).
|
||||||
apiVersion: traefik.io/v1alpha1
|
# Dashboard IngressRoute
|
||||||
kind: IngressRoute
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
metadata:
|
metadata:
|
||||||
name: traefik-dashboard
|
name: traefik-dashboard
|
||||||
namespace: traefik-system
|
namespace: traefik-system
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ spec:
|
|||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- kind: Rule
|
- kind: Rule
|
||||||
match: Host(`voice.bluejay.dev`)
|
match: Host(`voice.bluejay.dev`) && (Method(`GET`) || Method(`HEAD`) || Method(`POST`) || Method(`OPTIONS`))
|
||||||
services:
|
services:
|
||||||
- name: voice-bridge
|
- name: voice-bridge
|
||||||
port: 8766
|
port: 8766
|
||||||
@@ -84,7 +84,7 @@ spec:
|
|||||||
- websecure
|
- websecure
|
||||||
routes:
|
routes:
|
||||||
- kind: Rule
|
- kind: Rule
|
||||||
match: Host(`voice-ws.bluejay.dev`)
|
match: Host(`voice-ws.bluejay.dev`) && (Method(`GET`) || Method(`HEAD`))
|
||||||
services:
|
services:
|
||||||
- name: voice-bridge
|
- name: voice-bridge
|
||||||
port: 8765
|
port: 8765
|
||||||
|
|||||||
@@ -77,8 +77,6 @@ spec:
|
|||||||
flowercore.io/tenant-id: system
|
flowercore.io/tenant-id: system
|
||||||
flowercore.io/created-by: bluejay-infra
|
flowercore.io/created-by: bluejay-infra
|
||||||
annotations:
|
annotations:
|
||||||
fc.flowercore.io/healthz-anon: "true"
|
|
||||||
fc.flowercore.io/probe-path: "/healthz"
|
|
||||||
prometheus.io/scrape: "true"
|
prometheus.io/scrape: "true"
|
||||||
prometheus.io/port: "8080"
|
prometheus.io/port: "8080"
|
||||||
prometheus.io/path: "/metrics/prometheus"
|
prometheus.io/path: "/metrics/prometheus"
|
||||||
@@ -95,7 +93,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
# fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178) before any future public/OIDC flip.
|
|
||||||
env:
|
env:
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:8080"
|
value: "http://+:8080"
|
||||||
@@ -257,26 +254,3 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
secretName: worldbuilder-web-tls
|
secretName: worldbuilder-web-tls
|
||||||
# ---- PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only) ----
|
|
||||||
# When the operator decides to expose worldbuilder-web publicly, uncomment + update the host,
|
|
||||||
# then verify the five safe-to-expose gates (authentik-safe-to-expose-readiness-2026-06-07.md section 2).
|
|
||||||
#
|
|
||||||
# --- IngressRoute ---
|
|
||||||
# apiVersion: traefik.io/v1alpha1
|
|
||||||
# kind: IngressRoute
|
|
||||||
# metadata:
|
|
||||||
# name: worldbuilder-web-public
|
|
||||||
# namespace: worldbuilder
|
|
||||||
# spec:
|
|
||||||
# entryPoints: [websecure]
|
|
||||||
# routes:
|
|
||||||
# - match: Host(`worldbuilder.flowercore.io`) && (Method(`GET`) || Method(`HEAD`))
|
|
||||||
# kind: Rule
|
|
||||||
# middlewares:
|
|
||||||
# - name: worldbuilder-web-public-profile-header # injects entitlement profile
|
|
||||||
# services:
|
|
||||||
# - name: worldbuilder-web
|
|
||||||
# port: 80
|
|
||||||
# tls: {}
|
|
||||||
# # POST/PUT/PATCH/DELETE miss every route -> Traefik 404 -> no admin writes on the public surface.
|
|
||||||
# # Reference pattern: dist.flowercore.io (already live + method-gated; do not edit that one).
|
|
||||||
|
|||||||
@@ -344,6 +344,7 @@ spec:
|
|||||||
dnsNames:
|
dnsNames:
|
||||||
- zabbix.iamworkin.lan
|
- zabbix.iamworkin.lan
|
||||||
---
|
---
|
||||||
|
# Internal-only route: if a public twin is ever operator-approved, gate it with Host(`<public-host>`) && (Method(`GET`) || Method(`HEAD`)).
|
||||||
# Traefik IngressRoute
|
# Traefik IngressRoute
|
||||||
apiVersion: traefik.io/v1alpha1
|
apiVersion: traefik.io/v1alpha1
|
||||||
kind: IngressRoute
|
kind: IngressRoute
|
||||||
|
|||||||
@@ -13,21 +13,67 @@ public sealed class FleetManifestLintTests
|
|||||||
|
|
||||||
private static readonly HashSet<string> PublicReadOnlyHosts = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> PublicReadOnlyHosts = new(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
|
"bluejay.dev",
|
||||||
"brochure.flowercore.io",
|
"brochure.flowercore.io",
|
||||||
"dist.flowercore.io",
|
"dist.flowercore.io",
|
||||||
|
"element.flowercore.io",
|
||||||
|
"erckak.dev",
|
||||||
|
"flowercore.io",
|
||||||
|
"flowerinsider.xyz",
|
||||||
|
"timeforta.co",
|
||||||
|
"voice-ws.bluejay.dev",
|
||||||
|
"www.bluejay.dev",
|
||||||
|
"www.erckak.dev",
|
||||||
|
"www.flowercore.io",
|
||||||
|
"www.flowerinsider.xyz",
|
||||||
|
"www.timeforta.co",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hosts that allow a tightly bounded write surface in addition to GET/HEAD.
|
// Public hosts that allow a tightly bounded write surface in addition to
|
||||||
// updatecenter.iamworkin.lan accepts POST /api/v1/checkin/{id}
|
// GET/HEAD. updatecenter.iamworkin.lan accepts POST /api/v1/checkin/{id}
|
||||||
// (bootstrap-JWT) so its allowlist is GET||HEAD||POST||OPTIONS — but
|
// (bootstrap-JWT) so its allowlist is GET||HEAD||POST||OPTIONS — but
|
||||||
// PUT/PATCH/DELETE must still 404 at the route. Public
|
// PUT/PATCH/DELETE must still 404 at the route. Anything wider than this
|
||||||
// update.flowercore.io remains a GET/HEAD download surface in the
|
// set should fail this lint.
|
||||||
// FlowerCore.Updater sibling manifest and is covered by the general
|
//
|
||||||
// public-method allowlist lint instead of this write-surface rule.
|
// PUB-1 (2026-05-06): update.flowercore.io / updates.flowercore.io were
|
||||||
|
// added for the Cloudflare-proxied public Update Center edge. They use the
|
||||||
|
// same bounded read-write allowlist as the LAN pair.
|
||||||
private static readonly HashSet<string> PublicReadWriteAllowlistHosts = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> PublicReadWriteAllowlistHosts = new(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
|
"chat.flowercore.io",
|
||||||
|
"gitea.flowercore.io",
|
||||||
|
"matrix.flowercore.io",
|
||||||
|
"telephony.flowercore.io",
|
||||||
|
"telephony.iamwork.in",
|
||||||
"updatecenter.iamworkin.lan",
|
"updatecenter.iamworkin.lan",
|
||||||
"updates.iamworkin.lan",
|
"updates.iamworkin.lan",
|
||||||
|
"update.flowercore.io",
|
||||||
|
"updates.flowercore.io",
|
||||||
|
"voice.bluejay.dev",
|
||||||
|
"webmail.flowercore.io",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, string> InfraHealthzProbeDeployments = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
["andrew"] = "andrew-web",
|
||||||
|
["dustin"] = "dustin-web",
|
||||||
|
["erik"] = "erik-web",
|
||||||
|
["fc-landing"] = "fc-landing",
|
||||||
|
["fit"] = "fit-web",
|
||||||
|
["flowercore"] = "flowercore-web",
|
||||||
|
["pki-web"] = "pki-web",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, string> InfraForwardedProtoProbeDeployments = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
["andrew"] = "andrew-web",
|
||||||
|
["dustin"] = "dustin-web",
|
||||||
|
["erik"] = "erik-web",
|
||||||
|
["fc-landing"] = "fc-landing",
|
||||||
|
["fit"] = "fit-web",
|
||||||
|
["flowercore"] = "flowercore-web",
|
||||||
|
["pki-web"] = "pki-web",
|
||||||
|
["telephony"] = "telephony-web",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly HashSet<string> ApiKeyProtectedDeployments = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> ApiKeyProtectedDeployments = new(StringComparer.Ordinal)
|
||||||
@@ -65,7 +111,7 @@ public sealed class FleetManifestLintTests
|
|||||||
["github-runner-updater"] = "https://github.com/astoltz/FlowerCore.Updater",
|
["github-runner-updater"] = "https://github.com/astoltz/FlowerCore.Updater",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly HashSet<string> RepoScopedLinuxRunnerDeployments = new(StringComparer.Ordinal)
|
private static readonly HashSet<string> ScaledLinuxRunnerDeployments = new(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
"github-runner-sharedpos",
|
"github-runner-sharedpos",
|
||||||
"github-runner-puppet",
|
"github-runner-puppet",
|
||||||
@@ -79,44 +125,6 @@ public sealed class FleetManifestLintTests
|
|||||||
"github-runner-updater",
|
"github-runner-updater",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly IReadOnlyDictionary<string, (string Deployment, string ProbePath)> BroaderHardeningDeployments =
|
|
||||||
new Dictionary<string, (string Deployment, string ProbePath)>(StringComparer.Ordinal)
|
|
||||||
{
|
|
||||||
["fc-aistation"] = ("aistation-web", "/healthz"),
|
|
||||||
["fc-chat"] = ("chat-web", "/healthz"),
|
|
||||||
["fc-devicemgmt"] = ("fc-devicemgmt-web", "/healthz"),
|
|
||||||
["fc-library"] = ("library-web", "/health"),
|
|
||||||
["fc-llm-bridge"] = ("fc-llm-bridge", "/healthz"),
|
|
||||||
["fc-messageboard"] = ("messageboard-web", "/health"),
|
|
||||||
["fc-retail"] = ("retail-web", "/healthz"),
|
|
||||||
["fc-ttsreader"] = ("ttsreader-web", "/health"),
|
|
||||||
["fc-updater"] = ("updatecenter-web", "/"),
|
|
||||||
["knowledge"] = ("knowledge-web", "/healthz"),
|
|
||||||
["telephony"] = ("telephony-web", "/health"),
|
|
||||||
["worldbuilder"] = ("worldbuilder-web", "/healthz"),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly HashSet<string> BroaderHardeningInternalPrestageApps = new(StringComparer.Ordinal)
|
|
||||||
{
|
|
||||||
"fc-aistation",
|
|
||||||
"fc-desktop",
|
|
||||||
"fc-dms",
|
|
||||||
"fc-library",
|
|
||||||
"fc-llm-bridge",
|
|
||||||
"fc-menuboard",
|
|
||||||
"fc-messageboard",
|
|
||||||
"fc-mysql",
|
|
||||||
"fc-php",
|
|
||||||
"fc-presentations",
|
|
||||||
"fc-retail",
|
|
||||||
"fc-scoreboard",
|
|
||||||
"fc-segmentdisplay",
|
|
||||||
"fc-signage",
|
|
||||||
"fc-ttsreader",
|
|
||||||
"knowledge",
|
|
||||||
"worldbuilder",
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly IReadOnlyDictionary<string, string> WritableRunnerEnv = new Dictionary<string, string>(StringComparer.Ordinal)
|
private static readonly IReadOnlyDictionary<string, string> WritableRunnerEnv = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
["HOME"] = "/home/runner",
|
["HOME"] = "/home/runner",
|
||||||
@@ -165,8 +173,13 @@ public sealed class FleetManifestLintTests
|
|||||||
}))
|
}))
|
||||||
.Where(entry => PublicReadOnlyHosts.Any(host => entry.Match.Contains($"Host(`{host}`)", StringComparison.Ordinal)))
|
.Where(entry => PublicReadOnlyHosts.Any(host => entry.Match.Contains($"Host(`{host}`)", StringComparison.Ordinal)))
|
||||||
.Where(entry => !entry.Match.Contains("Method(`GET`)", StringComparison.Ordinal)
|
.Where(entry => !entry.Match.Contains("Method(`GET`)", StringComparison.Ordinal)
|
||||||
|| !entry.Match.Contains("Method(`HEAD`)", StringComparison.Ordinal))
|
|| !entry.Match.Contains("Method(`HEAD`)", StringComparison.Ordinal)
|
||||||
.Select(entry => $"{entry.Document.Descriptor} is missing an explicit GET/HEAD method allowlist.")
|
|| entry.Match.Contains("Method(`POST`)", StringComparison.Ordinal)
|
||||||
|
|| entry.Match.Contains("Method(`PUT`)", StringComparison.Ordinal)
|
||||||
|
|| entry.Match.Contains("Method(`PATCH`)", StringComparison.Ordinal)
|
||||||
|
|| entry.Match.Contains("Method(`DELETE`)", StringComparison.Ordinal)
|
||||||
|
|| entry.Match.Contains("Method(`OPTIONS`)", StringComparison.Ordinal))
|
||||||
|
.Select(entry => $"{entry.Document.Descriptor} must explicitly allow GET/HEAD only on a public read-only host.")
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
violations.Should().BeEmpty();
|
violations.Should().BeEmpty();
|
||||||
@@ -305,17 +318,17 @@ public sealed class FleetManifestLintTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GitHubRunnerFleet_MustAvoidRwoMultiAttachForRepoScopedDeployments()
|
public void GitHubRunnerFleet_MustAvoidRwoMultiAttachForScaledDeployments()
|
||||||
{
|
{
|
||||||
var deployments = GitHubRunnerDeployments();
|
var deployments = GitHubRunnerDeployments();
|
||||||
|
|
||||||
foreach (var deploymentName in RepoScopedLinuxRunnerDeployments)
|
foreach (var deploymentName in ScaledLinuxRunnerDeployments)
|
||||||
{
|
{
|
||||||
var deployment = deployments[deploymentName];
|
var deployment = deployments[deploymentName];
|
||||||
// Sprint 34 ops trimmed runner load while the cluster was degraded
|
// Scaled runners must have >= 2 replicas (avoid single-pod bottleneck).
|
||||||
// to two healthy nodes. Repo-scoped runners can be tuned back above
|
// Individual deployments may be tuned upward per CI activity — see
|
||||||
// one replica, but they must stay RWO-safe before that happens.
|
// "runners: right-size replica counts per 14d CI activity (#24)".
|
||||||
ReplicaCount(deployment).Should().BeGreaterOrEqualTo(1, $"{deploymentName} must keep at least one repo-scoped runner online");
|
ReplicaCount(deployment).Should().BeGreaterOrEqualTo(2, $"{deploymentName} is in the scaled set and must run with at least 2 replicas");
|
||||||
|
|
||||||
var volumes = deployment.MappingSequence("spec", "template", "spec", "volumes");
|
var volumes = deployment.MappingSequence("spec", "template", "spec", "volumes");
|
||||||
var claimNames = volumes
|
var claimNames = volumes
|
||||||
@@ -323,7 +336,7 @@ public sealed class FleetManifestLintTests
|
|||||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
claimNames.Should().BeEmpty($"{deploymentName} must remain ready for safe multi-replica scaling without sharing a RWO PVC");
|
claimNames.Should().BeEmpty($"{deploymentName} is scaled and must not share a RWO PVC");
|
||||||
volumes.Should().Contain(volume =>
|
volumes.Should().Contain(volume =>
|
||||||
string.Equals(ManifestNodeExtensions.Scalar(volume, "name"), "nuget-cache", StringComparison.Ordinal)
|
string.Equals(ManifestNodeExtensions.Scalar(volume, "name"), "nuget-cache", StringComparison.Ordinal)
|
||||||
&& ManifestNodeExtensions.Mapping(volume, "emptyDir") != null);
|
&& ManifestNodeExtensions.Mapping(volume, "emptyDir") != null);
|
||||||
@@ -507,6 +520,49 @@ public sealed class FleetManifestLintTests
|
|||||||
violations.Should().BeEmpty();
|
violations.Should().BeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AuthSafeInfraHealthzProbes_MustDeclareAnonymousHealthzContract()
|
||||||
|
{
|
||||||
|
var violations = InfraHealthzProbeDeployments.SelectMany(expected =>
|
||||||
|
{
|
||||||
|
var deployment = AppDocuments(expected.Key)
|
||||||
|
.Single(document => document.Kind == "Deployment" && document.Name == expected.Value);
|
||||||
|
var hasHealthzProbe = deployment.MainContainerMappings()
|
||||||
|
.Any(container => ProbeHttpGetPath(container, "readinessProbe") == "/healthz"
|
||||||
|
|| ProbeHttpGetPath(container, "startupProbe") == "/healthz"
|
||||||
|
|| ProbeHttpGetPath(container, "livenessProbe") == "/healthz");
|
||||||
|
|
||||||
|
return hasHealthzProbe
|
||||||
|
&& !string.Equals(PodAnnotation(deployment, "flowercore.io/healthz-auth-policy"), "allow-anonymous", StringComparison.Ordinal)
|
||||||
|
? new[] { $"{deployment.Descriptor} probes /healthz but lacks flowercore.io/healthz-auth-policy: allow-anonymous." }
|
||||||
|
: Array.Empty<string>();
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
violations.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AuthSafeInfraHttpProbes_MustSendForwardedProtoHttpsHeader()
|
||||||
|
{
|
||||||
|
var violations = InfraForwardedProtoProbeDeployments.SelectMany(expected =>
|
||||||
|
{
|
||||||
|
var deployment = AppDocuments(expected.Key)
|
||||||
|
.Single(document => document.Kind == "Deployment" && document.Name == expected.Value);
|
||||||
|
|
||||||
|
return deployment.MainContainerMappings()
|
||||||
|
.SelectMany(container => new[] { "startupProbe", "readinessProbe", "livenessProbe" }
|
||||||
|
.Where(probeKey => ProbeHttpGetPath(container, probeKey) is "/healthz" or "/health")
|
||||||
|
.Where(probeKey => !string.Equals(ProbeHttpGetHeaderValue(container, probeKey, "X-Forwarded-Proto"), "https", StringComparison.Ordinal))
|
||||||
|
.Select(probeKey =>
|
||||||
|
{
|
||||||
|
var containerName = ManifestNodeExtensions.Scalar(container, "name") ?? "<unnamed>";
|
||||||
|
return $"{deployment.Descriptor} container '{containerName}' {probeKey} is missing X-Forwarded-Proto=https.";
|
||||||
|
}));
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
violations.Should().BeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Knowledge_OidcEnforcement_MustKeepHealthzAnonymousContractVisibleInManifest()
|
public void Knowledge_OidcEnforcement_MustKeepHealthzAnonymousContractVisibleInManifest()
|
||||||
{
|
{
|
||||||
@@ -646,6 +702,7 @@ public sealed class FleetManifestLintTests
|
|||||||
var expectedFiles = new[]
|
var expectedFiles = new[]
|
||||||
{
|
{
|
||||||
"1password-item.yaml",
|
"1password-item.yaml",
|
||||||
|
"argocd-application.yaml",
|
||||||
"certificate-web.yaml",
|
"certificate-web.yaml",
|
||||||
"clusterrole-operator.yaml",
|
"clusterrole-operator.yaml",
|
||||||
"clusterrolebinding-operator.yaml",
|
"clusterrolebinding-operator.yaml",
|
||||||
@@ -801,62 +858,17 @@ public sealed class FleetManifestLintTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FcDeviceManagement_MustRelyOnApplicationSetDiscovery()
|
public void FcDeviceManagement_ArgocdApplicationMustMatchApplicationSetDiscoveryConventions()
|
||||||
{
|
{
|
||||||
var documents = FcDeviceManagementDocuments();
|
var application = FcDeviceManagementDocuments()
|
||||||
|
.Single(document => document.Kind == "Application" && document.Name == "infra-fc-devicemgmt");
|
||||||
|
|
||||||
documents.Should().NotContain(document => document.Kind == "Application");
|
application.Namespace.Should().Be("argocd");
|
||||||
|
application.Scalar("spec", "source", "repoURL")
|
||||||
var ns = documents.Single(document => document.Kind == "Namespace" && document.Name == "fc-devicemgmt");
|
.Should()
|
||||||
ns.FileText.Should().Contain("ArgoCD discovers this directory as Application `infra-fc-devicemgmt`.");
|
.Be("http://gitea-clusterip.gitea.svc.cluster.local:3000/bluejay/bluejay-infra.git");
|
||||||
}
|
application.Scalar("spec", "source", "path").Should().Be("apps/fc-devicemgmt");
|
||||||
|
application.Scalar("spec", "destination", "namespace").Should().Be("fc-devicemgmt");
|
||||||
[Fact]
|
|
||||||
public void BroaderHardeningDeployments_MustAnnotateAnonymousHealthProbeIntent()
|
|
||||||
{
|
|
||||||
foreach (var expected in BroaderHardeningDeployments)
|
|
||||||
{
|
|
||||||
var deployment = AppDocuments(expected.Key)
|
|
||||||
.Single(document => document.Kind == "Deployment" && document.Name == expected.Value.Deployment);
|
|
||||||
|
|
||||||
PodAnnotation(deployment, "fc.flowercore.io/healthz-anon").Should().Be("true");
|
|
||||||
PodAnnotation(deployment, "fc.flowercore.io/probe-path").Should().Be(expected.Value.ProbePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void BroaderHardeningDeployments_MustDocumentForwardedProtoAuthPosture()
|
|
||||||
{
|
|
||||||
foreach (var expected in BroaderHardeningDeployments)
|
|
||||||
{
|
|
||||||
var deployment = AppDocuments(expected.Key)
|
|
||||||
.Single(document => document.Kind == "Deployment" && document.Name == expected.Value.Deployment);
|
|
||||||
|
|
||||||
deployment.FileText.Should().Contain(
|
|
||||||
"fc-safe-to-expose: X-Forwarded-Proto handled by AddFlowerCoreWebAuth (ADR-178)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void BroaderHardeningInternalApps_MustOnlyPrestageCommentedPublicMethodAllowlist()
|
|
||||||
{
|
|
||||||
foreach (var app in BroaderHardeningInternalPrestageApps)
|
|
||||||
{
|
|
||||||
var documents = AppDocuments(app);
|
|
||||||
var text = string.Join(Environment.NewLine, documents.Select(document => document.FileText));
|
|
||||||
|
|
||||||
text.Should().Contain("PUBLIC HOST PRE-STAGING (DISABLED - Sprint 61+ exposure go-decision only)");
|
|
||||||
text.Should().Contain("# - match: Host(`");
|
|
||||||
text.Should().Contain("Method(`GET`) || Method(`HEAD`)");
|
|
||||||
|
|
||||||
documents
|
|
||||||
.Where(document => document.Kind == "IngressRoute")
|
|
||||||
.SelectMany(document => document.MappingSequence("spec", "routes"))
|
|
||||||
.Select(route => ManifestNodeExtensions.Scalar(route, "match") ?? string.Empty)
|
|
||||||
.Should()
|
|
||||||
.NotContain(match => match.Contains(".flowercore.io", StringComparison.Ordinal),
|
|
||||||
"Sprint 61 broader hardening only pre-stages commented public hosts for internal-only apps");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -1093,6 +1105,20 @@ public sealed class FleetManifestLintTests
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string? ProbeHttpGetHeaderValue(YamlMappingNode container, string probeKey, string name)
|
||||||
|
{
|
||||||
|
if (!ManifestNodeExtensions.TryGetMapping(container, probeKey, out var probe)
|
||||||
|
|| !ManifestNodeExtensions.TryGetMapping(probe, "httpGet", out var httpGet))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ManifestNodeExtensions.MappingSequence(httpGet, "httpHeaders")
|
||||||
|
.Where(header => string.Equals(ManifestNodeExtensions.Scalar(header, "name"), name, StringComparison.Ordinal))
|
||||||
|
.Select(header => ManifestNodeExtensions.Scalar(header, "value"))
|
||||||
|
.SingleOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<ManifestDocument> FcDeviceManagementDocuments()
|
private static IReadOnlyList<ManifestDocument> FcDeviceManagementDocuments()
|
||||||
{
|
{
|
||||||
return Inventory.Documents
|
return Inventory.Documents
|
||||||
|
|||||||
Reference in New Issue
Block a user