Authorization¶
Authentication decides who you are. Authorization decides what you can do.
AppGantry uses several overlapping checks:
- Role checks: is the developer a member of the org / project with the right role?
- Project access token role checks: if the request authenticated via a project access token, does the token's granted role cover the action, and does its bound project / org match the URL?
- PAT scope checks: if the request authenticated via a personal access token, does the PAT carry the right scope?
- PAT org-binding: does the PAT's bound org match the URL's org?
A request that fails any check returns HTTP 403 with
error: "permission_denied" or error: "insufficient_role".
Organization roles¶
Members of an organization carry one or more of these roles:
| Role | What it lets the developer do |
|---|---|
READ |
View org metadata, member list, projects. |
EDIT_PROJECT |
Create / edit / delete projects. |
MANAGER |
All project-level operations across every project in the org. |
BILLING |
View invoices, set spend caps, manage payment methods. |
EDIT_ORGANIZATION |
Edit org name, retention defaults, members. |
ADMIN |
Superset of every other role. |
Roles compose: a single member can hold any combination. ADMIN
implies every other role.
Project roles¶
Projects layer additional, finer roles on top of org membership:
| Role | What it lets the developer do |
|---|---|
READ |
View project metadata + builds. |
DOWNLOAD_BUILD |
Pull a build artifact (gated by license / billing). |
UPLOAD_BUILD |
Upload new builds (also gated by billing on Team). |
EDIT_PROJECT |
Edit project name, settings. |
MANAGE_TESTERS |
Invite, remove, regroup testers. |
MANAGE_RELEASES |
Create and update releases / channels. |
DELETE_BUILD |
Permanently delete a build (audit-logged). |
ADMIN |
Superset of every project role. |
Org-MANAGER fallback¶
A developer with OrganizationMemberRole.MANAGER on a project's
owning org is treated as having the full project role set without
needing a per-project membership row. Project-level grants are checked
first; org-MANAGER is the fallback.
This makes "an org admin can operate on every project without per-project memberships" work without requiring you to keep per-project memberships in sync for admins.
Project access tokens¶
A project access token authorizes by project role, not by scopes. At creation it is granted a role from the project roles above, capped at the creating developer's own effective role in the project. The token can never do more than that role allows, and never more than the developer who minted it could do.
| Granted role | Typical use |
|---|---|
Read |
Read-only automation: list builds, read metadata. |
Read + Download |
Mirror or archive build artifacts. |
Read + Download + Upload |
The canonical CI build pipeline. |
Maintainer |
Automation that also manages channels or testers. |
Admin |
Full control of the project's build and release surface. |
A project access token is bound to one project and its organization
when created. A request whose ?organization_id= / ?project_id=
doesn't match the token's binding is rejected, so a leaked token is
confined to a single project.
Why bind to one project?¶
A project access token has no human owner, so it cannot inherit a developer's roles. Binding it to one project with an explicit, capped role keeps a leak confined to that project rather than everything its creator can reach. If your CI pushes to several projects, create one token per project.
Managing project access tokens (create, rotate, revoke) is
interactive-only: those routes require a logged-in developer session
and reject both PATs and project access tokens. So even an Admin
project access token cannot mint or revoke other tokens.
PAT scopes¶
Personal access tokens carry their own scope bitmask. Scopes are an intersection with the underlying developer's roles: if either says "no", the request is denied.
| Scope | Lets the token… |
|---|---|
builds:write |
Upload builds, complete uploads, set notes. |
builds:read |
List builds and read metadata. |
testers:write |
Invite and remove testers. |
testers:read |
List testers. |
releases:write |
Create and update releases / channels. |
releases:read |
List releases / channels. |
audit:read |
Read the org's audit feed. |
pat:revoke |
Revoke other PATs (used by ops tooling). |
A PAT is also bound to one organization when you create it. A
request whose ?organization_id= query parameter doesn't match the
PAT's bound org is rejected, even if the developer who owns the PAT
is a member of both orgs.
Why bind PATs to one org?¶
Leaking a PAT is bad. Leaking a PAT that grants access to every org you belong to is much worse. Per-org binding limits the blast radius to a single tenant.
If you need to script against multiple orgs from a single developer account, create one PAT per org.
Authorization for testers¶
Testers don't hold roles or scopes. Their authorization is purely the invite token they were given. That token is the credential; the identity, and the authorization scope, all at once.
An invite token authorizes:
- Loading the install page for a specific build (or set of builds the tester was invited to).
- Downloading the build artifact through a signed URL.
…and nothing else. Testers cannot read your audit feed, see your members list, or list other testers.
Common 403 shapes¶
When a request fails authorization, the error code tells you which check failed:
error |
Cause |
|---|---|
permission_denied |
The identity has no membership / scope that grants this action. |
insufficient_role |
The identity is a member, but lacks the specific role / scope this action requires. |
email_not_verified |
The developer hasn't verified their email yet. |
operation_not_allowed |
The credential type is wrong (e.g. a PAT was used on a route that requires a JWT). |
All four return HTTP 403.
See also¶
- Authentication: login flow, JWT lifetimes, token formats.
- Audit & spend caps: every permission-affecting change lands in the audit feed.
- CI with project access tokens: token creation + role choices.