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/
2.2 KiB
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 (0–62).
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
int64column 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
rbacdomain-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.
PermissionProvideris the extension point for DB-backed resolution; the bit-set design does not constrain the storage schema.