fix(binjovi): grant the legacy read the backup mirror gate needs #588

Merged
binjovi-bot merged 1 commit from fix/push-mirror-legacy-rbac into trunk 2026-09-10 18:06:53 +00:00
Owner

Binjovi could not read forgejopushmirrors, so legacy_absent returned 403 and every codeberg mirror create failed denied before it reached Forgejo.

Measured, not predicted

After v0.1.611 deployed, both bootstrap operations sat at last_error: "denied", attempt 3 of 5:

kubectl auth can-i get forgejopushmirrors  -n forgejo --as=...:binjovi  -> no
kubectl auth can-i get forgejorepositories -n forgejo --as=...:binjovi  -> yes

The Role is resourceNames-scoped per resource, and this resource had no rule at all. ServiceLegacyClient cannot then distinguish an absent controller object from a denied read, which is the whole point of that gate.

This was my omission

The design called this rule mandatory. I scoped RBAC out of the first seanfarm change because neither repository was in the repository bindings yet — which was correct at the time — and then never brought it back when the mirrors landed. The failure is exactly the one that was predicted.

Scope

Only the two repositories binjovi declares. The other 36 mirrors still belong to forgejo-operator, and a name here would let binjovi act on one of them.

Guard

The assertion looks the rule up by resource rather than by index, so reordering the rules cannot hide it, and asserts it is read-only and exactly those two names. Both controls confirmed red before this landed:

  • rule renamed away -> FAIL: no forgejopushmirrors read grant; every mirror create dies denied
  • grant widened to a third repository -> FAIL: ... is not the two binjovi-owned mirrors, read-only

Passing: binjovi-service-handoff, binjovi-repository-bootstrap, binjovi-collaborator-bootstrap, gitmirror-fleet, binjovi.

After deploy

The two operations will have reached five attempts and gone terminal. A terminal operation needs a fresh one, not a retry — I will re-request both and then verify the backup from outside the cluster with git ls-remote and the two check-repository-backup.sh guards, which currently hold make rebuild.

https://claude.ai/code/session_01TdW5FSvRJW8CtGnpd29Xp8

Binjovi could not read `forgejopushmirrors`, so `legacy_absent` returned 403 and **every codeberg mirror create failed `denied`** before it reached Forgejo. ## Measured, not predicted After `v0.1.611` deployed, both bootstrap operations sat at `last_error: "denied"`, attempt 3 of 5: ``` kubectl auth can-i get forgejopushmirrors -n forgejo --as=...:binjovi -> no kubectl auth can-i get forgejorepositories -n forgejo --as=...:binjovi -> yes ``` The Role is `resourceNames`-scoped per resource, and this resource had no rule at all. `ServiceLegacyClient` cannot then distinguish an **absent** controller object from a **denied** read, which is the whole point of that gate. ## This was my omission The design called this rule mandatory. I scoped RBAC out of the first seanfarm change because neither repository was in the repository bindings yet — which was correct at the time — and then never brought it back when the mirrors landed. The failure is exactly the one that was predicted. ## Scope Only the two repositories binjovi declares. The other 36 mirrors still belong to forgejo-operator, and a name here would let binjovi act on one of them. ## Guard The assertion looks the rule up **by resource** rather than by index, so reordering the rules cannot hide it, and asserts it is read-only and exactly those two names. Both controls confirmed red before this landed: - rule renamed away -> `FAIL: no forgejopushmirrors read grant; every mirror create dies denied` - grant widened to a third repository -> `FAIL: ... is not the two binjovi-owned mirrors, read-only` Passing: `binjovi-service-handoff`, `binjovi-repository-bootstrap`, `binjovi-collaborator-bootstrap`, `gitmirror-fleet`, `binjovi`. ## After deploy The two operations will have reached five attempts and gone terminal. **A terminal operation needs a fresh one, not a retry** — I will re-request both and then verify the backup from outside the cluster with `git ls-remote` and the two `check-repository-backup.sh` guards, which currently hold `make rebuild`. https://claude.ai/code/session_01TdW5FSvRJW8CtGnpd29Xp8
fix(binjovi): grant the legacy read the backup mirror gate needs
All checks were successful
binjovi/ci Binjovi completed the frozen plan
b9795703a0
Binjovi could not read `forgejopushmirrors`, so `legacy_absent` returned 403 and
every codeberg mirror create failed `denied` before it reached Forgejo.

MEASURED, NOT PREDICTED. After v0.1.611 deployed, both bootstrap operations sat
at `last_error: denied`, attempt 3. `kubectl auth can-i get forgejopushmirrors
-n forgejo --as=system:serviceaccount:binjovi:binjovi` answered `no`, while the
same question for `forgejorepositories` answered `yes`. The Role is
resourceNames-scoped per resource, and this resource had no rule at all.

This was my omission. The design called the rule mandatory; I scoped RBAC out of
the first change because neither repository was in the repository bindings yet,
and never brought it back when the mirrors landed.

Only the two repositories binjovi declares. The other 36 mirrors still belong to
forgejo-operator, and a name here would let binjovi act on one.

The guard now looks the rule up BY RESOURCE rather than by index, so reordering
cannot hide it, and asserts it is read-only and exactly those two names. Both
controls confirmed red: the rule renamed away, and the grant widened to a third
repository.

Note for the operator: the two operations reach five attempts and go terminal.
A terminal operation needs a FRESH one, not a retry, once this is deployed.

Claude-Session: https://claude.ai/code/session_01TdW5FSvRJW8CtGnpd29Xp8
binjovi-bot deleted branch fix/push-mirror-legacy-rbac 2026-09-10 18:06:53 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/seanfarm!588
No description provided.