RBAC Decomposition

lockedCross-repo implementation sequencing for setldhub RBAC· 2026-05-30
0

What This Is

Cross-repo coordination doc. Lives in the workspace repo because it spans platform and frontend. The authoritative design is the RBAC analysis document. Per-repo implementation plans live in their repos, not here.

Fine-grained, deny-by-default RBAC for setldhub, spanning two repos:

  • platform (packages/card-auth/authorize-api, packages/shared/auth, + newpackages/shared/rbac) — Fastify/Prisma backend; the real enforcement gate.
  • frontend (apps/setldhub, libs/shared/auth, + newlibs/shared/rbac) — Angular 21; advisory UI gating only.

Hybrid model: Frontegg assigns coarse roles (roles[] JWT claim); a code-owned catalog (@setldpay/rbac) maps roles → fine-grained <entity>:<action>permissions. 14 roles, 36 permissions. Multi-role = union (additive, no deny). The design is locked (DEC-1...DEC-14 resolved); these units are the implementation, not a re-litigation.

1

Units & Sequencing

B - @setldpay/rbac catalog + interim FE sync ---- FOUNDATION; blocks C & D
   |-> C - Backend enforcement (platform)  -- carries the ISSUE-1/2/3/4 hotfixes
   |-> D - Frontend enforcement (frontend)  -- parallel to C once B lands
                                              |
                                              v
                                        E - Cutover (Frontegg roles, soak, retire aliases)
UnitRepo(s)ScopeDepends on
B platform + frontend @setldpay/rbac catalog (data + resolvers) in platform; interim vendored copy + the frontend repo's mise run rbac:sync + self-validating tests in frontend. ---
C platform resolvePermissions hook, requirePermission preHandler + route requires, DEC-6 eligibilityFiltersFor + widener migration, ApiKey.roles[] + adapter/issuer + backfill, scope-coverage tests. Carries the hotfixes. B
D frontend PermissionsService rewrite, authGuard + permissionGuard (kill alert), *spCan directive, sidebar requires, dev-token presets. B
E Frontegg + both Create real roles + scope assignment, shadow-mode soak/metrics, execute key backfill, retire RANUIAdministrator/onboarding-admin aliases. C, D
2

Security Hotfixes

Folded into Unit C, opening routes of its rollout.

#SevIssueHandling in C
ISSUE-1 CRITICAL mcc-imports fully ungated -- any token can publish, rebuilding global mcc_code Gated first in the route-by-route rollout.
ISSUE-2 HIGH x-internal is cosmetic -- agreement:write / program:write enforced by nothing Replace with real requirePermission.
ISSUE-3 INTENTIONAL agreement-auth write checks clientId, not programId Not a bug -- by design. Document (code comment + asserting scope test); no fix.
ISSUE-4 MEDIUM onboarding search may leak cross-client merchant/card existence Verify listMerchants/listAllCards are client-scoped.
ISSUE-5 HIGH client API keys unrestricted (no roles + no enforcement) Closed by the ApiKey.roles[] + backfill work.
3

Locked Sequencing Forks

  1. Distribution = interim sync bridge first. No publish pipeline exists in either repo today. The spec assumed a pipeline; we start with a vendored copy + manual mise run rbac:sync (frontend repo's task) + a source-commit stamp drift guard. The full AWS CodeArtifact pipeline (the spec's end-state) is a deferred follow-on.
  2. Hotfixes folded into Unit C (not a standalone track).

Resolved execution-order decisions (Unit C planning)

  1. Single big-C PR, no carve-out. All enforcement infra + the ApiKey prereq + the DEC-6 widener + the catalog grant ship in one platform PR, internally sequenced as TDD tasks.
  2. Per-route shadow → enforce. Route-option requires: Permission[] + optional enforceNow; global RBAC_ENFORCE (default false). Internal-only routes carry enforceNow: true → enforced on merge (closes ISSUE-1 + ISSUE-2). All external routes get requires in shadow.
  3. Uniform token-type handling. All token types resolve roles → permissions identically; no token-type-specific ceiling. Keys are confined by role assignment. Defense-in-depth ceiling deferred.
  4. PM + client-onboarder granted signing. With ISSUE-3 reclassified intentional, agreement_authorization:write + onboarding:sign_agreement are added to both roles in the catalog.
4

Sequencing Hazards

These are guaranteed by Unit B's catalog and must be handled before C flips enforcement:

  • sp-service ships with [] permissions (deny-by-default). A workspace-wide search found zero backend service-to-service callers of authorize-api. So shipping sp-service: [] is low-risk. Keep a cutover-time per-env check of ALLOW_INTERNAL_TOKENS.
  • API keys carry no roles todaypermissionsForRoles([]) is empty → every key 403s the moment enforcement flips (ISSUE-5). C must land ApiKey.roles[] + adapter/issuer threading + a least-privilege backfill before any backend enforcement. This is a hard prerequisite.
  • program-manager / client-onboarder ship without signing permissions. Resolved: ISSUE-3 is intentional, so C grants both roles these permissions in the catalog.
5

Provenance

Decomposed from the locked design on 2026-05-30. Unit B plan reviewed via three-leg multi-model-review (self-audit + codex + adversarial) before execution — matrix fidelity verified clean (0 cell divergences, 36 perms / 14 roles); findings folded into the per-repo plans.