Skip to content

Roles & permissions

Every guarded request evaluates:

effective permission =
organization role base grant from your membership
+ repository override per-repository access level, when set
+ token scope a hard cap — never widens
+ account status must be ACTIVE
+ organization status must be active

Any input denying produces 403 forbidden. Scopes only ever subtract.

The granular capabilities the API checks. You rarely name these directly, but they’re what the tables below resolve to.

Permission Covers
organization.read Read the organization
organization.update Change settings, manage webhooks
organization.delete Delete the organization
members.read List members
members.invite Invite members
members.remove Remove members, change roles
billing.read Read plan, subscription, invoices
billing.update Checkout, billing portal, add-ons
repositories.create Create repositories
repositories.read List and read repositories
repositories.update Change repository settings
repositories.delete Delete repositories
repository.pull Download objects
repository.push Upload objects, index, snapshot
repository.admin Full control of one repository
repository.members.manage Manage collaborators and repository invitations
tokens.create / tokens.revoke Manage API tokens
audit.read Read the audit log
usage.read Read usage and storage metrics
storage.read / storage.manage Read / manage storage connections and groups
Permission OWNER ADMIN DEVELOPER VIEWER BILLING
organization.read
organization.update
organization.delete
members.read
members.invite
members.remove
billing.read
billing.update
repositories.create
repositories.read
repositories.update
repositories.delete
repository.pull
repository.push
repository.admin
repository.members.manage
tokens.create
tokens.revoke
audit.read
usage.read
storage.read
storage.manage
Person Role Why
Founder, platform lead OWNER Needs deletion and billing
Team lead, SRE ADMIN Manages people and storage, not spend
Engineer, researcher DEVELOPER Pushes and pulls, makes repositories
Auditor, PM, stakeholder VIEWER Reads and pulls only
Finance BILLING Invoices and usage, no data
Contractor on one project any + a repository override, or an external repository invitation Least privilege

An override replaces the org role for that repository only.

Access Grants
NONE Nothing. Revokes access an org role would otherwise give.
READ repository.pull, repositories.read
WRITE READ + repository.push, repositories.update
ADMIN WRITE + repository.admin, repositories.delete, repository.members.manage

Managed at Repository → Settings → Collaborators, or:

Terminal window
export API=https://cavsnode.com/api/v1
export H="Authorization: Bearer $CAVS_TOKEN"
curl -sS "$API/repositories/$REPO/members" -H "$H"
curl -sS -X POST "$API/repositories/$REPO/members" \
-H "$H" -H "Content-Type: application/json" \
-d '{"user_id": "…", "access": "WRITE"}'
curl -sS -X DELETE "$API/repositories/$REPO/members/$USER_ID" -H "$H"

Requires repository.members.manage (ADMIN/OWNER, or an ADMIN override).

What each scope satisfies:

Permission repo:read repo:write repo:admin org:read usage:read
repository.pull / repositories.read
repository.push / repositories.update
repository.admin / repositories.delete
repositories.create
repository.members.manage
organization.read / members.read
usage.read

Note the two conveniences: repo:read also satisfies organization reads (so a read-only token can resolve slugs), and org:read also satisfies usage reads.

Anything not in this table — billing, audit, token management, storage connections, organization update or delete — cannot be reached by any token scope. Those operations require a Firebase-authenticated session with the right role. This is intentional: a leaked CI token can’t change your plan, read your audit trail, mint more tokens or repoint your storage.

Situation Outcome
DEVELOPER + repo:write token → push
VIEWER + repo:write token → push 403 — the role doesn’t grant push; the scope can’t add it
DEVELOPER + repo:read token → push 403 — the scope caps it
DEVELOPER, repository override NONE → pull that repository 403
VIEWER, repository override WRITE → push that repository
cavs_ci_ with repo:write, no user → push ✓ — scope alone decides
cavs_ci_ with repo:admin → read the audit log 403 — no scope reaches audit
Any role, suspended account 403 — “account is suspended”
ADMIN → start a checkout 403 — billing is OWNER/BILLING
  1. GET /users/me — which organizations and roles does this credential actually see?
  2. If it’s a token, check its scopes in the token list. Is the scope narrower than the operation?
  3. Check for a repository override on the repository in question — an override wins over the role.
  4. Check the account and organization status.
  5. Confirm the operation is reachable by tokens at all (billing, audit, token and storage management are not).

The request_id in the error body ties the denial to a specific server-side event if you need support to look.