Skip to main content

Permissions

Permissions are the atomic units of authorization. Each permission is a branded nominal token -- a value that carries its identity at both the type level and runtime.

Why Branded Nominals?​

TypeScript's structural type system means two objects with the same shape are interchangeable. For authorization, this is dangerous: you don't want ReadUsers to accidentally satisfy a check for WriteUsers just because they're both strings.

Branded nominal tokens solve this by giving each permission a unique phantom type brand. Two permissions with different names are different types, caught at compile time.

Cross-Boundary Identity with Symbol.for()​

Permissions use Symbol.for() under the hood, which means:

  • Two createPermission("ReadUsers") calls in different modules produce the same runtime value
  • Identity comparison (===) works across module boundaries, bundle splits, and even separate packages
  • No global registry, no side effects -- Symbol.for() is the registry

Creating Permissions​

import { createPermission } from "@hex-di/guard";

const ReadUsers = createPermission("ReadUsers");
const WriteUsers = createPermission("WriteUsers");
const DeleteUsers = createPermission("DeleteUsers");

Each call returns a branded permission token. The string name is used for:

  • Symbol.for() identity
  • Serialization (policies reference permissions by name)
  • Debugging and error messages

Permission Groups​

When you have a set of related permissions, bundle them into a named group:

import { createPermissionGroup } from "@hex-di/guard";

const UserPermissions = createPermissionGroup("UserPermissions", {
read: ReadUsers,
write: WriteUsers,
delete: DeleteUsers,
});

// Access individual permissions
UserPermissions.read; // === ReadUsers
UserPermissions.write; // === WriteUsers
UserPermissions.delete; // === DeleteUsers

Groups are convenience wrappers -- they don't change how permissions work. Every permission in the group retains its individual identity. Groups are useful for:

  • Organizing permissions by domain (UserPermissions, OrderPermissions)
  • Passing related permissions as a bundle to role definitions
  • IDE autocompletion when working with large permission sets