Skip to main content

Authorization

Two properties drive authorization in Quonfig:

  1. Every environment is either protected or not.
  2. Every flag and config has an access levelsupport, standard, protected-env, or protected-all-envs.

Cross the two and you get a matrix of edit decisions. Roles are how you grant someone cells of that matrix: each role is a row. That is the whole model — the rest of this page is the matrix, plus a few footnotes.

Environments: protected or not

Each environment carries a protected bit. Marking production protected is the typical move, but nothing is hard-coded: mark prod-eu or a compliance environment protected and every rule on this page treats it exactly the same way.

Marking an environment protected does not lock every config in it. It changes the verdict only where the matrix says it does — most visibly for protected-env configs, which lock in protected environments and stay open everywhere else.

Configs: four access levels

Every flag and config carries an access field. Default if omitted: standard.

accessMeaningTypical use
supportEditable by the support team as well as engineers.Account-level overrides, manual feature grants
standardEditable by engineers, in any environment.Most flags and configs
protected-envLocked in every protected environment; open everywhere else."Engineers change it freely in dev and staging; only the inner circle changes it in production"
protected-all-envsLocked down everywhere.Pricing, billing, compliance-sensitive configs

Hierarchy from lowest to highest: support < standard < protected-env < protected-all-envs.

Roles

Three roles grant config editing. Each one is a row of the matrix below:

RoleWhat it grants
SupportEdit support configs, in every environment.
EngineerEdit support and standard configs everywhere, and protected-env configs in non-protected environments.
Protected EngineerEdit everything, everywhere.

Everyone also holds Member, the read-only floor: view workspaces, configs, and history. Roles are assigned at the organization scope and apply uniformly to every workspace in the org, and they combine — an Engineering Manager might hold Engineer plus Admin, a senior IC Protected Engineer alone.

The matrix

Columns are each access level crossed with where your edit lands: a non-protected environment, a protected environment, or the default value (what an environment serves when none of its own rules matched).

Who can edit whatAccess level across the top; where the edit lands underneath it. A cell is the verdict for an edit that touches only that section.SupportStandardProtected-envProtected-all-envsnon-protectedenvironmentprotectedenvironmentdefaultvaluenon-protectedenvironmentprotectedenvironmentdefaultvaluenon-protectedenvironmentprotectedenvironmentdefaultvaluenon-protectedenvironmentprotectedenvironmentdefaultvalueMemberlocklocklocklocklocklocklocklocklocklocklocklockSupporteditediteditlocklocklocklocklocklocklocklocklockEngineereditediteditediteditediteditlockcondlocklocklockProtected Engineereditediteditediteditediteditediteditediteditediteditableconditional — editable only while every protected environment ends in a catch-all rulelocked
The same matrix in text

Each cell reads non-protected environment / protected environment / default value:

Rolesupportstandardprotected-envprotected-all-envs
Memberlock / lock / locklock / lock / locklock / lock / locklock / lock / lock
Supportedit / edit / editlock / lock / locklock / lock / locklock / lock / lock
Engineeredit / edit / editedit / edit / editedit / lock / condlock / lock / lock
Protected Engineeredit / edit / editedit / edit / editedit / edit / editedit / edit / edit

Three things to know when reading it:

  • A save is judged per section you touched. Editing only your staging rules never asks for permission in a protected environment — the protected column applies only when your edit actually lands there.
  • Creating and deleting a config, and metadata changes (name, description, tags, the access field) are not scoped to any environment — a new flag or a deletion changes what every environment serves — so they are judged at the strictest context. Nobody can lower a config's access in order to edit it.
  • The one cond cell is the default value of a protected-env config. It moves as the config's own rules change:
When is a default value locked, and what to do about it

A config's default value is what an environment serves when none of that environment's own rules matched. Editing the default is therefore an edit to your protected environments — unless they can never fall through to it:

The default value is unreachable — and unlocks — when every protected environment ends in a terminal catch-all: a rule with no criteria, or one whose criteria are all "always true". Then editing the default cannot change what those environments serve.

Concretely, with production marked protected:

DEFAULT IS REACHABLE  ->  locked

production:
if plan is "pro" -> on (targeted; can fail to match)
(no catch-all: falls through)
default:
always -> off <- production serves this

DEFAULT IS UNREACHABLE -> editable

production:
if plan is "pro" -> on
always -> off (terminal catch-all)
default:
always -> off <- production never reaches it

