• v0.9.0 0864f031a1

    Release 0.9.0 Stable

    Rene Nochebuena released this 2026-03-18 13:27:28 -06:00 | 0 commits to main since this release

    v0.9.0

    code.nochebuena.dev/go/rbac

    Overview

    rbac provides the foundational identity and permission types for role-based access control in a Go service. It defines the Identity value type (the authenticated principal), the PermissionMask bit-set type (a resolved set of capabilities for a user on a resource), and the PermissionProvider interface (the contract for DB-backed permission resolution). Every other module that needs to carry or inspect an authenticated identity imports this package; it has no micro-lib dependencies of its own, only stdlib.

    What's Included

    • Permissionint64 type representing a named bit position (0–62) for a single capability
    • PermissionMaskint64 type representing a resolved bit-set of permissions
    • PermissionMask.Has(p Permission) bool — O(1) check whether a permission bit is set; returns false for out-of-range values
    • PermissionMask.Grant(p Permission) PermissionMask — returns a new mask with the given bit set; does not mutate the receiver
    • Identity — value type carrying UID, TenantID, DisplayName, and Email for an authenticated principal
    • NewIdentity(uid, displayName, email string) Identity — constructs an Identity from token authentication data; TenantID is left empty for later enrichment
    • Identity.WithTenant(id string) Identity — returns a copy of the Identity with TenantID set; does not mutate the receiver
    • SetInContext(ctx, Identity) context.Context — stores an Identity in a context using a private key
    • FromContext(ctx) (Identity, bool) — retrieves the Identity stored by SetInContext; returns zero value and false if absent
    • PermissionProvider interface — ResolveMask(ctx, uid, resource string) (PermissionMask, error) for DB-backed permission resolution

    Installation

    require code.nochebuena.dev/go/rbac v0.9.0
    

    Design Highlights

    • Permissions are bit positions packed into an int64 mask, giving O(1) Has checks and compact storage; applications define their own Permission constants — none are defined in this package (see docs/adr/ADR-001-bitset-permissions.md).
    • Identity is a value type (not a pointer) — it is copied on every enrichment call, preventing nil bugs and accidental mutation from concurrent middleware (see docs/adr/ADR-002-identity-value-type.md).
    • The rbac package owns the context key for Identity via an unexported authContextKey{} struct type, preventing collisions with keys from other packages (see docs/adr/ADR-003-identity-context-ownership.md).
    • TenantID is an optional enrichment field; PermissionProvider implementations retrieve the tenant from FromContext(ctx) when needed, so multi-tenancy does not require threading an extra parameter through every call site.

    Known Limitations & Edge Cases

    • There is no audit logging of permission checks. PermissionMask.Has is a pure in-memory operation — denials and grants are not recorded anywhere by the framework.
    • PermissionProvider.ResolveMask results are not cached by this package. Implementations that hit a database on every call must implement their own caching layer; a hot permission check path with no cache will produce one DB round-trip per check.
    • Permissions at bit positions 63 and above are silently ignored by Has and Grant — no error or panic is produced. Applications must keep their constants in the range 0–62.
    • Role-to-permission mapping tables are out of scope. The bit-set model is deliberately flat; if role inheritance or hierarchical permissions are required, callers must implement that logic in their PermissionProvider.
    • FromContext returns the zero-value Identity and false when no identity is present; callers must handle the ok == false case, especially in unauthenticated paths.

    v0.9.0 → v1.0.0 Roadmap

    • Validate the Identity value type and PermissionMask bit-set model under production multi-tenant workloads, confirming that the context key collision policy holds across all imported packages.
    • Consider providing a reference caching wrapper for PermissionProvider (e.g. TTL-based in-memory cache) to reduce DB pressure without requiring every application to implement its own.
    • Decide whether audit logging of permission checks (denials at minimum) belongs in this package, in a wrapper type, or in the application layer, and document the recommended pattern.
    • Validate that 63 permission bits is sufficient for known use cases; document the upper bound explicitly in the package godoc.
    • Achieve production validation of concurrent middleware enrichment via WithTenant and SetInContext.

    v0.9.0 rationale: The API is stable and intentional — designed through multiple architecture reviews and tested end-to-end via the todo-api POC (SQLite, RBAC, middleware stack, HTTP handlers). The module is not yet battle-tested in production for all edge cases, and the pre-1.0 designation preserves the option for minor API refinements based on real-world use.

    Downloads