diff --git a/apps/fc-dns/fc-dns.yaml b/apps/fc-dns/fc-dns.yaml
index c57c0d5..52f5c28 100644
--- a/apps/fc-dns/fc-dns.yaml
+++ b/apps/fc-dns/fc-dns.yaml
@@ -26,6 +26,20 @@ metadata:
spec:
itemPath: "vaults/IAmWorkin/items/dns-oidc-client"
---
+# Service X-Api-Key for the cert-manager ACME webhook -> dns-web call path
+# (Phase 0 auth-flip). The 1Password operator resolves this item into a K8s
+# Secret of the same name; the `api_key` field becomes Secret key `api_key`.
+# dns-web reads it as FlowerCore__Auth__ApiKey (FcApiKey scheme, Operator
+# principal); dns-acme-webhook sends it as the X-Api-Key header. Dormant while
+# FlowerCore__Auth__Enabled=false (all policies allow-all).
+apiVersion: onepassword.com/v1
+kind: OnePasswordItem
+metadata:
+ name: dns-api-keys
+ namespace: fc-dns
+spec:
+ itemPath: "vaults/IAmWorkin/items/FlowerCore DNS API Keys"
+---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
@@ -111,7 +125,7 @@ spec:
fsGroup: 1654
containers:
- name: dns-web
- image: localhost/fc-dns-web:v20260614-wave5-isolation-6124856
+ image: localhost/fc-dns-web:v20260615-phase0-hybrid-f77fb94
imagePullPolicy: Never
securityContext:
readOnlyRootFilesystem: true
@@ -148,6 +162,16 @@ spec:
name: dns-oidc-client
key: client_secret
optional: true
+ # Service X-Api-Key accepted by the FcApiKey scheme. The standard
+ # key maps to an Operator principal (satisfies OperatorPolicy on the
+ # ACME present/cleanup endpoints). optional:true keeps the pod
+ # starting if the 1P operator has not yet produced the secret.
+ - name: FlowerCore__Auth__ApiKey
+ valueFrom:
+ secretKeyRef:
+ name: dns-api-keys
+ key: api_key
+ optional: true
- name: FlowerCore__Auth__Enabled
value: "false"
- name: FlowerCore__Auth__Oidc__Enabled
@@ -209,6 +233,54 @@ spec:
targetPort: 5320
type: ClusterIP
---
+# Defense-in-depth ingress isolation for dns-web (Phase 0). NetworkPolicy is
+# L3/L4 and cannot path-scope, so it CANNOT restrict only present/cleanup — the
+# real control on those endpoints is the X-Api-Key + OperatorPolicy. This policy
+# simply confines who may reach dns-web:5320 to known network zones without
+# breaking any live path:
+# * Traefik pods -> UI/API on dns.iamworkin.lan
+# * same fc-dns namespace -> dns-acme-webhook -> present/cleanup
+# * cluster pod CIDR (10.42/16) -> in-cluster Prometheus scrape, etc.
+# * node + LAN CIDRs -> kubelet probes, noc1 host-net Prometheus
+# Egress is intentionally left unrestricted: dns-web must reach pfSense
+# (diag_command.php / HTTPS), the K8s API, Authentik OIDC discovery, step-ca,
+# and DNS — over-tight egress would break the provider + auth paths.
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ name: dns-web-ingress-isolation
+ namespace: fc-dns
+spec:
+ podSelector:
+ matchLabels:
+ app.kubernetes.io/name: dns-web
+ policyTypes:
+ - Ingress
+ ingress:
+ - from:
+ - namespaceSelector:
+ matchLabels:
+ kubernetes.io/metadata.name: traefik-system
+ podSelector:
+ matchLabels:
+ app.kubernetes.io/name: traefik
+ - namespaceSelector:
+ matchLabels:
+ kubernetes.io/metadata.name: fc-dns
+ - ipBlock:
+ cidr: 10.42.0.0/16
+ - ipBlock:
+ cidr: 10.0.56.0/24
+ - ipBlock:
+ cidr: 10.0.57.0/24
+ - ipBlock:
+ cidr: 10.0.58.0/24
+ - ipBlock:
+ cidr: 10.0.68.0/27
+ ports:
+ - port: 5320
+ protocol: TCP
+---
apiVersion: v1
kind: ServiceAccount
metadata:
@@ -303,7 +375,7 @@ spec:
fsGroup: 1654
containers:
- name: dns-acme-webhook
- image: localhost/fc-dns-acme-webhook:v20260614-wave5-isolation-6124856
+ image: localhost/fc-dns-acme-webhook:v20260615-phase0-hybrid-f77fb94
imagePullPolicy: Never
securityContext:
readOnlyRootFilesystem: true
@@ -322,6 +394,16 @@ spec:
value: /tls/tls.key
- name: FlowerCore__Dns__AcmeWebhook__ServiceBaseUrl
value: http://dns-web:5320
+ # X-Api-Key sent to dns-web on present/cleanup so the webhook
+ # authenticates as an Operator once dns-web auth is enabled.
+ # optional:true keeps the webhook starting before the 1P secret
+ # exists; the header is simply omitted when the value is empty.
+ - name: FlowerCore__Dns__AcmeWebhook__ApiKey
+ valueFrom:
+ secretKeyRef:
+ name: dns-api-keys
+ key: api_key
+ optional: true
- name: FlowerCore__Dns__AcmeWebhook__GroupName
value: acme.flowercore.io
- name: FlowerCore__Dns__AcmeWebhook__SolverName
diff --git a/apps/fc-landing/fc-landing.yaml b/apps/fc-landing/fc-landing.yaml
index f6d2c34..f8eb785 100644
--- a/apps/fc-landing/fc-landing.yaml
+++ b/apps/fc-landing/fc-landing.yaml
@@ -9,7 +9,7 @@ metadata:
labels:
app.kubernetes.io/part-of: bluejay-infra
---
-# Landing page HTML (public-safe - no internal LAN references)
+# Landing page HTML (public-safe - no internal LAN references, no CDN)
apiVersion: v1
kind: ConfigMap
metadata:
@@ -20,144 +20,357 @@ data:
-
-
- FlowerCore
-
+
+
+ FlowerCore
+
+
-
-
🌻
-
FlowerCore
-
Blue Jay Lab
-
- Multi-tenant service management platform built on .NET 10,
- Kubernetes, and GitOps. Digital signage, telephony IVR,
- MySQL/PHP hosting, and infrastructure automation.
-
+
Skip to content
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+ FlowerCore
+ More to come.
+
+
+
+
+
+
+
+
+ Update Center
+
+
+ Software updates & releases
+
+
+
---
diff --git a/apps/fc-landing/preview.html b/apps/fc-landing/preview.html
new file mode 100644
index 0000000..e1dea4d
--- /dev/null
+++ b/apps/fc-landing/preview.html
@@ -0,0 +1,356 @@
+
+
+
+
+
+
FlowerCore
+
+
+
+
+
Skip to content
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FlowerCore
+ More to come.
+
+
+
+
+
+
+
+
+ Update Center
+
+
+ Software updates & releases
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/github-runner/Dockerfile.arm64 b/apps/github-runner/Dockerfile.arm64
new file mode 100644
index 0000000..5fb0ab3
--- /dev/null
+++ b/apps/github-runner/Dockerfile.arm64
@@ -0,0 +1,63 @@
+FROM myoung34/github-runner:latest
+
+# arm64 variant of the FlowerCore self-hosted runner image.
+# Built/maintained alongside the amd64 Dockerfile. The ONLY substantive
+# difference from the amd64 image is that the Actions/Ruby tool-cache arch
+# label is "arm64" instead of the amd64 "x64". Ruby itself is still compiled
+# from source via ruby-build, so it is naturally arm64 on an aarch64 host.
+
+ARG RUBY_VERSION=3.3.11
+ARG RUBY_MINOR=3.3
+ARG RUBY_BUILD_VERSION=v20260326
+ARG RUNNER_UID=1001
+ARG RUNNER_GID=1001
+# arm64 tool-cache arch label (was x64 on amd64). ruby/setup-ruby@v1 on a
+# self-hosted aarch64 Linux runner discovers Ruby under _tool/Ruby/
/arm64.
+ARG TOOLCACHE_ARCH=arm64
+
+ENV RUNNER_TOOL_CACHE=/home/runner/_tool
+ENV RUNNER_RUBY_TOOLCACHE=/opt/runner-toolcache
+ENV PATH="/home/runner/_tool/Ruby/${RUBY_MINOR}/${TOOLCACHE_ARCH}/bin:/opt/runner-toolcache/Ruby/${RUBY_MINOR}/${TOOLCACHE_ARCH}/bin:${PATH}"
+
+USER root
+
+# Bake the IAmWorkin step-ca root CA into the system trust store. Without
+# this, .NET HttpClient calls from CI tests against *.iamworkin.lan
+# (e.g. https://selenium.iamworkin.lan/session) fail with `PartialChain`
+# because the runner image's default Ubuntu trust bundle doesn't include
+# our internal Root CA. update-ca-certificates regenerates
+# /etc/ssl/certs/ca-certificates.crt, which OpenSSL + .NET on Linux read
+# automatically — no SSL_CERT_FILE env var needed.
+COPY step-ca-root.crt /usr/local/share/ca-certificates/iamworkin-step-ca-root.crt
+
+RUN apt-get update \
+ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ autoconf \
+ bison \
+ build-essential \
+ ca-certificates \
+ curl \
+ libdb-dev \
+ libffi-dev \
+ libgdbm-dev \
+ libgmp-dev \
+ libncurses-dev \
+ libreadline-dev \
+ libssl-dev \
+ libyaml-dev \
+ patch \
+ pkg-config \
+ uuid-dev \
+ zlib1g-dev \
+ && update-ca-certificates \
+ && curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/${RUBY_BUILD_VERSION}.tar.gz" -o /tmp/ruby-build.tar.gz \
+ && mkdir -p /tmp/ruby-build \
+ && tar -xzf /tmp/ruby-build.tar.gz --strip-components=1 -C /tmp/ruby-build \
+ && /tmp/ruby-build/install.sh \
+ && rm -rf /tmp/ruby-build /tmp/ruby-build.tar.gz /var/lib/apt/lists/*
+
+COPY install-ruby-toolcache.sh /usr/local/bin/install-ruby-toolcache.sh
+
+RUN chmod +x /usr/local/bin/install-ruby-toolcache.sh \
+ && RUBY_VERSION="${RUBY_VERSION}" RUBY_MINOR="${RUBY_MINOR}" TOOLCACHE_ARCH="${TOOLCACHE_ARCH}" TOOLCACHE_ROOT="${RUNNER_RUBY_TOOLCACHE}" RUNNER_UID="${RUNNER_UID}" RUNNER_GID="${RUNNER_GID}" /usr/local/bin/install-ruby-toolcache.sh \
+ && ruby -v
diff --git a/apps/github-runner/install-ruby-toolcache.sh b/apps/github-runner/install-ruby-toolcache.sh
index 544b25a..d753d54 100644
--- a/apps/github-runner/install-ruby-toolcache.sh
+++ b/apps/github-runner/install-ruby-toolcache.sh
@@ -6,12 +6,16 @@ RUBY_MINOR="${RUBY_MINOR:-3.3}"
TOOLCACHE_ROOT="${TOOLCACHE_ROOT:-/opt/runner-toolcache}"
RUNNER_UID="${RUNNER_UID:-1001}"
RUNNER_GID="${RUNNER_GID:-1001}"
-RUBY_PREFIX="${TOOLCACHE_ROOT}/Ruby/${RUBY_VERSION}/x64"
+# Tool-cache arch label. Defaults to x64 (amd64 image, unchanged). The arm64
+# Dockerfile passes TOOLCACHE_ARCH=arm64 so ruby/setup-ruby@v1 finds Ruby on
+# aarch64 self-hosted runners.
+TOOLCACHE_ARCH="${TOOLCACHE_ARCH:-x64}"
+RUBY_PREFIX="${TOOLCACHE_ROOT}/Ruby/${RUBY_VERSION}/${TOOLCACHE_ARCH}"
mkdir -p "${TOOLCACHE_ROOT}/Ruby"
RUBY_CONFIGURE_OPTS="${RUBY_CONFIGURE_OPTS:---disable-install-doc --disable-yjit}" ruby-build "${RUBY_VERSION}" "${RUBY_PREFIX}"
-touch "${TOOLCACHE_ROOT}/Ruby/${RUBY_VERSION}/x64.complete"
+touch "${TOOLCACHE_ROOT}/Ruby/${RUBY_VERSION}/${TOOLCACHE_ARCH}.complete"
ln -sfn "${RUBY_VERSION}" "${TOOLCACHE_ROOT}/Ruby/${RUBY_MINOR}"
"${RUBY_PREFIX}/bin/ruby" -v
diff --git a/docs/gx10-tenant-landing/CUTOVER-RUNBOOK.md b/docs/gx10-tenant-landing/CUTOVER-RUNBOOK.md
new file mode 100644
index 0000000..5c82b16
--- /dev/null
+++ b/docs/gx10-tenant-landing/CUTOVER-RUNBOOK.md
@@ -0,0 +1,83 @@
+# GX10 Tenant Landing-Site Migration — Cutover Runbook
+
+Date: 2026-06-16. Migrates the 5 per-tenant public landing sites from the OLD RKE2
+cluster (`10.0.56.200` Traefik) to the GX10 ARM64 cluster (`10.0.57.202` VIP /
+NodePort `10.0.56.14:32491`).
+
+## Deployed on GX10 (DONE — staged-verified, NOT yet receiving public traffic)
+
+| Domain(s) | GX10 ns | Workload | TLS secret (in ns + traefik-system) | Live content replicated |
+|-----------------------------------|--------------------|---------------|-------------------------------------|-------------------------|
+| bluejay.dev, www.bluejay.dev | `fc-tenant-andrew` | nginx:alpine | `cf-origin-bluejay-dev` | "Blue Jay" (custom) |
+| timeforta.co, www.timeforta.co | `fc-tenant-dustin` | nginx:alpine | `cf-origin-timeforta-co` | "Coming Soon" (generic) |
+| erckak.dev, www.erckak.dev | `fc-tenant-erik` | nginx:alpine | `cf-origin-erckak-dev` | "Erckak" (custom) |
+| flowerinsider.xyz, www.* | `fc-tenant-fit` | nginx:alpine | `cf-origin-flowerinsider-xyz` | "Flower Insider" (custom)|
+| matt.flowercore.io | `fc-tenant-matt` | nginx:alpine | `cf-origin-flowercore-io` | "Coming Soon" (generic) |
+
+All nginx pods 1/1 Running, IngressRoutes priority 100 (override the GX10
+`public-catchall`). Each site replicates EXACTLY what was live on OLD at migration
+time, so cutover is content-invisible.
+
+Staged verification (all HTTP 200, correct content, SNI-correct cert):
+```
+curl -sk --resolve :32491:10.0.56.14 https://:32491/
+```
+
+## Public routing reality (why NO automatic cutover happened)
+
+Every tenant domain enters the network through Cloudflare (proxied) → a dedicated
+pfSense WAN IP in 74.40.140.16/28 → pfSense port-forward. ALL FIVE currently forward
+to OLD Traefik `10.0.56.200:443`:
+
+| Domain | CF origin WAN IP | pfSense rdr today |
+|-------------------|------------------|--------------------|
+| bluejay.dev | 74.40.140.17 | → 10.0.56.200:443 |
+| matt.flowercore.io| 74.40.140.19 | → 10.0.56.200:443 |
+| timeforta.co | 74.40.140.21 | → 10.0.56.200:443 |
+| erckak.dev | 74.40.140.23 | → 10.0.56.200:443 |
+| flowerinsider.xyz | 74.40.140.25 | → 10.0.56.200:443 |
+
+(Contrast: main flowercore.io = WAN `.24` → already GX10 `10.0.56.14:32491`.)
+NOTE: matt.flowercore.io is bound to WAN `.19` (the MATT VPN IP), NOT `.24`, so the
+"*.flowercore.io already NATs to GX10" assumption does NOT cover matt.
+
+Because none of these NAT to GX10 yet, no cutover was performed (live sites untouched).
+
+## OPERATOR ACTION — cutover = repoint the pfSense port-forward target
+
+For each domain, change the HTTPS (and HTTP) port-forward TARGET from
+`10.0.56.200` to `10.0.56.14:32491` (HTTPS) / `10.0.56.14:30776` (HTTP). pfSense
+port-forwards (Firewall → NAT → Port Forward), edit these rule descriptions:
+
+- `ANDREW: HTTPS to Traefik` 74.40.140.17:443 → change target `10.0.56.200:443` to `10.0.56.14:32491`
+- `MATT: HTTPS to Traefik` 74.40.140.19:443 → change target `10.0.56.200:443` to `10.0.56.14:32491`
+- `DUSTIN: HTTPS to Traefik` 74.40.140.21:443 → change target `10.0.56.200:443` to `10.0.56.14:32491`
+- `ERIK: HTTPS to Traefik` 74.40.140.23:443 → change target `10.0.56.200:443` to `10.0.56.14:32491`
+- `FIT: HTTPS to Traefik` 74.40.140.25:443 → change target `10.0.56.200:443` to `10.0.56.14:32491`
+- (corresponding `:80 → 10.0.56.14:30776` HTTP rules likewise, optional — sites are HTTPS-only)
+
+No Cloudflare DNS change is required: the WAN IPs stay the same, only the internal
+NAT target moves. Each can be flipped independently (per-tenant blast radius).
+
+Post-flip verify (external):
+```
+curl -sI https:/// # expect HTTP 200, Server: cloudflare, unchanged content
+```
+
+## Rollback
+
+OLD cluster left fully intact (ArgoCD apps infra-andrew/dustin/erik/fit Synced+Healthy,
+pods Running). To roll back any domain: revert that pfSense port-forward target to
+`10.0.56.200`.
+
+## Notes
+- The OLD cluster has DUPLICATE namespaces per tenant (`tenant-X` custom page +
+ `fc-tenant-X` generic landing), both with IngressRoutes claiming the same host.
+ Traefik non-deterministically picked a winner; live content was: andrew/erik/fit =
+ custom (`tenant-X`), dustin/matt = generic (`fc-tenant-X`). GX10 consolidates to ONE
+ namespace per tenant (`fc-tenant-X`) serving the content that was actually live.
+- `infra-worldbuilder` (worldbuilder.iamworkin.lan, internal .NET app) was ALREADY
+ migrated to GX10 (`fc-worldbuilder`, 1/1 Running) — no action.
+- `infra-flowercore` (tenant-flowercore/flowercore-web demo) has NO public route and is
+ superseded by the production `fc-system/fc-landing-public` (flowercore.io root) already
+ live on GX10 — intentionally NOT migrated.
diff --git a/docs/gx10-tenant-landing/andrew-deploy.yaml b/docs/gx10-tenant-landing/andrew-deploy.yaml
new file mode 100644
index 0000000..1380abd
--- /dev/null
+++ b/docs/gx10-tenant-landing/andrew-deploy.yaml
@@ -0,0 +1,225 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: fc-tenant-andrew
+ labels:
+ app.kubernetes.io/part-of: bluejay-infra
+ flowercore.io/tenant: andrew
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: andrew-web-html
+ namespace: fc-tenant-andrew
+data:
+ index.html: |
+
+
+
+
+
+ Blue Jay — bluejay.dev
+
+
+
+
+
+
Andrew's Space
+
🐦
+
Blue Jay
+
bluejay.dev
+
+
+
+
+
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: andrew-web-nginx-conf
+ namespace: fc-tenant-andrew
+data:
+ default.conf: |
+ server {
+ listen 80;
+ server_name _;
+ root /usr/share/nginx/html;
+ index index.html;
+ location / { try_files $uri $uri/ =404; }
+ location /healthz { access_log off; return 200 "ok"; add_header Content-Type text/plain; }
+ }
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: andrew-web
+ namespace: fc-tenant-andrew
+ labels:
+ app: andrew-web
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: andrew-web
+ template:
+ metadata:
+ labels:
+ app: andrew-web
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ name: http
+ volumeMounts:
+ - name: nginx-conf
+ mountPath: /etc/nginx/conf.d/default.conf
+ subPath: default.conf
+ - name: html
+ mountPath: /usr/share/nginx/html
+ resources:
+ requests: { memory: 32Mi, cpu: 10m }
+ limits: { memory: 64Mi, cpu: 50m }
+ livenessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ readinessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 3
+ periodSeconds: 5
+ volumes:
+ - name: nginx-conf
+ configMap: { name: andrew-web-nginx-conf }
+ - name: html
+ configMap: { name: andrew-web-html }
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: andrew-web
+ namespace: fc-tenant-andrew
+spec:
+ selector:
+ app: andrew-web
+ ports:
+ - port: 80
+ targetPort: 80
+ name: http
diff --git a/docs/gx10-tenant-landing/andrew-ingressroute.yaml b/docs/gx10-tenant-landing/andrew-ingressroute.yaml
new file mode 100644
index 0000000..1c24197
--- /dev/null
+++ b/docs/gx10-tenant-landing/andrew-ingressroute.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: andrew-web
+ namespace: fc-tenant-andrew
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`bluejay.dev`) || Host(`www.bluejay.dev`)
+ kind: Rule
+ priority: 100
+ services:
+ - name: andrew-web
+ port: 80
+ tls:
+ secretName: cf-origin-bluejay-dev
diff --git a/docs/gx10-tenant-landing/dustin-deploy.yaml b/docs/gx10-tenant-landing/dustin-deploy.yaml
new file mode 100644
index 0000000..39c1dc4
--- /dev/null
+++ b/docs/gx10-tenant-landing/dustin-deploy.yaml
@@ -0,0 +1,208 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: fc-tenant-dustin
+ labels:
+ app.kubernetes.io/part-of: bluejay-infra
+ flowercore.io/tenant: dustin
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: dustin-web-html
+ namespace: fc-tenant-dustin
+data:
+ index.html: |
+
+
+
+
+
+ timeforta.co — Coming Soon
+
+
+
+
+
🌮
+
timeforta.co
+
Dustin
+
+
It's always time for tacos.
+
+
+ Under Construction
+
+
+
+ Powered by FlowerCore • Hosted on Blue Jay Infrastructure
+
+
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: dustin-web-nginx-conf
+ namespace: fc-tenant-dustin
+data:
+ default.conf: |
+ server {
+ listen 80;
+ server_name _;
+ root /usr/share/nginx/html;
+ index index.html;
+ location / { try_files $uri $uri/ =404; }
+ location /healthz { access_log off; return 200 "ok"; add_header Content-Type text/plain; }
+ }
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: dustin-web
+ namespace: fc-tenant-dustin
+ labels:
+ app: dustin-web
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: dustin-web
+ template:
+ metadata:
+ labels:
+ app: dustin-web
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ name: http
+ volumeMounts:
+ - name: nginx-conf
+ mountPath: /etc/nginx/conf.d/default.conf
+ subPath: default.conf
+ - name: html
+ mountPath: /usr/share/nginx/html
+ resources:
+ requests: { memory: 32Mi, cpu: 10m }
+ limits: { memory: 64Mi, cpu: 50m }
+ livenessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ readinessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 3
+ periodSeconds: 5
+ volumes:
+ - name: nginx-conf
+ configMap: { name: dustin-web-nginx-conf }
+ - name: html
+ configMap: { name: dustin-web-html }
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: dustin-web
+ namespace: fc-tenant-dustin
+spec:
+ selector:
+ app: dustin-web
+ ports:
+ - port: 80
+ targetPort: 80
+ name: http
diff --git a/docs/gx10-tenant-landing/dustin-ingressroute.yaml b/docs/gx10-tenant-landing/dustin-ingressroute.yaml
new file mode 100644
index 0000000..03a18f4
--- /dev/null
+++ b/docs/gx10-tenant-landing/dustin-ingressroute.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: dustin-web
+ namespace: fc-tenant-dustin
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`timeforta.co`) || Host(`www.timeforta.co`)
+ kind: Rule
+ priority: 100
+ services:
+ - name: dustin-web
+ port: 80
+ tls:
+ secretName: cf-origin-timeforta-co
diff --git a/docs/gx10-tenant-landing/erik-deploy.yaml b/docs/gx10-tenant-landing/erik-deploy.yaml
new file mode 100644
index 0000000..fcf3164
--- /dev/null
+++ b/docs/gx10-tenant-landing/erik-deploy.yaml
@@ -0,0 +1,225 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: fc-tenant-erik
+ labels:
+ app.kubernetes.io/part-of: bluejay-infra
+ flowercore.io/tenant: erik
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: erik-web-html
+ namespace: fc-tenant-erik
+data:
+ index.html: |
+
+
+
+
+
+ Erckak — erckak.dev
+
+
+
+
+
+
Erik's Space
+
🚀
+
Erckak
+
erckak.dev
+
+
+
+
+
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: erik-web-nginx-conf
+ namespace: fc-tenant-erik
+data:
+ default.conf: |
+ server {
+ listen 80;
+ server_name _;
+ root /usr/share/nginx/html;
+ index index.html;
+ location / { try_files $uri $uri/ =404; }
+ location /healthz { access_log off; return 200 "ok"; add_header Content-Type text/plain; }
+ }
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: erik-web
+ namespace: fc-tenant-erik
+ labels:
+ app: erik-web
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: erik-web
+ template:
+ metadata:
+ labels:
+ app: erik-web
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ name: http
+ volumeMounts:
+ - name: nginx-conf
+ mountPath: /etc/nginx/conf.d/default.conf
+ subPath: default.conf
+ - name: html
+ mountPath: /usr/share/nginx/html
+ resources:
+ requests: { memory: 32Mi, cpu: 10m }
+ limits: { memory: 64Mi, cpu: 50m }
+ livenessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ readinessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 3
+ periodSeconds: 5
+ volumes:
+ - name: nginx-conf
+ configMap: { name: erik-web-nginx-conf }
+ - name: html
+ configMap: { name: erik-web-html }
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: erik-web
+ namespace: fc-tenant-erik
+spec:
+ selector:
+ app: erik-web
+ ports:
+ - port: 80
+ targetPort: 80
+ name: http
diff --git a/docs/gx10-tenant-landing/erik-ingressroute.yaml b/docs/gx10-tenant-landing/erik-ingressroute.yaml
new file mode 100644
index 0000000..397465f
--- /dev/null
+++ b/docs/gx10-tenant-landing/erik-ingressroute.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: erik-web
+ namespace: fc-tenant-erik
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`erckak.dev`) || Host(`www.erckak.dev`)
+ kind: Rule
+ priority: 100
+ services:
+ - name: erik-web
+ port: 80
+ tls:
+ secretName: cf-origin-erckak-dev
diff --git a/docs/gx10-tenant-landing/fit-deploy.yaml b/docs/gx10-tenant-landing/fit-deploy.yaml
new file mode 100644
index 0000000..1fabaac
--- /dev/null
+++ b/docs/gx10-tenant-landing/fit-deploy.yaml
@@ -0,0 +1,225 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: fc-tenant-fit
+ labels:
+ app.kubernetes.io/part-of: bluejay-infra
+ flowercore.io/tenant: fit
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: fit-web-html
+ namespace: fc-tenant-fit
+data:
+ index.html: |
+
+
+
+
+
+ Flower Insider — flowerinsider.xyz
+
+
+
+
+
+
Flower Insider Team
+
🌸
+
Flower Insider
+
flowerinsider.xyz
+
+
+
+
+
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: fit-web-nginx-conf
+ namespace: fc-tenant-fit
+data:
+ default.conf: |
+ server {
+ listen 80;
+ server_name _;
+ root /usr/share/nginx/html;
+ index index.html;
+ location / { try_files $uri $uri/ =404; }
+ location /healthz { access_log off; return 200 "ok"; add_header Content-Type text/plain; }
+ }
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: fit-web
+ namespace: fc-tenant-fit
+ labels:
+ app: fit-web
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: fit-web
+ template:
+ metadata:
+ labels:
+ app: fit-web
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ name: http
+ volumeMounts:
+ - name: nginx-conf
+ mountPath: /etc/nginx/conf.d/default.conf
+ subPath: default.conf
+ - name: html
+ mountPath: /usr/share/nginx/html
+ resources:
+ requests: { memory: 32Mi, cpu: 10m }
+ limits: { memory: 64Mi, cpu: 50m }
+ livenessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ readinessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 3
+ periodSeconds: 5
+ volumes:
+ - name: nginx-conf
+ configMap: { name: fit-web-nginx-conf }
+ - name: html
+ configMap: { name: fit-web-html }
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: fit-web
+ namespace: fc-tenant-fit
+spec:
+ selector:
+ app: fit-web
+ ports:
+ - port: 80
+ targetPort: 80
+ name: http
diff --git a/docs/gx10-tenant-landing/fit-ingressroute.yaml b/docs/gx10-tenant-landing/fit-ingressroute.yaml
new file mode 100644
index 0000000..4b9007c
--- /dev/null
+++ b/docs/gx10-tenant-landing/fit-ingressroute.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: fit-web
+ namespace: fc-tenant-fit
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`flowerinsider.xyz`) || Host(`www.flowerinsider.xyz`)
+ kind: Rule
+ priority: 100
+ services:
+ - name: fit-web
+ port: 80
+ tls:
+ secretName: cf-origin-flowerinsider-xyz
diff --git a/docs/gx10-tenant-landing/matt-deploy.yaml b/docs/gx10-tenant-landing/matt-deploy.yaml
new file mode 100644
index 0000000..b5dfd58
--- /dev/null
+++ b/docs/gx10-tenant-landing/matt-deploy.yaml
@@ -0,0 +1,208 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: fc-tenant-matt
+ labels:
+ app.kubernetes.io/part-of: bluejay-infra
+ flowercore.io/tenant: matt
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: matt-web-html
+ namespace: fc-tenant-matt
+data:
+ index.html: |
+
+
+
+
+
+ matt.flowercore.io — Coming Soon
+
+
+
+
+
🔥
+
matt.flowercore.io
+
Matt
+
+
Building something extraordinary.
+
+
+ Under Construction
+
+
+
+ Powered by FlowerCore • Hosted on Blue Jay Infrastructure
+
+
+
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: matt-web-nginx-conf
+ namespace: fc-tenant-matt
+data:
+ default.conf: |
+ server {
+ listen 80;
+ server_name _;
+ root /usr/share/nginx/html;
+ index index.html;
+ location / { try_files $uri $uri/ =404; }
+ location /healthz { access_log off; return 200 "ok"; add_header Content-Type text/plain; }
+ }
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: matt-web
+ namespace: fc-tenant-matt
+ labels:
+ app: matt-web
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: matt-web
+ template:
+ metadata:
+ labels:
+ app: matt-web
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:alpine
+ ports:
+ - containerPort: 80
+ name: http
+ volumeMounts:
+ - name: nginx-conf
+ mountPath: /etc/nginx/conf.d/default.conf
+ subPath: default.conf
+ - name: html
+ mountPath: /usr/share/nginx/html
+ resources:
+ requests: { memory: 32Mi, cpu: 10m }
+ limits: { memory: 64Mi, cpu: 50m }
+ livenessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ readinessProbe:
+ httpGet: { path: /healthz, port: 80 }
+ initialDelaySeconds: 3
+ periodSeconds: 5
+ volumes:
+ - name: nginx-conf
+ configMap: { name: matt-web-nginx-conf }
+ - name: html
+ configMap: { name: matt-web-html }
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: matt-web
+ namespace: fc-tenant-matt
+spec:
+ selector:
+ app: matt-web
+ ports:
+ - port: 80
+ targetPort: 80
+ name: http
diff --git a/docs/gx10-tenant-landing/matt-ingressroute.yaml b/docs/gx10-tenant-landing/matt-ingressroute.yaml
new file mode 100644
index 0000000..ee70091
--- /dev/null
+++ b/docs/gx10-tenant-landing/matt-ingressroute.yaml
@@ -0,0 +1,18 @@
+---
+apiVersion: traefik.io/v1alpha1
+kind: IngressRoute
+metadata:
+ name: matt-web
+ namespace: fc-tenant-matt
+spec:
+ entryPoints:
+ - websecure
+ routes:
+ - match: Host(`matt.flowercore.io`)
+ kind: Rule
+ priority: 100
+ services:
+ - name: matt-web
+ port: 80
+ tls:
+ secretName: cf-origin-flowercore-io