feat(pm-agent): pin the first published agent-runner digest, start dry, and fix the token scopes
gate / gate (pull_request) Failing after 2m56s
images / images (pull_request) Successful in 32s
gate / conformance (pull_request) Has been skipped

Rebased #61 onto the CI image pipeline (#81). This commit makes the remaining in-git
blockers to going live go away:

- deployment.yaml and PM_WORKER_IMAGE pin images run 340's digest (sha256:c4eba5b3...,
  tag sha-523cd9118f95 = main), read from the registry. Renovate markers unchanged.
- PM_DRY_RUN=true and PM_REQUIRE_LABEL=agent for the first run. Dry run is required, not
  just cautious: the run-340 image's baked worker template predates imagePullSecrets, so
  admission would deny its Jobs until CI re-publishes after this merge and Renovate bumps.
- Token scopes: Gitea 1.24.3 scopes GET /api/v1/user (the dispatcher's first call) under
  read:user. The documented list was missing it. Now read:user, write:repository,
  write:issue, read:package everywhere.
- Org-package read documented as a team (Packages=Read, no repos), not just a scope.
- sops is toolbox-only; the .examples say so and give the in-place edit.
- runbook: key-name check no longer emits Secret values before the pipe.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
jakman85andClaude Opus 5.5 committed 2026-10-30 01:16:21 -05:00
1 parent a7eadd4054
commit 7f0cbc7ba2
8 files changed
+219 -29

No files matched your search

@@ -34,12 +34,31 @@
# package and nothing else.
#
# (2) ADD THE PACKAGE SCOPE. Gitea token scopes are fixed at creation, so this means
# generating a token with all three scopes and using it in BOTH Secrets:
# 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
@@ -64,6 +83,17 @@
#
# (4) COPY, FILL, ENCRYPT, COMMIT:
#
# `sops` IS IN THE PINNED TOOLBOX, NOT ON THE HOST (measured 2026-10-30: absent from the
# workstation's PATH; sops 3.9.4 and vi in the toolbox). The simplest path needs no
# plaintext file at all — edit the committed, already-encrypted file in place, from a
# worktree, the way runbooks/vpn-remote-access.md does for its key:
#
# ./infra/devcontainer/dev.sh run sops platform/pm-agent/pm-agent-registry.sops.yaml
#
# It opens vi on the decrypted document; replace the REPLACE-ME value(s), save, quit, and
# sops re-encrypts on write. If you take the cp route below instead, run the `sops -e -i`
# line through `./infra/devcontainer/dev.sh run` as well, and delete any plaintext temp file.
#
# cp platform/pm-agent/pm-agent-registry.sops.yaml.example \
# platform/pm-agent/pm-agent-registry.sops.yaml
# # paste the one-line JSON from step 3 as the .dockerconfigjson value