Evaluate Forgejo 16 Authorized Integrations as a replacement for the cross-repository PAT writers #806

Open
opened 2026-08-12 17:17:39 +00:00 by coilyco-ops · 0 comments
Owner

Outcome

Decide whether Forgejo 16's Authorized Integrations should replace part of the coilyco-ops PAT family, starting with the six cross-repository release writers.

This is an evaluation with a real tradeoff, not a straightforward win. It needs a decision before implementation.

How this surfaced

After the 16.0.2 upgrade (6b1bc59, merged 10fc64d), Forgejo Actions runs began showing a per-job banner:

Job image-build or its workflow has a permissions field, which is not supported in Forgejo and will be ignored. Use Authorized Integrations to grant capabilities to this job instead.

The banner renders above the log view rather than in the log bytes, which is why it does not appear in aosguard ops actions logs output.

Nothing regressed. permissions: was never honoured by Forgejo; 16.0.2 only started saying so. A separate sweep of all 29 repositories and 84 workflows found 31 workflows declaring permissions, of which 26 are .forgejo-lane and inert. That cleanup is cosmetic and deliberately out of scope here.

The banner's own remedy is what makes this issue worth opening.

What Authorized Integrations provide

A job sets enable-openid-connect: true, requests a JWT from Forgejo with the Integration's audience, and authenticates with HTTP Bearer. Per the documentation, the JWT can be used to "Interact with Forgejo's APIs, package registry, and Git operations", so this is not REST-only.

The rules bind a JWT to source repository, workflow filename, git ref, and trigger event. Capabilities use the same scopes as access tokens.

Why the cross-repository writers are the pilot

Six family members exist only so one repository's release job can write to another:

tap-bump-token                  scoop-write-token
agent-compose-tap-bump-token    agent-compose-scoop-bump-token
cli-guard-tap-write-token       cli-guard-scoop-write-token

Each is a static write:repository PAT. Anything holding one can write to the tap or bucket, from anywhere, until rotation. An Integration-issued token for the same job would be valid only from a named workflow, in a named repository, on a named ref, for a named event.

That is a real blast-radius reduction, and these six are the cleanest case because their consumer is unambiguously an Actions job.

Candidacy across the family

The discriminator is whether the consumer is an Actions job. No job means no OIDC context, so no Integration.

Candidates

  • registry-token (REGISTRY_TOKEN) - agentic-os release, dev-base-publish, ward promote
  • eco-mod-package-token (ECO_MOD_PACKAGE_TOKEN)
  • ci-release-token (CI_RELEASE_TOKEN) - agentic-os and cli-guard promote, ward promote / release
  • the six repository writers above
  • FORGEJO_PUSH_TOKEN - agentic-os-kai build-catalog-graph, sync-repo-digests, sync-repo-registry

Not candidates - no Actions job in the path

  • mcp-read-token - the Ward MCP pod
  • registry-read-token - Kubernetes imagePullSecrets
  • flux-deploy-token - the Flux controller
  • deploy-read-token - runner registration, outside any job
  • sirens-echo-issue-token - the in-namespace MCP pod
  • password - the minting root itself

Roughly ten of fifteen are candidates. The remainder are pods and controllers that will always need a PAT, so this reduces the family rather than retiring it.

The blocker

Authorized Integrations appear to be UI-only. The live instance's OpenAPI document carries 326 endpoints and none match integration, oidc, openid, or authorized:

$ curl -s https://forgejo.coilysiren.me/swagger.v1.json | jq '.paths | keys | length'
326
# zero matches for integration / oidc / openid / authorized

They are also configured per user, so they would live in the coilyco-ops bot account's settings.

That collides with how this fleet manages credentials. ward exec coilyco-ops-provision mints, verifies, rotates, and revokes the whole family as one attended scripted operation, and the inventory is reviewable in git. Click-configured Integrations would be invisible to that script and to review.

The tradeoff is therefore narrow-but-unauditable against broad-but-auditable, which is why this is a decision rather than a task.

Open questions

  1. Is there truly no API? A 16.0.x point release or an undocumented admin route may exist. Confirm before concluding.
  2. Can an Integration's configuration be exported or backed up, so the bot account is not the only copy?
  3. Does the package registry accept an Integration JWT for docker login, or only for REST package calls? The docs mention the registry but show only REST examples. registry-token candidacy depends on this.
  4. Does a cross-repository write work as expected - an Integration on coilyco-ops with capability targeting homebrew-tap, restricted to source repository cli-guard and workflow release.yml?
  5. If Integrations cannot be provisioned as code, is the blast-radius reduction still worth the auditability loss?

Suggested first step

Configure exactly one Integration by hand, for one of the six writers, and prove or disprove questions 3 and 4 before touching the provisioning script. A single working example settles most of this; the current uncertainty is not resolvable from documentation alone.

Out of scope

  • Removing the 26 inert .forgejo-lane permissions: blocks. Cosmetic, and unrelated to credentials.
  • The 5 .github-lane permissions: blocks. GitHub honours those - cli-guard's codeql.yml (security-events: write) and dependabot-auto-merge.yml (pull-requests: write) need theirs, so a blanket sweep would break them.

The token-minting skill is dated 2026-07-05 and predates this upgrade. Its trap 6, "No headless release re-run (Forgejo 15.0.2)", is now stale - aosguard ops actions rerun exists. The skill wants a pass against 16.0.2 regardless of the outcome here.

Next owner

Ops, for the single hand-configured probe. Any change to provision-coilyco-ops-bot.sh follows the decision, not the probe.

