These docs are under active development.
On this page
§3.6

POSIX Projection (uid/gid)

Every principal that can own or access a Linux-visible object projects to a single POSIX integer id. The projection exists only for Linux application and filesystem compatibility: uid/gid carry no authority in PeiosKACS decides access by SID (PSD-004). The id is the value Linux apps see via getuid()/stat() and the owner stored on Linux filesystems; the compatibility layer maps an inbound id back to a SID to apply KACS. Because ownership is stored as a SID in the security descriptor, the id is a cosmetic projection — the authoritative owner is always the SID.

§3.6.1 The unified id invariant

The id space is unified: an integer identifies at most one principal and is used as a uid xor a gid — never both, and never for two different principals. A single rid_counter (§3.1) makes every base + rid id unique across users and groups, and the well-known and allocated assignments are likewise disjoint. An implementation MUST NOT maintain separate uid and gid counters. The benefit is context-free reverse mapping: an integer resolves to one SID regardless of the slot it appeared in. (This matches a Samba autorid member's unified-xid model.) Peios therefore does not use Linux user-private-groups: a user's primary gid is its real primary group's id, not a copy of its uid.

§3.6.2 Three allocators, one address space

The bands of §9 are a shared allocation contract. Three allocators assign ids within it, each authoritative for, and storing, its own part; they never collide because each draws only from its assigned bands:

  • lpsd — local accounts and groups. lpsd assigns the id in the local band at creation and stores it on the principal record (posix_uid/posix_gid, §3.5). lpsd is the source of truth for local principals and returns their ids in the resolved principal (§4).
  • adpsd / AD (deferred — §8) — domain principals. The directory is authoritative (an explicit uidNumber-style attribute, or an autorid-style per-domain computation); adpsd returns the id.
  • authd — the ephemeral / sourceless SIDs that are not directory accounts: service SIDs (S-1-5-80-…), virtual identities, and orphan/foreign SIDs. authd allocates these next-free in their bands and persists them in its idmap store (§2.1). SIDs on the no-id list (§9) are not object-owning principals and are never allocated an id — they are omitted from the projection entirely. authd also caches the mappings the directories report, so its idmap is the single point the compatibility layer queries to reverse-resolve any id.
  • well-known SIDs are computed by arithmetic (§9), need no storage, and are seeded into the idmap on demand.

Assignment happens at a SID's first projection (commonly token-mint, also file-ownership assignment), performed by the owning allocator, and is frozen thereafter. Forward (SID → id) and reverse (id → SID) are both served from authd's idmap; on a miss in a computable band the id is derived arithmetically and backfilled.

authd's idmap is low-criticality: the cached directory mappings rebuild from the directories, and a lost ephemeral assignment only causes a service's files to show a different cosmetic uid (the SID-native owner is unchanged). It holds no accounts and no secrets (§2.1).

Reverse-resolution is not cosmetic on the setuid-bit transition path (PSD-004 — a setuid binary's id → SID lookup decides which principal it becomes): for any id that can be a setuid target, the mapping MUST be authoritative and stable. The 0xFFFFFFFF wire sentinel (§6.4) means "the source assigns no id"; authd MUST resolve it to a real allocated id before minting — 0xFFFFFFFF MUST NOT appear in a token spec or be treated as a literal uid/gid (it equals (uid_t)-1, the "no-change" sentinel).

§3.6.3 Bands and reverse mapping

The full band layout is §9. Reverse-resolution is a range check:

Range Resolves to Allocator
0; 1–999; 1000–1999; 65200–65299; 65530; 65534 well-known / fixed sentinels (computed — §9) computed
1,000,000 – 4,999,999 fallback / service / package / capability (idmap lookup) authd
5,000,000 – 9,999,999 local: rid = id − 5,000,000 lpsd
≥ 10,000,000 domain: dn = id ÷ 5,000,000 − 1, rid = id mod 5,000,000, then dn → domain SID adpsd

All ids MUST be below 2³¹ (2,147,483,648). Within a namespace, rid MUST be < 5,000,000 (the slice width). 65534 is the overflow/unmapped sentinel (S-1-0-0); 65535 is reserved. Any id with no assignment — a reserved hole (e.g. 18) or an unallocated gap — also reverse-maps to S-1-0-0. A namespace whose slice would reach 2³¹ MUST be refused at join (domain support is deferred — §8).

A principal's stored projection is a single id. Supplementary gids are not stored — they are the projection of the principal's expanded group set (§3.2, §5.2) to gids at token-mint time.

ⓘ Informative
§9 enumerates the complete well-known band assignments (the named S-1-5-N, the S-1-5-32 aliases, the S-1-2/S-1-1/S-1-0 SIDs, and the service/virtual-host family roots) and the SID families that receive no id — integrity labels (S-1-16-*), Creator placeholders (S-1-3-*), logon-session SIDs (S-1-5-5-X-Y), authorization-context markers (S-1-5-64-*, S-1-18-*, S-1-5-1000), and bare authority/domain prefixes — none of which are object-owning principals.