Raxx · internal docs

internal · gated

RBAC V2 — CI lint gates

Issue: #1480 Script: scripts/ci/lint_rbac_legacy_callsites.sh Workflow job: rbac-legacy-lint in .github/workflows/ci-pr.yml Gate added: 2026-06-30

Purpose

After the S3–S10 RBAC V2 cutover (#1472, #1498, #1499), the legacy @require_role decorator and inline users.role == comparisons were removed from all Console blueprints and Raptor routes. This CI gate prevents those patterns from reappearing via future PRs.

What the gate checks

Check 1 — Console blueprints + middleware: @require_role active decorator

Scope: console/app/blueprints/ and console/app/middleware/

Pattern: ^[[:space:]]*@require_role\( (extended regex)

Matches only lines where @require_role( is the first non-whitespace token on the line — i.e. an active Python decorator call. The pattern intentionally excludes:

What to use instead: @require_rbac_role or @require_permission from app.middleware.rbac.

Check 2 — Raptor routes: users.role == hardcoded comparison

Scope: backend_v2/api/routes/

Pattern: users\.role[[:space:]]*== (extended regex)

Matches direct role comparisons that bypass the RBAC V2 has_permission() / require_rbac_role model.

What to use instead: has_permission(g.admin, "<permission>") or the @require_rbac_role decorator from app.middleware.rbac.

Tests directory exclusion

Both checks exclude */tests/* sub-directories via --exclude-dir=tests. Test fixtures may legitimately reference legacy patterns for backward-compatibility assertions.

Suppressing a false positive (# rbac-lint-ok)

If a line is a genuine exception (test fixture, migration compat shim, or external interface constraint), add the trailer comment # rbac-lint-ok to the same line as the callsite:

@require_role("superadmin")  # rbac-lint-ok — compat shim for legacy client, see #9999

Requirements for suppression:

  1. The # rbac-lint-ok comment must be on the same line as the callsite.
  2. Include the issue number that approved the exception.
  3. The PR introducing the suppression requires explicit code-review sign-off on the exception — do not suppress silently.
  4. Document the exemption in the PR description under a heading ## rbac-lint-ok suppressions.

Suppressions are auditable: git grep '# rbac-lint-ok' shows all active exceptions at a glance.

Running locally

bash scripts/ci/lint_rbac_legacy_callsites.sh

Expected output on a clean tree:

Check 1: @require_role active decorator in console blueprints + middleware
  OK — no active @require_role decorators found in Console scope

Check 2: users.role == hardcoded comparison in Raptor routes
  OK — no users.role == comparisons found in Raptor routes

lint_rbac_legacy_callsites: all checks passed.

CI workflow integration

The gate runs as rbac-legacy-lint in ci-pr.yml, triggered when a PR touches:

The job is path-filtered so it does not incur cost on unrelated PRs.

Future expansion (Phase 2+)

When new surfaces are added (Reasonator routes, Velvet endpoints), extend the script with an additional grep block following the same pattern. Update this doc to list the new surface and its grep pattern.