# 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`](../../../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: ```bash 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 certificate - `private-key.pem` — ECDSA P-256 private key - `chain.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. ```bash # 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: ```bash 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 ```bash 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.