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
| Property | Type | Required | Description |
|---|---|---|---|
| name | string | ✅ | Unique position name (lowercase snake_case) |
| label | string | ✅ | Display label (e.g. VP of Sales) |
| description | string | optional | |
| delegatable | boolean | ✅ | ADR-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 } | optional | Package author protection block — lock policy for this position. |
| _lock | Enum<'none' | 'no-overlay' | 'no-delete' | 'full'> | optional | Item-level lock — controls overlay & delete (ADR-0010). |
| _lockReason | string | optional | Human-readable reason shown when a write is refused by _lock. |
| _lockSource | Enum<'artifact' | 'package' | 'env-forced'> | optional | Layer that set _lock (artifact | package | env-forced). |
| _provenance | Enum<'package' | 'org' | 'env-forced'> | optional | Origin of the item (package | org | env-forced). |
| _packageId | string | optional | Owning package machine id. |
| _packageVersion | string | optional | Owning package version. |
| _lockDocsUrl | string | optional | Optional documentation link surfaced next to _lockReason. |