Adds apps/fc-distribution/{fc-distribution.yaml,kustomization.yaml,README.md}.
Ships the FlowerCore.Distribution service (Blazor + REST + MCP) backed by
Synology NFS for SQLite catalog + content-addressed blob root.
Contents:
- Namespace fc-distribution
- 3x OnePasswordItem (FlowerCore Code Signing CA informational + per-edition
signing keys for kiosk-standard and aistation-field)
- Deployment: localhost/fc-distribution:v202604232000 (already imported to
rke2-server via ctr), pinned to rke2-server nodeSelector because Synology
NFS ACL restricts writes to that node, emptyDir for /tmp + /app/logs,
inline NFS for /data (subPath distribution/data) and /blobs (subPath
distribution/blobs), Secret volume mounts for /signing/<edition>.
readOnlyRootFilesystem + runAsUser 1654 + drop ALL capabilities.
Probes: startup + readiness on /healthz, liveness on tcpSocket (defense
against future auth middleware accidentally gating /healthz).
- Service (ClusterIP :80 -> container :8080)
- Certificate (cert-manager ClusterIssuer step-ca-acme, dist.iamworkin.lan,
90d / 30d renew). pfSense Unbound override dist.iamworkin.lan ->
10.0.56.200 already in place (req'd for HTTP-01).
- IngressRoute (Traefik websecure, Host rule on dist.iamworkin.lan)
Env var keys align with the scaffold:
FlowerCore__Database__ConnectionStrings__Sqlite
FlowerCore__Distribution__Blobs__Root
FlowerCore__Distribution__Signing__EditionCerts__<slug>__{CertPath,KeyPath}
Consumer: ProvisioningAgent (USB-side, Phase 2) — see
FlowerCore.Notes/docs/infrastructure/usb-provisioning-architecture.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.7 KiB
fc-distribution — staged deployment (Phase 1, USB provisioning)
Status: manifests staged, NOT YET APPLIED. Image must be built +
imported and signing 1Password items confirmed before git push.
- Architecture:
../../../FlowerCore.Notes/docs/infrastructure/usb-provisioning-architecture.md - Repo:
D:\git\FlowerCore\FlowerCore.Distribution\(README.md,CLAUDE.md) - Shared lib:
FlowerCore.Common->FlowerCore.Shared.Distribution
FlowerCore.Distribution publishes signed edition manifests (ECDSA P-256
over canonical JSON) and serves the SHA-256 content-addressed blob store
that USB builders pull from. The verifier embeds the IAmWorkin ACME CA Root CA as the trust anchor; per-edition leaf signing material lives in
1Password and is mounted into the pod read-only.
Deployment order (do NOT skip / reorder)
1. pfSense Unbound DNS — DONE 2026-04-23
dist.iamworkin.lan -> 10.0.56.200 was added to pfSense Unbound out of band.
Verify before push:
nslookup dist.iamworkin.lan 10.0.56.1 # expect 10.0.56.200
python bluejay-infra/scripts/check-pfsense-dns.py
If this is missing, cert-manager HTTP-01 will silently back off ~2h. See
memory feedback_pfsense_dns_required_for_acme.md.
2. 1Password items required in vault IAmWorkin
| Item title | Item id | Used as |
|---|---|---|
FlowerCore Code Signing CA |
(existing) | Informational handle only — root CA is baked into the image at build time, not mounted |
FlowerCore Edition Signing Key - edition:kiosk-standard |
3hf33egdvnni6jyuws3r737mqe |
Mounted at /signing/kiosk-standard/ |
FlowerCore Edition Signing Key - edition:aistation-field |
ccxrtsan5samfq4pfuczymacrq |
Mounted at /signing/aistation-field/ |
Each edition item must publish three field labels (the operator turns field labels into Secret keys verbatim):
certificate.pem— leaf certificateprivate-key.pem— ECDSA P-256 private keychain.pem— leaf + intermediate (referenced by the env var as the cert-path; the verifier uses this for signature path validation)
3. Build + import the image to rke2-server
The Pod is pinned to rke2-server because the Synology NFS export
/volume1/kubernetes only allows that node. Importing to the agents is
optional until the ACL is widened.
# From BLUEJAY-WS, in D:\git\FlowerCore\FlowerCore.Distribution
TAG="v$(date +%Y%m%d%H%M)"
dotnet.exe publish -c Release -o deploy/app \
src/FlowerCore.Distribution.Web/FlowerCore.Distribution.Web.csproj
podman build -t localhost/fc-distribution:$TAG -f deploy/Dockerfile.deploy deploy
podman save localhost/fc-distribution:$TAG -o /tmp/fc-distribution.tar
scp /tmp/fc-distribution.tar rke2-server:/tmp/
ssh rke2-server "sudo /var/lib/rancher/rke2/bin/ctr -a /run/k3s/containerd/containerd.sock -n k8s.io images import /tmp/fc-distribution.tar"
4. Bump the image tag + push
Edit fc-distribution.yaml, replace localhost/fc-distribution:v202604231530
with the tag from step 3, then:
cd D:/git/FlowerCore/bluejay-infra
python scripts/check-pfsense-dns.py
git add apps/fc-distribution/
git commit -m "feat(fc-distribution): deploy Phase 1 manifest publisher"
git push
ArgoCD picks up within ~3 minutes and creates infra-fc-distribution.
5. Verify
fcadmin_ssh noc1 '
kubectl -n argocd get application infra-fc-distribution
kubectl -n fc-distribution get certificate,pod,secret
curl -sk -m 8 -o /dev/null -w "HTTP %{http_code}\n" https://dist.iamworkin.lan/healthz
'
Expect: Certificate Ready: True within ~60s, /healthz HTTP 200, both
edition-kiosk-standard and edition-aistation-field Secrets present
with certificate.pem, private-key.pem, chain.pem keys.