Skip to content

Feature access & grants

b'nerd uses a ceiling/grant model to control which managed applications and platform features a project can activate. The model has two tiers:

  • Org ceiling — the platform sets an upper bound per feature on your organization. Org Admins can see the ceiling but cannot write it.
  • Project grant — an Org Admin distributes capacity within the ceiling to individual projects. The effective access a project holds is always ceiling ∩ grant (enforced at both grant time and app-create time — see Runtime enforcement below).
Platform (SuperAdmin)
  └─ sets ceiling on OrganizationFeature
        e.g. apps: allowed_kinds = [Managed::Gitlab::V1, Databases::Postgres::V1]

Org Admin
  └─ grants within ceiling to a project
        POST /organizations/{organization_id}/projects/{project_id}/feature-grants
        {
          "feature_key": "apps",
          "approved_configuration": { "allowed_kinds": ["Managed::Gitlab::V1"] }
        }

Project
  └─ holds the effective grant
        approved_configuration.allowed_kinds = ["Managed::Gitlab::V1"]

Absent ceiling means unbounded

If the platform has not set a ceiling for your organization on a given feature key, the org is treated as unbounded — Org Admins can grant any kind the platform supports for that feature. This is the default for all organizations, preserving behavior for existing projects and the automatic new-project grant.

Who can do what

Actor Can do
Platform admin Set the org ceiling (ceiling_configuration). Org admins receive 403 when attempting to write this field.
Org Admin Read the ceiling via GET /organizations/{id}/features/{feature_key}. Grant any kind within the ceiling to a project via POST /organizations/{organization_id}/projects/{project_id}/feature-grants.
Member (non-admin) Request feature access via POST /projects/{project_id}/feature_requests. Requests are subject to the org's auto-approve policy and the ceiling.

Org feature configuration

Org Admins can read and update their organization's feature settings.

Get feature configuration

GET /organizations/{id}/features/{feature_key}
Authorization: Bearer <token>

Returns the current configuration for the feature in your organization.

Response fields

Field Type Description
feature_key string Feature identifier (e.g. apps, k8s, object_storage)
enabled boolean Whether the feature is available for request in this org
auto_approve boolean Whether member requests are auto-approved
default_configuration object Configuration stamped on auto-approved grants
ceiling_configuration object | null Read-only, platform-managed. The upper bound the platform has set for this org. null means unbounded. Org Admins cannot write this field.

Update feature configuration

PATCH /organizations/{id}/features/{feature_key}
Authorization: Bearer <token>
Content-Type: application/json

{
  "enabled": true,
  "auto_approve": false,
  "default_configuration": {
    "allowed_kinds": ["Apps::App::V1"]
  }
}

Org Admins can update enabled, auto_approve, default_configuration, and auto_approve_configuration. The ceiling_configuration field is platform-admin-only — submitting it as an Org Admin returns 403 Forbidden with error code update_ceiling. Unknown app kinds in any configuration field return 422 Unprocessable Entity with error code ungrantable_kind.

ceiling_configuration is platform-admin-only

Org Admins cannot set ceiling_configuration. The platform sets ceilings; Org Admins manage grants within them. Passing this field as an Org Admin returns 403 — it is not silently ignored.

Project feature grants

Org Admins can grant feature access to individual projects within their org. Grants are bounded by the org ceiling — attempting to grant kinds outside the ceiling returns 422 with error code exceeds_ceiling.

List ceiling vs grants for a project

GET /organizations/{organization_id}/projects/{project_id}/feature-grants
Authorization: Bearer <token>

Returns the organization's per-feature ceiling and the project's current approved grants, side by side. Useful for seeing at a glance what is available to grant versus what has already been granted.

Response shape

{
  "metadata": { "..." : "..." },
  "data": {
    "ceiling": {
      "apps": { "allowed_kinds": ["Apps::Managed::Gitlab::V1", "Databases::Postgres::V1"] }
    },
    "grants": {
      "apps": {
        "allowed_kinds": ["Apps::Managed::Gitlab::V1"],
        "effective_allowed_kinds": ["Apps::Managed::Gitlab::V1"]
      }
    }
  }
}

ceiling reflects the org-level ceiling set by the platform. grants reflects what the project currently holds. A key absent from ceiling means that feature is unbounded for this org.

Each grant entry includes effective_allowed_kinds — the server-computed intersection of the stored grant and the current ceiling. Prefer this over the raw allowed_kinds in approved_configuration when deciding which kinds a project may currently use. If the ceiling is absent (unbounded), effective_allowed_kinds is null.

