Files
rbac/docs/adr/ADR-001-bitset-permissions.md
Rene Nochebuena 0864f031a1 feat(rbac): initial stable release v0.9.0
Foundational identity and permission types for role-based access control — bit-set PermissionMask, immutable Identity value type, and PermissionProvider interface.

What's included:
- `Identity` value type with NewIdentity / WithTenant constructors and SetInContext / FromContext context helpers
- `Permission` (int64 bit position) and `PermissionMask` (int64 bit-set) with O(1) Has and non-mutating Grant
- `PermissionProvider` interface for DB-backed ResolveMask(ctx, uid, resource) resolution

Tested-via: todo-api POC integration
Reviewed-against: docs/adr/
2026-03-18 13:25:43 -06:00

2.2 KiB
Raw Blame History

ADR-001: Bit-Set Permissions

Status: Accepted Date: 2026-03-18

Context

Applications need to represent and check whether a user holds a specific capability on a resource. Common approaches include: role strings (e.g. "admin", "editor"), permission string lists, or bit-set integers. Role strings require the application to know which capabilities each role implies, making capability checks indirect and requiring either a lookup table or a case statement everywhere. Permission string lists are flexible but expensive to check (linear scan) and verbose to store.

Decision

Permission is typed as int64 and represents a named bit position (062). Applications define their own constants using this type:

const (
    Read   rbac.Permission = 0
    Write  rbac.Permission = 1
    Delete rbac.Permission = 2
)

PermissionMask is also typed as int64 and holds the resolved OR-combination of granted permission bits. It is returned by PermissionProvider.ResolveMask and checked with PermissionMask.Has(p Permission) bool.

The upper bound is 62 (not 63) to keep the value within the positive range of a signed 64-bit integer, avoiding sign-bit ambiguity. Has and Grant both return false/no-op for out-of-range values (p < 0 || p >= 63).

Grant(p Permission) PermissionMask is provided for constructing masks in tests and in-memory provider implementations, returning a new mask with the bit set without mutating the receiver.

Consequences

  • Permission checks are O(1) bitwise operations — no map lookup, no string comparison.
  • A single int64 column stores up to 62 independent permission bits per user-resource pair in the database.
  • Applications must define their own named constants; this package does not enumerate domain permissions. This keeps rbac domain-agnostic.
  • The 62-bit limit is sufficient for all foreseeable use cases; if an application needs more than 62 orthogonal permissions on a single resource, a structural refactor (multiple resources or resource hierarchies) is the appropriate response.
  • PermissionProvider is the extension point for DB-backed resolution; the bit-set design does not constrain the storage schema.