@vperms/core
@vperms/core is the adapter-independent engine: types, pattern matching,
reconciliation and resolution. It has no runtime dependencies.
Adapter
Section titled “Adapter”VeguiPermsAdapter
Section titled “VeguiPermsAdapter”Abstract persistence contract. Adapters perform no validation, resolution, inheritance or evaluation.
abstract class VeguiPermsAdapter { abstract findSubject( workspaceId: string, subjectId: SubjectId, ): Promise<Subject | null>;
abstract saveSubject(workspaceId: string, subject: Subject): Promise<Subject>;
abstract deleteSubject( workspaceId: string, subjectId: SubjectId, ): Promise<boolean>;
abstract findSubjectGrants( workspaceId: string, subjectId: SubjectId, ): Promise<PermissionGrant[]>;
abstract grantPermission( workspaceId: string, subjectId: SubjectId, permission: string, value: boolean, ): Promise<PermissionGrant>;
abstract ungrantPermission( workspaceId: string, subjectId: SubjectId, permission: string, ): Promise<boolean>;}saveSubjectupserts the subject record.grantPermissionmust upsert: setting the same permission twice replaces the value.- Unknown subjects return
null/falserather than throwing.
VeguiPermsMemoryAdapter
Section titled “VeguiPermsMemoryAdapter”Map-backed reference implementation. No options, no migrate(). Data is lost
when the process restarts.
type SubjectId = string;
enum SubjectType { User = "user", Service = "service", Group = "group", Anon = "anon",}
interface Subject { id: SubjectId; type: SubjectType; parents: SubjectId[]; // "!id" negates a default parent}
interface Principal { getSubjectId(): SubjectId;}
interface DefaultParents { global?: SubjectId[]; byType?: Partial<Record<SubjectType, SubjectId[]>>;}
interface PermissionGrant { permission: string; value: boolean; subjectId: SubjectId; workspaceId: string;}
interface ResolvedPermissionGrant extends PermissionGrant { depth: number; layer: number;}Matching
Section titled “Matching”function matchesPattern(pattern: string, permission: string): boolean;function matchPermission( grants: PermissionGrant[], permission: string,): boolean | null;function permissionSpecificity(pattern: string): number;function exactSegments(pattern: string): number;matchesPatterncompares dot-separated segments;*matches a single inner segment and a trailing*matches any remaining segments, including none.matchPermissionsorts candidates withcompareGrantsand returns the first match, ornullwhen nothing matches.permissionSpecificity: exact segment+2, inner*+1, trailing*+0.
Reconciliation
Section titled “Reconciliation”function compareGrants( a: ResolvedPermissionGrant, b: ResolvedPermissionGrant,): number;function depthOf(grant: PermissionGrant): number;function layerOf(grant: PermissionGrant): number;compareGrants is the single source of precedence, in order:
- layer ascending
- depth ascending
- specificity descending
- exact segments descending
- deny before allow
- pattern ascending
- subject id ascending
The last two make ordering deterministic and independent of insertion order.
depthOf/layerOf return 0 for grants without resolution metadata.
Layers
Section titled “Layers”const EXPLICIT_PARENT_LAYER = 0;const TYPE_DEFAULT_PARENT_LAYER = 1;const GLOBAL_DEFAULT_PARENT_LAYER = 2;const BUILTIN_PERMISSION_LAYER = 3;Inheritance rules
Section titled “Inheritance rules”const VIRTUAL_PARENT_NEGATION = "!";
function splitParents(parents: SubjectId[]): { explicit: SubjectId[]; excluded: Set<SubjectId>;};
function effectiveParentLayers( subject: Subject, defaults?: DefaultParents,): { explicit: SubjectId[]; byType: SubjectId[]; global: SubjectId[] };effectiveParentLayers merges the explicit subject.parents with the type and
global defaults, removing duplicates across layers. Negation only excludes
virtual (default) parents; explicit parents are always kept.
Inheritance resolution
Section titled “Inheritance resolution”interface ResolveInheritedPermissionsOptions { defaultParents?: DefaultParents;}
function resolveInheritedPermissions( adapter: VeguiPermsAdapter, workspaceId: string, subject: Subject, options?: ResolveInheritedPermissionsOptions,): Promise<ResolvedPermissionGrant[]>;Walks parents highest-priority-first (explicit, then type defaults, then global
defaults; closer depth first). A visited set prevents infinite cycles. A
negation directive on the root subject propagates through the whole walk.
Default-reached parents contribute their explicit grants plus the applicable
defaults, tagged with max(layer, defaultLayer). The result is sorted with
compareGrants.
Subject resolution
Section titled “Subject resolution”interface ResolvedPermission { permission: string; value: boolean; weight: number;}
interface ResolvedSubject { id: SubjectId; type: SubjectType; parents: SubjectId[]; permissions: ResolvedPermission[];}
function resolveSubjectPermissions( adapter: VeguiPermsAdapter, workspaceId: string, subject: Subject, options?: ResolveInheritedPermissionsOptions,): Promise<ResolvedSubject>;
function canResolved( permissions: ResolvedPermission[], permission: string,): boolean;Merges direct grants (depth 0, layer 0) with inherited grants and a
synthesized built-in grant for SELF_PERMISSIONS_PERMISSION (value true,
depth 0, layer 3). It sorts, dedupes by permission keeping the first
(highest-priority) occurrence and assigns descending weights (total - index). canResolved returns the value of the highest-weight matching
candidate, or false.
Permissions about permissions
Section titled “Permissions about permissions”const SELF_PERMISSIONS_PERMISSION = "vperms.subject.me.permissions";
function subjectPermissionsPermission(subjectId: SubjectId): string;// "vperms.subject.<id>.permissions"These gate the permissions export endpoint. The built-in self grant has the lowest priority and can be overridden by an explicit deny.