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 { PositionSchema } from '@objectstack/spec/identity';
import type { Position } from '@objectstack/spec/identity';

// Validate data
const result = PositionSchema.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).
protection{ lock: Enum<'none' | 'no-overlay' | 'no-delete' | 'full'>; reason: string; docsUrl?: string }optionalPackage author protection block — lock policy for this position.
_lockEnum<'none' | 'no-overlay' | 'no-delete' | 'full'>optionalItem-level lock — controls overlay & delete (ADR-0010).
_lockReasonstringoptionalHuman-readable reason shown when a write is refused by _lock.
_lockSourceEnum<'artifact' | 'package' | 'env-forced'>optionalLayer that set _lock (artifact | package | env-forced).
_provenanceEnum<'package' | 'org' | 'env-forced'>optionalOrigin of the item (package | org | env-forced).
_packageIdstringoptionalOwning package machine id.
_packageVersionstringoptionalOwning package version.
_lockDocsUrlstringoptionalOptional documentation link surfaced next to _lockReason.

On this page