Access control

Status Condition
200 OK Caller is an Org Admin in this organization
403 Forbidden Caller is a plain member (not an Org Admin)
404 Not Found Organization or project not found, or caller is not a member

Create or update a grant

POST /organizations/{organization_id}/projects/{project_id}/feature-grants
Authorization: Bearer <token>
Content-Type: application/json

{
  "feature_key": "apps",
  "approved_configuration": {
    "allowed_kinds": ["Apps::Managed::Gitlab::V1"]
  },
  "review_notes": "Approved for Q3 GitLab pilot"
}

Request fields

Field Type Required Description
feature_key string yes Feature to grant (e.g. apps)
approved_configuration object no Grant configuration
approved_configuration.allowed_kinds array of strings no Application kind identifiers (e.g. Apps::Managed::Gitlab::V1). Omit to grant with no kind restriction (subject to the ceiling)
review_notes string no Optional note recorded on the grant for audit purposes

Response201 Created

{
  "metadata": { "..." : "..." },
  "data": {
    "id": "pfr_uuid",
    "feature_key": "apps",
    "status": "approved",
    "approved_configuration": {
      "allowed_kinds": ["Apps::Managed::Gitlab::V1"]
    }
  }
}

If the project already has an active grant for the feature key, the existing grant is updated in place — kinds are replaced, not merged.

Errors

Status Error code Meaning
403 Forbidden Caller is not an Org Admin
404 Not Found Organization or project not found, or caller is not a member
422 Unprocessable Entity exceeds_ceiling allowed_kinds contains kinds not in the org ceiling
422 Unprocessable Entity ungrantable_kind One or more kind strings are not recognized by the platform
422 Unprocessable Entity feature_not_available The feature is disabled for this organization (enabled: false)

Example: grant GitLab access to one project

POST /organizations/org_abc123/projects/proj_xyz789/feature-grants
Authorization: Bearer <token>
Content-Type: application/json

{
  "feature_key": "apps",
  "approved_configuration": {
    "allowed_kinds": ["Apps::Managed::Gitlab::V1"]
  },
  "review_notes": "GitLab pilot — approved by Org Admin"
}

The project can now activate a managed GitLab instance. Members of the project with apps permissions can proceed without filing a feature request.

Member request flow

Members who are not Org Admins follow the request flow:

  1. Member calls POST /projects/{project_id}/feature_requests with the desired feature key and configuration.
  2. If auto_approve is enabled on the org feature and the requested kinds are within the ceiling, the request is approved immediately.
  3. Otherwise, the request enters pending state and an Org Admin approves or denies it via the dashboard or the API.
  4. On approval, the project receives a grant bounded by the ceiling. Approval cannot grant kinds that exceed the org ceiling regardless of what the member requested.

Ceiling enforcement is unconditional

Every path that creates or updates a grant — member approval, auto-approve, and direct Org Admin grants — validates allowed_kinds ⊆ org ceiling before committing. There is no bypass.

Runtime enforcement — ceiling narrows app creation immediately

The org ceiling is enforced at two distinct points:

  1. Grant time — an Org Admin cannot grant kinds outside the ceiling (422 exceeds_ceiling).
  2. App-create time — when a project member attempts to create a new application, the platform re-checks effective_allowed_kinds = project grant ∩ current org ceiling. If the kind is outside the effective list, the request is rejected (422).

This means narrowing a ceiling takes effect immediately for all projects in the org, without revoking existing grants or affecting running applications. Running apps continue unaffected; only new app creates are gated.

Effective allowed kinds

The API exposes the server-computed effective list so clients do not need to recompute it:

  • GET /organizations/{organization_id}/projects/{project_id}/feature-grants — each grant entry includes effective_allowed_kinds (array of strings, or null meaning unbounded).
  • GET /projects/{project_id}/capabilities (via the project detail endpoint) — the apps capability includes effective_allowed_kinds.

Prefer effective_allowed_kinds over the raw allowed_kinds in approved_configuration when deciding which kinds a project may use — the raw field reflects the stored grant, the effective field reflects the current ceiling intersection.

Semantics table

Ceiling set? Grant has allowed_kinds? Effective
No (absent) Yes Grant's allowed_kinds as-is
No (absent) No (legacy full grant) Unbounded — all kinds allowed
Yes Yes ceiling ∩ grant (intersection)
Yes No (legacy full grant) Ceiling's allowed_kinds

enabled: false on an OrganizationFeature gates feature requests and delegation only — it does not affect runtime app-kind enforcement for projects that already hold a grant.