Deploys the FuzeFront backend + frontend into the same local kind cluster as
FuzeInfra, wired to FuzeInfra's shared Postgres/Redis. Local JWT auth only;
Authentik (OIDC) and Permit (PDP) are added in a later overlay.
browser ──http──▶ ingress-nginx (host :80)
└─▶ fuzefront-frontend (nginx :8080)
├─ serves the React host shell (Module Federation container)
└─ proxies /api and /socket.io ─▶ fuzefront-backend (:3001)
└─▶ postgres.fuzeinfra.svc / redis.fuzeinfra.svc
winget install Kubernetes.kind RedHat.Helm # kind + helm (kubectl/docker already present)
cd FuzeInfra
make kind-up # creates kind cluster "fuzeinfra" + ingress-nginx + deploys the infra chart
kubectl -n fuzeinfra get pods # wait until postgres/redis are Running
This creates kind cluster fuzeinfra (kube-context kind-fuzeinfra) with host
ports 80/443 mapped to the ingress controller.
kind can't pull fuzefront/*:local from a registry, so build locally and load:
# from the repo root (D:\source\FuzeFront)
docker build -t fuzefront/backend:local ./backend
docker build -t fuzefront/frontend:local \
--build-arg VITE_API_URL=http://fuzefront.dev.local ./frontend
kind load docker-image fuzefront/backend:local fuzefront/frontend:local --name fuzeinfra
The
VITE_API_URLbuild-arg is baked into the frontend bundle, so the browser callshttp://fuzefront.dev.local/api/..., which the ingress routes to the frontend nginx, which proxies to the backend.
helm upgrade --install fuzefront deploy/helm/fuzefront \
-n fuzefront --create-namespace \
-f deploy/helm/fuzefront/values-local.yaml
For real secrets (instead of the dev placeholders), either:
helm upgrade --install fuzefront deploy/helm/fuzefront -n fuzefront --create-namespace \
-f deploy/helm/fuzefront/values-local.yaml \
--set secret.jwtSecret="$JWT" --set secret.sessionSecret="$SESSION" \
--set secret.dbPassword="$DBPASS"
…or create a Secret yourself and set secret.existingSecret=fuzefront-secrets.
Add to your hosts file (C:\Windows\System32\drivers\etc\hosts):
127.0.0.1 fuzefront.dev.local
kubectl -n fuzefront get pods,svc,ingress
curl http://fuzefront.dev.local/api/health # {"status":"ok",...}
# open http://fuzefront.dev.local
curl -X POST http://fuzefront.dev.local/api/apps/register \
-H 'Content-Type: application/json' \
-d '{"name":"My App","url":"http://my-app:8080",
"integrationType":"module-federation",
"remoteUrl":"http://my-app:8080/remoteEntry.js",
"scope":"myApp","module":"./App"}'
The app appears in the 9-dots launcher and is loaded via Module Federation at runtime — no rebuild of the host.
- DB bootstrap vs. runtime (least-privilege): a privileged Helm
pre-install,pre-upgradeJob (templates/db-bootstrap-job.yaml, runningnode dist/scripts/db-bootstrap.js) connects as the FuzeInfra Postgres superuser (fuzeinfra) and idempotently creates thefuzefront_platformDB, a least-privilegefuzefront_userrole (no CREATEDB/CREATEROLE), and grants it ownership of thepublicschema. The backend then runs asfuzefront_userand only verifies the DB exists — it never creates databases or roles. Superuser creds come from a Secret in this namespace: by defaultDB_SUPERUSER_PASSWORDin the chart Secret (setsecret.dbSuperuserPassword, or seal it in prod), or pointdatabase.bootstrap.superuser.secretNameat an existing Secret. The runtime role's password isDB_PASSWORD(secret.dbPassword). - No seed data:
NODE_ENV=production(required so the image's compiled.jsmigrations are found) skips seeds. The platform starts empty; apps self-register at runtime. Run seeds as a one-off Job if you want the demo apps. - Auth/AuthZ: Authentik + Permit PDP are intentionally out of this first cut.
- Validate the chart before deploying:
helm lint deploy/helm/fuzefrontandhelm template fuzefront deploy/helm/fuzefront -f deploy/helm/fuzefront/values-local.yaml.
Self-hosted Unleash (OSS feature-flag server) runs inside this umbrella chart,
gated by unleash.enabled (default false). It is an external upstream image
(unleashorg/unleash-server, pinned to 5.12.0) — it is NOT built by release.yml
and NOT in the CI build matrix, so its tag is bumped deliberately in a deploy window,
never auto-bumped by CI. There is no separate Argo Application: like billing, the
templates live in this umbrella chart and a second Argo app on the same chart path
would double-claim the Deployment.
When unleash.enabled=true the chart renders, in order:
unleash-db-bootstrapJob (pre-install,pre-upgrade, hook-weight-4): connects as the FuzeInfra Postgres superuser and idempotently creates a least-privilegeunleash_svcLOGIN role and a dedicatedunleashdatabase thatunleash_svcOWNS. Unleash self-migrates its own schema on boot asunleash_svc(that is why the role must own the database).CREATE DATABASEis guarded with theSELECT … WHERE NOT EXISTS … \gexecidiom (it can't run in a DO block / txn), and the role password is round-tripped through a session GUC (psql:vardoesn't interpolate inside dollar-quoted blocks) — same pattern as billing.fuzefront-unleashDeployment + Service (unleashorg/unleash-server, port4242,/healthprobes). The Service is cluster-internal only.
Unleash reads three keys from the unleash-secrets SealedSecret (placeholders in
deploy/contabo/sealed/unleash-secrets.yaml — they will NOT decrypt; seal the real
values before enabling):
| Key | Used for |
|---|---|
UNLEASH_DB_PASSWORD |
password for the unleash_svc role (set by the bootstrap Job; used in DATABASE_URL) |
INIT_ADMIN_API_TOKENS |
admin/bootstrap API token, format *:*.<hex> |
UNLEASH_CLIENT_TOKEN |
client/SDK token (format [project]:[env].<hex>), fed to the server as INIT_CLIENT_API_TOKENS — this is the token the @fuzefront/feature-flags package authenticates with |
Seal each (offline, credential-free — FuzeInfra holds the decrypt key):
deploy/scripts/seal-secret.sh UNLEASH_DB_PASSWORD --scope fuzefront/unleash-secrets
deploy/scripts/seal-secret.sh INIT_ADMIN_API_TOKENS --scope fuzefront/unleash-secrets
deploy/scripts/seal-secret.sh UNLEASH_CLIENT_TOKEN --scope fuzefront/unleash-secrets
The @fuzefront/feature-flags package (owned by another agent) connects in-cluster to:
http://fuzefront-unleash.fuzefront.svc.cluster.local:4242/api
authenticated with the UNLEASH_CLIENT_TOKEN (the INIT_CLIENT_API_TOKENS value).
The Unleash admin UI is not exposed via the public app ingress. Two admin-gated options:
- (a) Port-forward (default, no infra needed):
kubectl port-forward svc/fuzefront-unleash 4242:4242, then openhttp://localhost:4242and log in with theINIT_ADMIN_API_TOKENSadmin token. - (b) Optional admin-gated host: set
unleash.ingress.enabled=true+unleash.ingress.host=<host>to render an Ingress for the admin UI. This is off by default and does not make Unleash public on its own: the host MUST sit under the*.prod.fuzefront.comCloudflare-Access zone (admin Zero-Trust). The CF tunnel ingress rule (<host> → traefik.kube-system:80) and the proxied CNAME are FuzeInfra-owned (Terraform) — delegated to FuzeInfra (see the values-prod ingress comment /izzywdev/FuzeInfra#43). The Ingress here only host-matches once FuzeInfra has wired that CF-Access host.