waylog
Core Systems

Permissions (RBAC & FGAC)

The 64-bit BigInt permission architecture.

Waylog requires an organizational structure capable of supporting thousands of VTC members with granular control. We implemented a system heavily inspired by Discord, combining Role-Based Access Control (RBAC) with Fine-Grained Access Control (FGAC).

The Bitfield Architecture

Instead of utilizing rigid string enumerations (e.g., "ADMIN", "MODERATOR"), permissions in Waylog are mapped to the bits of a 64-bit integer (BigInt).

Currently, 27 of the 64 available bits are allocated across 8 logical categories:

  1. General (4 bits): Viewing, editing, and deleting the VTC.
  2. Members (4 bits): Kicking members, viewing audit logs.
  3. Roles (4 bits): Managing the role hierarchy.
  4. Applications (4 bits): Handling recruitment pipelines.
  5. Jobs/Dispatch (4 bits): Dispatching deliveries and viewing fleet telemetry.
  6. Convoys/Events (3 bits): Event organization.
  7. Announcements (3 bits): Internal communications.
  8. Admin (5 bits): High-level organizational control.

All bitwise constants are exported from the shared @waylog/permissions package.

export const VTC_GENERAL_VIEW = 1n << 0n;
export const VTC_MEMBERS_MANAGE = 1n << 5n;
export const VTC_JOBS_DISPATCH_CREATE = 1n << 17n;

Role Templates & Assignment

VTC Owners can create infinite custom roles, establishing a hierarchy using the position integer. Waylog provides sensible defaults:

  • OWNER: Automatically granted all 27 bits.
  • EVERYONE: The baseline role, typically restricted to VTC_GENERAL_VIEW.
  • DISPATCHER: Granted specialized bits within the Jobs/Dispatch category.

Members can be assigned multiple roles; their baseline permissions are the combined total (Bitwise OR) of all assigned roles.

Fine-Grained Access Control (FGAC)

Role templates are powerful, but sometimes a specific user needs a unique exception. The vtcMember record includes two dedicated fields:

  • permissionOverrides (BigInt): Explicitly grants specific permissions to a user, regardless of their roles.
  • permissionDenials (BigInt): Explicitly revokes specific permissions from a user, overriding any roles that might grant them.

Computing Effective Permissions

To determine a user's actual capabilities, the server evaluates their "effective" permissions on the fly:

function computeEffectivePermissions(
  rolePermissions: bigint[], 
  overrides: bigint, 
  denials: bigint, 
  isOwner: boolean
): bigint {
  if (isOwner) return ALL_PERMISSIONS;
  
  // Combine roles (OR)
  const basePerms = rolePermissions.reduce((acc, curr) => acc | curr, 0n);
  
  // Add overrides (OR), then remove denials (AND NOT)
  return (basePerms | overrides) & ~denials;
}

In the Fastify API, a requirePermission(VTC_MEMBERS_MANAGE) hook automatically performs this computation, rejecting unauthorized requests at the routing layer.

On this page