# ADR-0008: Private package, External-Secrets pull over public HTTPS (no k3s restart) **Status:** Accepted — 2026-07-17 · **Supersedes** the registry-visibility, image-pull, CI-auth, and pipeline specifics of [ADR-0007](0007-gitea-actions-and-packages.md). (ADR-0007's core — CI on Gitea Actions, images in Gitea Packages — still stands; only the details below changed once we actually built and debugged it.) ## Context ADR-0007 proposed a **public** package pulled by k3s from the internal HTTP registry (`10.66.15.22:3000`), which required `registries.yaml` on every node + a **k3s restart**, and CI auth via the runner's automatic token. In practice: - The automatic Gitea Actions token **lacked reliable package-write scope** → `docker login` returned `unauthorized`. - k3s trusting an HTTP registry means a **rolling cluster restart** — unacceptable disruption for a static site, and the user wanted zero risk to the (still-live) old site and other workloads. - The user chose a **private** package (least exposure) managed via the existing **External Secrets** + OpenBao stack. - Testing showed a cluster pod **can reach the registry over public HTTPS** (`git.7tl-homelab.com`, valid cert) — so no node-level registry config is needed at all. ## Decision - **Package is PRIVATE.** (Gitea user packages are private by default; no public/anon pull.) - **Split push/pull, same Gitea package, two access paths:** - **CI pushes** to the **internal** endpoint `10.66.15.22:3000/bmr_bluecap/bcs-website` (HTTP, fast, no edge). The runner host's dockerd trusts it via `insecure-registries` (managed in the gitea Ansible role + SIGHUP live-reload — no container restart). - **k3s pulls** the **public HTTPS** endpoint `git.7tl-homelab.com/bmr_bluecap/bcs-website` (valid TLS) → **no `registries.yaml`, no node insecure config, no k3s restart.** - `.gitea/workflows/ci.yml` uses `PUSH_IMAGE` (internal) and `DEPLOY_IMAGE` (public); the tag bump / k8s image ref use `DEPLOY_IMAGE`. - **Pull auth via External Secrets:** `k8s/externalsecret-registry.yaml` (ClusterSecretStore `openbao-cluster`, OpenBao KV v2 mount `cluster`, path `bluecap-registry` → fields `username`/`password`) materializes a `kubernetes.io/dockerconfigjson` secret `gitea-registry`; the Deployment references it via `imagePullSecrets`. No static secret in Git. - **CI auth via a dedicated PAT** `CI_TOKEN` (repo Actions secret; scopes `write:package` + `write:repository`) for both the registry push and the tag-bump commit-back — not the auto token. - **Playwright e2e removed from CI** (heaviest step, headless Chrome, unneeded for a static site). CI runs `npm ci` → build (typecheck) → `seo:lint`. Run e2e locally: `npm run test:e2e`. ## Consequences - **No cluster restart, no per-node registry config** — deploys never disturb k3s. - Two tokens by role (least privilege): `CI_TOKEN` (write) as a repo secret; a separate `read:package` PAT in OpenBao for cluster pulls. - The internal-push insecure-registry trust is the one host-level bit (gitea role), needed only on the runner host, applied by live-reload. - Verified 2026-07-17: full loop green, 2/2 pods pulling the private image over HTTPS and serving.