Skip to main content

roles overview

RBAC in duck-iam - roles convert to ABAC policies internally and run through the same evaluation pipeline.

What roles do

Roles are the RBAC side of duck-iam. Build them with defineRole(), granting permissions as action/resource pairs. At evaluation time, roles convert to ABAC policies and run through the same engine as hand-written ones.

import { defineRole } from '@gentleduck/iam'

const viewer = defineRole('viewer')
  .name('Viewer')
  .desc('Read-only access to published content')
  .grant('read', 'post')
  .grant('read', 'comment')
  .build()

defineRole() returns a RoleBuilder. .build() produces the plain AccessControl.IRole object.


Reading order

PageCovers
definitiondefineRole(), grant(), shorthand methods, meta()
inheritanceinherits(), multi-parent, cycle handling, depth
scoped rolesMulti-tenancy - role-level scope, permission-level scope, scoped assignments
conditional permissionsgrantWhen() - attribute-aware grants on roles
type-safe rolesdefineIam() constraints, validation, edge cases
rolesToPolicyHow roles become an ABAC policy under the hood

Role object structure

interface AccessControl.IRole {
  id: string
  name: string
  description?: string
  permissions: readonly AccessControl.IPermission[]
  inherits?: readonly string[]
  scope?: string
  metadata?: Readonly<IamPrimitives.Attributes>
}

interface AccessControl.IPermission {
  action: string | '*'
  resource: string | '*'
  scope?: string | '*'
  conditions?: AccessControl.IConditionGroup
}

Each permission is an action/resource pair with optional scope and conditions. Roles are stored as plain JSON in the adapter - no runtime classes. See types & namespaces for the full namespace map.


How roles fit in

Loading diagram...

The engine doesn't have a separate "RBAC" path. It calls rolesToPolicy() to turn role definitions into one synthetic ABAC policy with id: '__rbac__', then evaluates that alongside your custom policies. See rolesToPolicy for the conversion details.