iterion

Forge permissions & identity — who a bot acts as

This is the model people most often get wrong: the iterion user who launches a bot is NOT who the bot acts as on the forge. Two completely separate planes are at play. This page documents both and how they meet (they barely do).

TL;DR

So: “users in a GitHub team that has repo rights → do they inherit those rights to make PRs?”No. Their GitHub membership only gated their login to the iterion org. The PR is opened with the connection’s token.

The two planes

Plane 1 — iterion RBAC (what you can do in iterion)

Per-team role, ordered viewer(1) < member(2) < admin(3) < owner(4) (pkg/identity/types.go). Gates iterion operations: launch runs (member+), manage integrations / keys / members / SSO (canManageTeam = admin+, pkg/server/auth_routes.go). It does not choose or change any forge credential.

GitHub SSO team-gating populates this plane only: a verified+enabled grant (github_org, team_slug) → member lets matching users log in and join the iterion org. The GitHub OAuth login token is used once to read the user’s email + org/team memberships and is then discarded — never stored, never reused for forge writes (pkg/auth/oidc/github.go, pkg/auth/oidc_service.go).

Plane 2 — the forge connection (what a bot does on the repo)

A forge.Connection (pkg/forge/types.go) is a team-scoped service credential. Its sealed token is opened (AdminTokenFor, pkg/forge/connection_sealer.go) and re-packaged once as a managed forge_token generic secret (ensureManagedSecret, pkg/forge/orchestrator.go), injected into every bot run at /run/iterion/secrets/forge_token. The bot’s gh/glab/git calls authenticate with that token — i.e. the connection’s identity. (The managed secret holds the same token value as the connection; the re-seal only changes the encryption envelope, not the identity.)

Who the connection acts as — by kind

Connection kind Token Acts on the forge as Repo rights =
oauth_app OAuth user access token (refreshable) the user who authorized the OAuth app that user’s repo permissions
github_app Installation token (minted ~1h, GitHub only) the GitHub App (a bot identity) the App’s manifest permissions (narrow, least-privilege)
pat Personal access token (static) the PAT owner (a person or a service account) the PAT’s scopes

The launching user’s iterion role is irrelevant to this: a member and an admin launching the same bot both write via the same connection token. Provision records the launching user only as ActorID / created_by for the audit trail (pkg/forge/orchestrator.go) — never for authentication.

What this means for your setup (and the super-admin question)

If the GitHub connection is oauth_app authorized by an org-admin (e.g. the super-admin’s own account), then every bot run that any team user launches opens PRs / pushes as that org-admin, with that account’s full GitHub powers — a shared, broad service credential. Convenient, but high blast radius: a plain iterion member who can launch a bot effectively wields the connecting admin’s GitHub rights on the repos that account can reach.

Being a GitHub org-admin yourself does not grant your bots anything extra beyond what the connection token can do; it only mattered because the org-admin is typically who authorized the connection.

Least privilege

Prefer narrowing the connection, not the user:

GitHub vs GitLab vs Forgejo

The identity model is identical across providers — connection token = acting identity — via the common forge.Admin abstraction (ADR-049, docs/adr/049-forge-as-interchangeable-substrate.md).

  GitHub GitLab Forgejo/Gitea
oauth_app (acts as the authorizing user)
pat (acts as the token owner)
github_app (acts as a scoped App/bot) ✅ (best path on a self-hosted instance: a project/group access token used as a pat) — (no App concept; same — use a bot-account PAT)
auth header Bearer Bearer token (Gitea scheme)

So the only real difference is that the narrow “bot identity” path is a first-class GitHub App on GitHub, whereas on GitLab/Forgejo you approximate least-privilege with a scoped service-account / project token connected as a pat. Clients: pkg/forge/gitlab/client.go, pkg/forge/forgejo/client.go.

Audit — correlating a forge action back to a person

The forge action is authored by the connection identity, but who triggered it is recorded: RepoIntegration.CreatedBy / the run’s ActorID and the tenant audit log (pkg/audit). So “a PR opened by the connection bot” is traceable to the iterion user (and run) that launched it, even though the GitHub author is the connection.

Possible evolutions (not built today)

When any of these ship, update this page.