Access control
GantryCD authenticates users through single sign-on and authorizes them through role-based access control, all scoped to an organization. This page is the user-facing model; operators set up the providers in Single sign-on, and the exact permission catalog lives in Permissions Catalog.
Sign-in and identity
There are no local accounts. Every user is provisioned the first time they sign in through an SSO provider — GitHub, Google, or any OIDC source. A user’s identity is derived from the provider, so a person from your GitHub org and the same person from an OIDC tenant are two distinct identities until explicitly linked.
The very first administrator is bootstrapped out-of-band (sign in once, then promote from a shell with database access). After that, everything is managed in the UI.
Membership
Being able to sign in is not the same as having access. A user needs an active membership in an organization before any permission counts. Members appear under Org settings → Members.
Roles and permissions
A role is a bundle of permissions. Each permission is a resource type,
an action, and an optional target — for example “deploy any stack”, “read
stacks named team-a-*”, or “deploy stacks in the authz group env-prod”.
The target is what scopes a role to a subset of resources; you don’t scope
the assignment, you scope the role.
Stacks can be targeted two ways. A name pattern matches the stack’s name
(* and ? wildcards). An authz group matches stacks that carry that
exact group name; a stack can carry several (team-payments, env-prod),
so one stack is reachable by several teams’ roles. Putting a stack into a
group, or taking it out, needs the stack_authz_group: assign permission
for that group name on top of the stack permission — so a team cannot move
a stack into a group whose grants it holds. Names work the same way in the
other direction: creating or renaming a stack is authorized by the name, so
a group-scoped role cannot move a stack into another team’s team-a-*. Groups are the option that stays
fast for very large organizations; patterns stay for naming conventions.
GantryCD ships system roles (Admin, and others) seeded on every install, and lets admins create custom roles per org for finer control. Creating custom roles is itself an admin-only action.
A user gets a role two ways:
| Way | Best for |
|---|---|
| Direct assignment | Stable identities — e.g. an external auditor. |
| SSO group binding | Everyone else. A (identity source, group) pair maps to one or more roles; anyone in that group gets the roles. |
Prefer group bindings. If your IdP renames a user, their direct assignments become orphaned (the identity changed), but group bindings just re-apply on the next sign-in. Bindings are how access scales without per-user toil.
Note: A new binding takes effect for affected users on their next sign-in (bounded by session lifetime). This is fail-closed — it under-grants briefly, never over-grants.
Service accounts
Service accounts are non-human principals for automation. There are two kinds:
- Per-stack default account — every stack auto-creates one; this is the identity a run authenticates as. Grant it roles to control what runs on that stack can do.
- Standalone accounts — org-level accounts admins create for automation, with their own long-lived API tokens.
A service account holds the same kind of roles a member does — except it can’t be granted the identity-management roles (managing members, roles, or other service accounts). That guard stops an automation token from escalating into account management.
Personal access tokens
A personal access token (PAT) lets you call the API and CLI as yourself, without a browser session. Each PAT is bound to one user and one organization; revoking your membership immediately invalidates your PATs in that org, and a PAT can never mint another PAT. See Automation.
Protected labels
Stack labels are ordinarily free-form
metadata that anyone with (stack, update) can edit. But a label can carry
weight — in particular, a
context can auto-attach to
any stack matching a label selector, so setting the right label pulls that
context’s variables and secrets onto the stack. Note the asymmetry:
explicitly attaching a context requires (context, read) on it, but
auto-attach performs no such check — it is gated only by who can set the
triggering label. So (label, update, env) is effectively “may receive every
context that auto-attaches on env,” including ones the holder cannot read. To
keep that safe, an org can mark specific label keys as protected.
Once a key is protected, adding, removing, or changing that key on a stack needs
a dedicated per-key permission — (label, update, <key>) — instead of the
blanket (stack, update). The same (label, update, <key>) grant also gates
the other direction — giving a context an auto-attach selector on that key.
So one permission is the single control point for everything a key can do through
auto-attach:
- Stack side (pull): who may set the label on a stack (and so pull matching contexts in).
- Context side (push): who may make a context auto-attach on that key (and so push its variables and hooks into every stack already carrying it).
Protect the keys your auto-attach selectors use.
(label, update, <key>)gates using a key as a selector, but the two sides are only symmetric when the key is also in the protected set. If you grant(label, update, app)without protectingapp, the push side is gated but the pull side stays open — any(stack, update)holder can still label their stackapp=…and receive the context (and its secrets). So a label key a secret-bearing context auto-attaches on should be protected; the context editor warns when it isn’t. As a rule: protect a key before you use it as an auto-attach selector.
This closes the push direction that protection alone would miss: a context can
only auto-attach on a key someone holds (label, update, <key>) for. With no
protected labels, (label, update, *) is admin-only, so by default only admins
can create auto-attaching contexts — you open it up per key on purpose. So you
can hand a team (label, update, env) to let them both set env=… on their
stacks and create contexts that auto-attach on env, protect the tier key,
and keep tier to the platform owners — all without widening anyone’s stack-edit
rights. Two more permissions govern the protected set itself:
(label, read)— view which keys the org protects (held by Stack Admin so enforcement isn’t opaque).(label, create)— change the protected-key set (a whole-set replace).
Manage protected keys under Org settings. Protection is not
retroactive on the pull side: only future changes to a protected key are gated,
so labels already on your stacks are grandfathered until someone edits them
(the push side has no such gap — a context is re-authorized against its full
selector on every create/update). Reserved gantrycd: keys can’t be protected —
they are already governed by their namespace rules. The exact resource model is
in Permissions Catalog and
Authorization.
How a check is decided (in one line)
Every request resolves to a single question — may this user do this action on this resource in this org? — answered by one database query against the user’s roles. Super-admins bypass it (so a broken org can always be recovered); everyone else needs membership plus a matching grant. The full design is in Authorization.