# ============================================================================= # TEMPLATE — NOT APPLIED, NOT ENCRYPTED (`.yaml.example` is ignored by kustomize and is # never auto-encrypted by SOPS). Reference scaffold + OPERATOR HAND-OFF. # # WHAT THIS IS: the credential the KUBELET uses to pull `agent-runner` from the in-cluster # Gitea registry. It is NOT the credential the agent uses to talk to Gitea — that one is # `pm-agent-credentials` and it is read by the application. Two Secrets, because they are # consumed by two different things in two different formats: # # pm-agent-credentials Opaque read by pm-dispatch / pm-worker as env vars # pm-agent-registry dockerconfigjson read by the KUBELET, before any container runs # # WHY A PULL CREDENTIAL IS NEEDED AT ALL. Measured 2026-10-05: an unauthenticated client # gets nothing from this Gitea's registry — it issues an anonymous token whose `access` # claim is `null`, and every path answers 401 whether it exists or not # (`/api/v1/packages/Home-Network` replies `{"message":"token is required"}`). So a pull # with no credential cannot succeed, and before this Secret there was no `imagePullSecret` # anywhere in the tree. See decisions/2026-10-05-pm-agent-registry-pull-prereq.md. # # THE OTHER HALF, ALREADY FIXED: the nodes used to resolve this registry's hostname to the # non-hairpinned WAN address and fail TLS against the router's `CN=%D` placeholder # certificate. That is the `/etc/hosts` pin in # infra/ansible/inventory/group_vars/k3s_prod/vars.yml # (decisions/2026-10-06-pm-agent-node-resolves-gitea-to-wan.md). Both halves are needed; a # pull fails on whichever is missing. # # ============================================================================= # HAND-OFF STEPS (operator) # ============================================================================= # # (1) USE THE SAME `pm-agent` ACCOUNT the agent already has (created per # pm-agent-credentials.sops.yaml.example — the dedicated, NON-ADMIN one). A second # account would be a second thing to keep non-admin for no gain: this token reads one # package and nothing else. # # (2) ADD THE PACKAGE SCOPE. Gitea token scopes are fixed at creation, so this means # generating a token with all four scopes and using it in BOTH Secrets: # # read:package <- NEW, what the kubelet needs to pull # read:user <- the dispatcher's startup GET /user (Gitea 1.24.3 scopes it) # write:repository <- the agent's clone / branch push / pull-request create # write:issue <- the agent's issue comments # # THE SCOPE IS NOT THE PERMISSION. `read:package` only lets the token use whatever # package access the ACCOUNT has, and these packages belong to the ORGANISATION # (`home-network/…`), not to a repository. Gitea 1.24.3 decides org-package access in # services/context/package.go `determineAccessMode`: the highest Packages-unit mode of # any org team the user is in, else READ if the org is visible to that user. Home-Network # is `public` today (measured 2026-10-30), so a signed-in non-member already gets read — # but that is a side effect of a visibility setting nobody chose for this. Make it # explicit, and independent of visibility: # # Organisation Home-Network -> Teams -> New Team # name: pm-agent-pull permission: none (unit-level) # repositories: NONE (do not tick "all repositories") # units: Packages = Read; every other unit = No Access # then add `pm-agent` as its only member. # # That grants package read on the org and nothing on any repository; the repo `Write` # comes only from the collaborator entry in the credentials template. # # Gitea -> (as pm-agent) Settings -> Applications -> Generate New Token. # Put the SAME value in `pm-agent-credentials` (`gitea-token`) and in the # `.dockerconfigjson` below. One token, two consumers, two formats — the same # one-value-in-two-files pattern the Keycloak client secret already uses here, and for # the same reason: a Secret cannot be shared across formats. # # (3) BUILD THE DOCKERCONFIGJSON. `auth` is base64 of `:` — NOT a hash, # just encoding, so treat this file exactly as secret as the token itself: # # USER=pm-agent # TOKEN='' # AUTH=$(printf '%s:%s' "$USER" "$TOKEN" | base64 -w0) # cat > /tmp/dockerconfig.json <