Reachability is computed on the stored document, not the one being written — so removing the protected environment's catch-all to unlock the default doesn't work: that removal is itself an edit to the protected environment, which is exactly what the fence denies.

If the editor tells you the default is locked, you have two ways forward:

  • Ask a Protected Engineer to add an explicit rule in the protected environment. Once every protected environment ends in a rule that always matches, the default unlocks — permanently, until someone removes that rule.
  • Set the value in your own environment instead. Usually what you actually wanted: an explicit rule in staging beats the default for staging traffic and leaves the protected environments alone.

Additional roles: fencing people out of protected environments

The roles above lose access in a protected environment only where the config's access level says so — an Engineer still edits standard configs in production. Some teams also want the opposite: people who can work freely in staging and dev but can never touch a protected environment at all. Two opt-in roles do that.

Support and Engineer, non-protected environments only
RoleSlugWhat it grantsWhere
Support (non-protected envs)support_nonprotectedEdit support configsNon-protected environments only
Engineer (non-protected envs)engineer_nonprotectedEdit support, standard, and protected-env configsNon-protected environments only

The fence is the environment's protected bit — the same bit used everywhere on this page, not a hard-coded "production". And unlike the unscoped roles, here it is absolute: a holder is locked out of a protected environment at every access level, including support and standard.

Their rows of the matrix (same cell format as above — non-protected environment / protected environment / default value):

Rolesupportstandardprotected-envprotected-all-envs
Support (non-protected envs)edit / lock / condlock / lock / locklock / lock / locklock / lock / lock
Engineer (non-protected envs)edit / lock / condedit / lock / condedit / lock / condlock / lock / lock

cond is the same conditional default described above: editable only while every protected environment ends in a catch-all rule. If a holder finds the default locked, the two ways forward are the same too — an explicit rule in their own environment, or a Protected Engineer adds a catch-all in the protected one.

Beyond the environment fence, these roles are narrower in a second, independent way: a whole class of writes has no environment for the fence to open on, so it stays closed regardless of how your environments are configured:

  • Creating or deleting a config. A new flag defines what every environment serves, including protected ones. Ask an engineer to create the skeleton, then edit it.
  • Metadata — name, description, tags, the access field.
  • Segments, schemas, and restore-from-history — whole-file writes with no environment to name.
  • SDK keys — workspace plumbing, not a per-environment config edit.

One wire-level note: the conditional default exists only when saving a whole document (the editor's Save, qfg push). An env-targeted write — PATCH /v1/flags/{key}/environments/{env}, MCP set_flag — names a real environment and can never address the default value at all.

These two roles are not self-serve — they don't appear in your role picker until switched on for your organization. Contact us and we'll enable them; everything else on this page works out of the box.

Members and admins

Member is the read-only floor everyone holds automatically: view workspaces, configs, and history.

Admin manages workspace plumbing — invites, role assignment, billing, environments, SDK keys, integrations. It grants no config editing and stacks with the roles above: an EM can manage the team without being able to edit pricing, and a senior IC can hold Protected Engineer without being on the hook for billing.

SDK-key management is the one place the axes meet: an Admin manages keys in any environment; otherwise the config role decides — Protected Engineer anywhere, Engineer in non-protected environments, Support not at all.

Where authorization is enforced

Everything on this page is enforced in Quonfig's application layer, and every write path converges on it: the app UI, both CLI push engines (qfg push and qfg migrate --push), the public API, and MCP. The layer sits between all of those and your workspace's git repo — and your deployed SDKs receive only what's committed to that repo.

YOUR MACHINEQUONFIGYOUR INFRASTRUCTUREApp UIqfg pushfrom a local checkoutREST API / MCPlocal files are plain text —editing them enforces nothingAuthorizationrole × access level× environmentthe matrix above✗ denied writes stop hereand never reach the repocommitsWorkspace git repothe stored truth —every change is a commitYour serversSDKs receive onlywhat's committed,via SSE / HTTPEvery write path converges on the same authorization layer, before anything reaches the repo your SDKs consume.

This placement is what makes local editing harmless. A local checkout of your workspace is plain text — anyone can open the JSON in an editor and change anything, and nothing stops them, because nothing needs to: those edits change nothing until they're pushed, and the push lands in the authorization layer before it can touch the repo. Direct pushes to the underlying git repo bypass these tier checks; they're rare, bounded by repo-level access, and structurally validated, but per-file authorization at the git layer is not enforced today.