ObjectStackObjectStack

Position

Position protocol schemas

Position Schema — the flat capability-distribution group (ADR-0090 D3).

A position (岗位, "job role" in NetSuite/Workday terms) is a **named,

assignable bundle of permission sets**: users hold positions

(sys_user_position), positions bind permission sets

(sys_position_permission_set), and a user's capability is the union of

every set reached that way plus direct grants.

Positions are deliberately flat — no parent, no hierarchy. The

visibility hierarchy lives on the business-unit tree (sys_business_unit,

ADR-0057 D2) and the manager chain (sys_user.manager_id); re-adding a

second tree here is the mistake ADR-0057 D5 retired and ADR-0090 D3

finalizes.

VOCABULARY (ADR-0090 D3): the word "role" is reserved-forbidden across the

platform — capability = permission_set, distribution = position,

hierarchy = business_unit. The sole exception is better-auth's internal

sys_member.role (org-membership tier), projected as

org_membership_level.

NAMING CONVENTION:

Position names MUST be lowercase snake_case to prevent security issues.

@example Good position names

  • 'sales_manager'

  • 'ceo'

  • 'region_east_vp'

  • 'engineering_lead'

@example Bad position names (will be rejected)

  • 'SalesManager' (camelCase)

  • 'CEO' (uppercase)

  • 'Region East VP' (spaces and uppercase)

Source: packages/spec/src/identity/position.zod.ts

TypeScript Usage

import { Position } from '@objectstack/spec/identity';
import type { Position } from '@objectstack/spec/identity';

// Validate data
const result = Position.parse(data);

Position

Properties

PropertyTypeRequiredDescription
namestringUnique position name (lowercase snake_case)
labelstringDisplay label (e.g. VP of Sales)
descriptionstringoptional
delegatablebooleanADR-0091 D3: holders may self-service delegate this position, time-boxed (default false).

On this page