## Outcome Decide whether Forgejo 16's Authorized Integrations should replace part of the `coilyco-ops` PAT family, starting with the six cross-repository release writers. This is an evaluation with a real tradeoff, not a straightforward win. It needs a decision before implementation. ## How this surfaced After the 16.0.2 upgrade (`6b1bc59`, merged `10fc64d`), Forgejo Actions runs began showing a per-job banner: > Job image-build or its workflow has a permissions field, which is not supported in Forgejo and will be ignored. Use Authorized Integrations to grant capabilities to this job instead. The banner renders above the log view rather than in the log bytes, which is why it does not appear in `aosguard ops actions logs` output. **Nothing regressed.** `permissions:` was never honoured by Forgejo; 16.0.2 only started saying so. A separate sweep of all 29 repositories and 84 workflows found 31 workflows declaring `permissions`, of which 26 are `.forgejo`-lane and inert. That cleanup is cosmetic and deliberately out of scope here. The banner's own remedy is what makes this issue worth opening. ## What Authorized Integrations provide A job sets `enable-openid-connect: true`, requests a JWT from Forgejo with the Integration's audience, and authenticates with HTTP Bearer. Per the [documentation](https://forgejo.org/docs/latest/user/api/authorized-integrations/), the JWT can be used to "Interact with Forgejo's APIs, package registry, and Git operations", so this is not REST-only. The rules bind a JWT to **source repository, workflow filename, git ref, and trigger event**. Capabilities use the same scopes as access tokens. ## Why the cross-repository writers are the pilot Six family members exist only so one repository's release job can write to another: ``` tap-bump-token scoop-write-token agent-compose-tap-bump-token agent-compose-scoop-bump-token cli-guard-tap-write-token cli-guard-scoop-write-token ``` Each is a static `write:repository` PAT. Anything holding one can write to the tap or bucket, from anywhere, until rotation. An Integration-issued token for the same job would be valid only from a named workflow, in a named repository, on a named ref, for a named event. That is a real blast-radius reduction, and these six are the cleanest case because their consumer is unambiguously an Actions job. ## Candidacy across the family The discriminator is whether the consumer is an Actions job. No job means no OIDC context, so no Integration. **Candidates** * `registry-token` (`REGISTRY_TOKEN`) - agentic-os `release`, `dev-base-publish`, ward `promote` * `eco-mod-package-token` (`ECO_MOD_PACKAGE_TOKEN`) * `ci-release-token` (`CI_RELEASE_TOKEN`) - agentic-os and cli-guard `promote`, ward `promote` / `release` * the six repository writers above * `FORGEJO_PUSH_TOKEN` - agentic-os-kai `build-catalog-graph`, `sync-repo-digests`, `sync-repo-registry` **Not candidates** - no Actions job in the path * `mcp-read-token` - the Ward MCP pod * `registry-read-token` - Kubernetes `imagePullSecrets` * `flux-deploy-token` - the Flux controller * `deploy-read-token` - runner registration, outside any job * `sirens-echo-issue-token` - the in-namespace MCP pod * `password` - the minting root itself Roughly ten of fifteen are candidates. The remainder are pods and controllers that will always need a PAT, so this reduces the family rather than retiring it. ## The blocker **Authorized Integrations appear to be UI-only.** The live instance's OpenAPI document carries 326 endpoints and none match `integration`, `oidc`, `openid`, or `authorized`: ``` $ curl -s https://forgejo.coilysiren.me/swagger.v1.json | jq '.paths | keys | length' 326 # zero matches for integration / oidc / openid / authorized ``` They are also configured per user, so they would live in the `coilyco-ops` bot account's settings. That collides with how this fleet manages credentials. `ward exec coilyco-ops-provision` mints, verifies, rotates, and revokes the whole family as one attended scripted operation, and the inventory is reviewable in git. Click-configured Integrations would be invisible to that script and to review. **The tradeoff is therefore narrow-but-unauditable against broad-but-auditable**, which is why this is a decision rather than a task. ## Open questions 1. Is there truly no API? A 16.0.x point release or an undocumented admin route may exist. Confirm before concluding. 2. Can an Integration's configuration be exported or backed up, so the bot account is not the only copy? 3. Does the package registry accept an Integration JWT for `docker login`, or only for REST package calls? The docs mention the registry but show only REST examples. `registry-token` candidacy depends on this. 4. Does a cross-repository write work as expected - an Integration on `coilyco-ops` with capability targeting `homebrew-tap`, restricted to source repository `cli-guard` and workflow `release.yml`? 5. If Integrations cannot be provisioned as code, is the blast-radius reduction still worth the auditability loss? ## Suggested first step Configure exactly one Integration by hand, for one of the six writers, and prove or disprove questions 3 and 4 before touching the provisioning script. A single working example settles most of this; the current uncertainty is not resolvable from documentation alone. ## Out of scope * Removing the 26 inert `.forgejo`-lane `permissions:` blocks. Cosmetic, and unrelated to credentials. * The 5 `.github`-lane `permissions:` blocks. GitHub honours those - `cli-guard`'s `codeql.yml` (`security-events: write`) and `dependabot-auto-merge.yml` (`pull-requests: write`) need theirs, so a blanket sweep would break them. ## Related maintenance The `token-minting` skill is dated 2026-07-05 and predates this upgrade. Its trap 6, "No headless release re-run (Forgejo 15.0.2)", is now stale - `aosguard ops actions rerun` exists. The skill wants a pass against 16.0.2 regardless of the outcome here. ## Next owner Ops, for the single hand-configured probe. Any change to `provision-coilyco-ops-bot.sh` follows the decision, not the probe.
Sign in to join this conversation.
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
coilyco-flight-deck/infrastructure#806
No description provided.