# Authentication

---

# How authentication works

_Peios / Peios Security Fundamentals / Authentication_

> How a sign-in works on Peios — the conversation with the authority on /run/logon.sock, the roles involved, and why authentication and derivation are separate.

Every sign-in on Peios is a conversation with an **authority** over a Unix socket at `/run/logon.sock`. The protocol is PGSS Logon, and it is a conformance requirement rather than an implementation detail: a system that does not offer it, at that path, with these semantics, is not Peios.

Knowing its shape is worth the few minutes it takes. It explains why a login prompt behaves the way it does, why a wrong password and an unknown account are indistinguishable, and what you would have to build to add a new way of signing in.

## Three roles

**The authority** listens on the logon socket. It decides whether a sign-in succeeds, and it is the only thing on the system that mints tokens. On a stock Peios system that is `authd`. There is at most one on a running system — two would make "who authenticated this?" unanswerable.

**The client** connects and speaks on the principal's behalf. `login` is a client. So is any greeter, remote access daemon or kiosk agent you might write. A client renders the prompts it is asked to render, returns the answers, and installs the token it is given. It is not trusted, and nothing it sends is taken as fact.

**A principal source** knows who exists and verifies credentials. It asserts an identity and can do nothing else — it cannot mint a token, grant a privilege or create a session, because the protocol it speaks has no message for any of those. `lpsd` is the local one. See [principal sources](/peios/security-fundamentals/authentication/principal-sources.md).

## The conversation

A client opens a connection and sends one `LogonStart`, naming the principal, the kind of session it wants, and **the credential types it is able to collect**. The authority may then ask for credentials — as many rounds as the source needs — and each request carries prompts the client renders without interpreting. The conversation ends when the authority grants or denies.

The capability list is a statement about the client, not a demand. Declaring that you can collect a password does not mean one will be asked for; declaring that you can collect nothing says you are able to complete a sign-in that needs no interaction, and nothing else. An authority faced with that either completes the sign-in or denies it. That is the mechanism behind [passwordless accounts](/peios/using-peios/managing-local-principals/creating-accounts.md) and console autologon.

A connection carries exactly one conversation, so the connection is the conversation's identity. There is no correlation identifier to forge.

## Changing your own password

The logon socket carries one other kind of conversation: changing your own credential. It opens with a different message, which names nobody — the account is the one your token says you are — and it ends without a token, because a change mints nothing. Your current credential is asked for first, even though you are signed in. [`passwd`](/peios/using-peios/signing-in/the-passwd-command.md) is the client for it.

Because every principal needs to reach the socket for this, reaching it is not permission to originate a logon. The authority decides that per caller, from the caller's token.

## The authority is not a process factory

A grant carries a token as a file descriptor, a session identifier, and a profile. It carries nothing about what should happen next, because nothing about what happens next is the authority's concern.

The client installs the token itself and proceeds. `login` makes the token its own and then `exec`s your shell, rather than the authority forking anything. This keeps the most privileged process on the system out of the business of launching programs: `authd` never learns about terminals, environments or session leadership, and stays small enough to audit.

It also means a client can obtain a token for a purpose the authority never anticipated.

## Authentication and derivation are separate

Two acts, deliberately distinguished.

**Authentication** establishes that the principal is who they claim. Its output is an identity and nothing more.

**Derivation** constructs the token — which SIDs it carries, which privileges, at what integrity level, with what projected identifiers. Its inputs are the authenticated identity **and this machine's policy**.

The separation is what stops an identity established elsewhere from carrying entitlements onto this machine with it. A directory can tell this machine that you are a member of `Domain Admins`; whether that membership carries `SeLoadDriverPrivilege` here is answered here, every time, by the authority applying local policy. An authority never accepts a token, a privilege set or an integrity level from another party, whatever its trust level.

See [privileges](/peios/security-fundamentals/privileges/overview.md) for what that policy contains.

## What the authority does not trust

Nothing a client sends. Three consequences are worth knowing because you will see them from the outside:

**Peer identity comes from the socket.** The authority reads the connected peer's token from the kernel rather than believing anything in a message, because there is no field a client could put its identity in that it could not also lie in. `SO_PEERCRED` is not used: it returns the projected uid, and on Peios many principals project to the same number.

**The logon type is a proposal.** Only the caller knows whether a connection is an interactive shell or a batch command, so the caller says — and the authority constrains that against what the verified peer is permitted to request. See [logon types](/peios/security-fundamentals/logon-sessions/logon-types.md).

**The identifier is a claim** until authentication succeeds, and `tty` and `remote_host` are never more than that. A client that lies about its remote host is not prevented from doing so by this protocol.

## A wrong password and an unknown account look identical

An authority never distinguishes an unknown principal from a bad credential — not by denial code, not by the text it returns, and not by timing. There is one denial for both.

`lpsd` holds to the timing half by verifying an unknown name against a decoy: a real verifier over a password nobody knows, so both outcomes cost one full derivation. Returning early for a name it did not recognise would make "does this account exist?" answerable by anyone who can time a sign-in.

Account **existence** is not a secret, and Peios does not pretend otherwise — it is answered plainly on a different socket, `/run/ident.sock`, which is how `ls -l` turns an owner into a name. What the logon socket refuses to do is let you learn it by guessing credentials. See [resolving names](/peios/security-fundamentals/authentication/resolving-names.md).

## What the audit trail records

The caller is told only that authentication failed, but the audit trail may know more. Every sign-in leaves two events, one from each side of the conversation:

- **`authd.logon.attempted`**, from the authority, once per `LogonStart`, however the logon ends. It names the originator: the principal of the program that asked, as `source.token.sid`. It also records the logon type asked for, the principal source asked, and the remote address when the originator gave an IP address. A success adds the principal signed in (`subject.token.sid`), the new session and the token. A failure adds `outcome.reason`: the denial the originator was sent, such as `authentication-failed`, `logon-type-not-permitted` or `account-restricted`.
- **`lpsd.credential.verified`**, from the local principal source, when it checks a credential. It says what the wire must not. `outcome.reason` is `no-such-account`, `wrong-credential`, `disabled` or `not-permitted`, and `object.account.sid` names the account when one exists.

**The name typed is never recorded**, in either event. A name the authority could not match to an account is the caller's input, not an identity, and a record of it would turn the audit trail into a list of mistyped passwords and probed usernames. An account appears only by its SID.

`authd.logon.attempted` is essential, so the emission policy cannot switch it off. `lpsd.credential.verified` is standard and can be: a machine exposed to a network may be asked to check credentials far more often than it signs anybody in.

## Where to start

- [The `login` command](/peios/using-peios/signing-in/the-login-command.md) — the terminal client, and how a live image signs in without being asked anything.
- [The `passwd` command](/peios/using-peios/signing-in/the-passwd-command.md) — changing your own password.
- [Logon sessions](/peios/security-fundamentals/logon-sessions/overview.md) — the kernel object a successful sign-in creates.
- [Authorities and principal sources](/peios/security-fundamentals/authentication/principal-sources.md) — the accounts being signed in to.

The protocol is specified rather than merely documented: [PGSS §2](/peios/advanced-peios/pgss/logon/scope-and-roles.md) is Logon, and [PSPU §2](/peios/advanced-peios/pspu/principal-source-interface/scope-and-roles.md) is PSI, the interface principal sources speak. Read those if you are writing a client or a source of your own.

---

# Authorities and principal sources

_Peios / Peios Security Fundamentals / Authentication_

> Where the accounts on a Peios machine live — the authority that mints tokens, the sources that hold identity, and lpsd, the one that owns the local store.

Signing in on Peios involves three separate things, and it is worth being able to name them before you administer any of them.

**The authority** is the process that mints tokens. On a stock Peios system that is `authd`. It listens on `/run/logon.sock`, it holds the privilege to create tokens, and it is the only thing on the system that does. When you type a password at a login prompt, `login` is talking to `authd`.

**A principal source** is a process that knows who exists. It verifies credentials and answers the question "who is this?" — and nothing else. It cannot mint a token, cannot grant a privilege, and cannot create a session, because the protocol it speaks gives it no way to say any of those things.

**`lpsd`** is the local principal source: the one that holds this machine's own accounts. It is where `jack` lives, and it is what you are administering when you run `lps`.

## Why the split

It would be simpler to have one daemon that both checks passwords and mints tokens. The split exists because those two jobs have very different risk profiles.

Checking a credential means parsing input that came from outside — from a login prompt, a network connection, a smartcard reader. That is the code most likely to have a defect in it. Minting a token is the most privileged operation on the system.

Putting them in one process means a defect in the first becomes a compromise of the second. So `authd` mints and cannot verify; `lpsd` verifies and cannot mint. A completely compromised `lpsd` can lie about the accounts it holds, and that is the whole of what it can do — it cannot elevate anyone, cannot forge a token, and cannot claim identities that are not its to claim.

The division runs through what a source is even able to *say*. A source states **who someone is**: their SID, their memberships, their POSIX identifiers, where their session starts. It never states **how much this machine trusts them** — privileges and integrity levels are `authd`'s, decided from local policy, and there is no message with which a source could ask for one.

That local policy is a registry key, one record per principal, and it is where you go to change what an account may *do* as opposed to who it is: [assigning privileges](/peios/using-peios/managing-local-principals/assigning-privileges.md).

## How they find each other

`lpsd` connects **to** `authd`, not the other way round. That keeps `authd` — the process holding the token-minting privilege — free of any reason to open an outbound connection to anything.

At boot, `lpsd` starts after `authd`, connects to `/run/psi.sock`, registers itself, and only then reports itself ready. That ordering means anything that starts after `lpsd` — `login`, a greeter, a remote access daemon — finds a system that can genuinely authenticate, rather than one where the processes merely exist.

If you see `lpsd` failing to start, the usual causes are that `authd` is not running, or that this machine's configuration does not permit `lpsd` to register. The second is deliberate: a source must be explicitly allowed to assert identity, and an unconfigured machine permits none.

## More than one source

`lpsd` is the only source on a stock machine, but nothing about the design assumes it is alone. A directory-backed source can register alongside it, holding domain accounts while `lpsd` holds local ones, and `authd` routes each logon to whichever source owns the name.

Each source declares the SID namespace it is authoritative for, and `authd` confines it there. That is what stops a directory deciding who administers your machine, and equally what stops a local source handing out domain identities.

The same confinement applies to **numbers**. Sources assert relative POSIX identifiers; `authd` applies each source's assigned range and refuses out-of-range values. That keeps source-provided numbers clear of uid 0 and other sources' ranges. See [Numeric Scope](/peios/advanced-peios/pspu/principal-source-interface/numeric-scope.md#rebasing) for the rebasing and bounds rules.

## Seeing a principal, rather than a number

Signing in is one half. The other is that everything on the system can turn an identifier back into a name — `ls -l` showing an owner, `id` showing a group.

That goes to `authd` too, on a socket of its own: the authority resolves effective POSIX identifiers and applies the machine's source-search order. See [resolving names](/peios/security-fundamentals/authentication/resolving-names.md) for what lookup answers mean, and [Identity Lookup](/peios/advanced-peios/pgss/logon/identity-lookup.md#why-the-authority-answers-this) for the protocol boundary.

## Where to start

- [The local store](/peios/security-fundamentals/authentication/the-local-store.md) — what `lpsd` keeps, and where.
- [Resolving names](/peios/security-fundamentals/authentication/resolving-names.md) — how a name, a SID or a number becomes a principal.
- [Creating accounts](/peios/using-peios/managing-local-principals/creating-accounts.md) — the first one, and every one after.
- [The `lps` command](/peios/using-peios/managing-local-principals/lps-command.md) — the full reference.

The protocols themselves are specified rather than merely documented: [PGSS §2](/peios/advanced-peios/pgss/logon/scope-and-roles.md) is Logon, and [PSPU §2](/peios/advanced-peios/pspu/principal-source-interface/scope-and-roles.md) is PSI, the interface sources speak. Read those if you are writing a principal source of your own.

---

# The local store

_Peios / Peios Security Fundamentals / Authentication_

> What lpsd keeps on disk — the machine's domain SID, its principals, and the argon2id verifiers that stand in for their passwords — and why it is one file.

`lpsd` keeps everything it knows in one file:

```
/var/state/lpsd/principals
```

It is read once at startup and held in memory. It changes when an authorised account or group change is made, including [self-service account changes](/peios/using-peios/signing-in/overview.md#manage-your-own-account).

## What is in it

**The machine's domain SID.** Every local principal is numbered under it, and it is generated — from the kernel's random number generator — the first time `lpsd` runs on a machine that has no store.

That last point matters more than it sounds. Two machines built from the same image do **not** share a domain, so the security descriptors written on one do not grant access to the other's users. An image cannot ship a domain, because the domain does not exist until first boot.

The domain is always of the form `S-1-5-21-A-B-C`. That shape is structural — the store holds three numbers under a fixed prefix, so it cannot express a domain of any other kind. It cannot claim `S-1-5-32` (BUILTIN) or any well-known namespace, whatever is written into it.

**The principals.** For each: a relative identifier (RID), a Unix ID, the canonical spelling of the name, whether the account is enabled, a password verifier, the groups it belongs to, which of them is primary, a home directory, a shell, a display name, and any claims.

A principal's SID is the domain plus their RID. `jack` with RID 1000 in domain `S-1-5-21-A-B-C` is `S-1-5-21-A-B-C-1000`.

**RIDs start at 1000 and are never reused.** Not even after an account is deleted. That is deliberate and it is the property that makes deletion safe to offer at all: a reissued RID would give a new person the SID of an old one, silently inheriting every access the descriptors on this machine still grant them. It is the one identity mistake that cannot be undone by fixing the account afterwards.

**The local groups.** A group created here is an object: a RID from the same counter, a name, a Unix ID, and a description of what it is for, which anyone may read. Sharing the counter with principals is what stops a group ever colliding with a user's SID.

Well-known groups — `BUILTIN\Administrators`, `Everyone`, `Authenticated Users` — are **not** stored. Their SIDs are the same on every Peios machine, so a stored copy could only ever drift from the real one, and provisioning would freeze whatever the table said that day. `lpsd` resolves their names from a table in its own code.

## Unix IDs

Linux programs call `getuid` and `getgroups` and have never heard of a token, so every token carries POSIX numbers alongside the SIDs. Those numbers begin here.

**A principal's stored Unix ID is their RID**, and a group's is its RID. These numbers are **relative**: `authd` adds a base from the registry before the number reaches a token, so `jack` with RID 1000 stores 1000 and signs in as uid **1001000** when the base is 1,000,000.

The range is a boundary enforced by `authd`: an out-of-range relative ID is refused, so a source cannot number its principals as uid 0 or in another source's range. [Numeric Scope](/peios/advanced-peios/pspu/principal-source-interface/numeric-scope.md#rebasing) specifies the rebasing and bounds checks.

It also means moving a range is a registry edit rather than a rewrite: nothing stored here was ever absolute.

`lps list` and `lps show` display the **effective** number, with the base already added, because that is the one an operator will see everywhere else. The authority communicates the range at [registration](/peios/advanced-peios/pspu/principal-source-interface/registration.md#registered) for that purpose; `lpsd` must still assert relative identifiers.

At lookup time, `getpwuid` asks `authd` about the effective number; `authd` queries the relevant source in its relative namespace. See [resolving names](/peios/security-fundamentals/authentication/resolving-names.md).

> [!NOTE]
> A `-` where a uid should be means `authd` has assigned this machine no range at all, and every principal here will sign in as `nobody`. The `UnixIDBase` value on this source is missing or unusable.

## Profiles and claims

The **profile** — home directory, shell, display name — is not identity. No security descriptor names a home directory and no token carries one. It is stored here alongside the account, and it travels to `login` on the logon reply so a session can start without a second lookup.

**Claims** are named, typed attributes that conditional ACEs read: a descriptor can grant access to *anyone whose Department is Engineering* without naming principals individually. They are fixed on a token when it is minted, so a claim set now takes effect at the principal's next sign-in.

## What is not in it

**Passwords.** What is stored is a **verifier** — an argon2id hash — and a verifier is deliberately not password-equivalent. You cannot authenticate with it, and it cannot be used to answer a challenge.

That is the property that makes stealing the store meaningfully weaker than knowing the passwords. It is also why Peios does not use challenge-response schemes: those require storing something a response can be recomputed from, which is exactly what this avoids.

Each verifier carries the cost parameters it was created with, so raising them applies to passwords set afterwards while existing accounts keep working.

**Well-known group definitions.** The store defines the groups *this machine* creates, and records memberships of everything else as SIDs. `BUILTIN\Administrators` exists on every Peios machine whether or not this store mentions it; `lpsd` only records that `jack` is in it.

**Privileges and integrity levels.** What a principal may *do* is not stored here at all. `authd` decides it, from local policy keyed on the SIDs a token ends up carrying — a registry key rather than this file, so that it stays readable by an administrator and this file can stay readable by nobody. A principal source says who someone is; it has no way to say how much this machine trusts them. See [assigning privileges](/peios/using-peios/managing-local-principals/assigning-privileges.md).

## The descriptor

The store file is owned by `LocalSystem` and its DACL grants `LocalSystem` and nothing else — not even `BUILTIN\Administrators`.

That is narrower than almost anything else on the system, and it is on purpose. This is the machine's password material, and the list of principals entitled to read it should be as close to empty as the system permits. An administrator who genuinely needs the file can take ownership, which is an act that leaves a trail; a read granted by the DACL does not.

It is also why `lps` talks to `lpsd` over a socket rather than editing the file. A tool that wrote the file would need that descriptor widened to whatever the tool runs as, undoing the one thing it exists to do.

## A file, not a database

The store is serialised whole and replaced atomically. Its temporary inode
is created with a protected SYSTEM-only descriptor **before the first byte is
written**, flushed, then renamed over the old one. Stale temporary files are
unlinked and recreated exclusively; they are never truncated or followed.
At startup lpsd establishes a protected SYSTEM-only inheritable descriptor on
its state directory before loading or provisioning the store. The final file
has the same SYSTEM-only access policy, independent of parent inheritance.

A few hundred principals, rewritten when an authorised account or group change is made, is not a workload that needs a write-ahead log. And because the swap is a rename, a reader sees the old file or the new one and there is no third outcome — which means there is no recovery code, and code that does not exist cannot be wrong.

The file carries a checksum. Not to catch a half-written file, which atomic replacement makes impossible, but because a corrupted store that decoded *short* would present as an account quietly no longer existing.

## Its format, and upgrading

The file also carries a format number. `lpsd` reads the format it writes and the one before it: a store written before groups had descriptions loads with every description empty, and is written in the new format the next time anything changes. Nothing has to be done to upgrade it.

Going back is another matter. An older `lpsd` refuses a store in a format newer than it knows, and does not start, rather than guess at what it cannot read. So once a newer `lpsd` has changed the store, an older one cannot be put back in its place.

## Missing versus corrupt

These are treated as opposite outcomes, and the distinction is the most important behaviour in this page.

**No store at all** means an unprovisioned machine. `lpsd` generates a domain, writes an empty store, and carries on.

**A store that will not read**, damaged or in a format this `lpsd` doesn't know, is fatal. `lpsd` refuses to start.

Collapsing the two — "cannot read it, so make a new one" — would turn a flipped bit into every account on the machine silently ceasing to exist, and then reappearing under a *new domain* with different SIDs, orphaning every security descriptor that named them. Refusing to start is loud, reversible, and leaves the evidence intact.

If `lpsd` reports that the store is corrupt, do not delete it. Take a copy first: it is the only record of your machine's identity.

---

# Resolving names

_Peios / Peios Security Fundamentals / Authentication_

> How a name, a SID or a POSIX identifier becomes a principal — why only the authority can answer, the search order, and why "unavailable" is not "not found".

Every program that prints a name for a file's owner is asking the same question: *who is this?* On Peios that question goes to `authd`, over a socket of its own:

```
/run/ident.sock
```

Separate from `/run/logon.sock`, but the same standard — [PGSS §2](/peios/advanced-peios/pgss/logon/scope-and-roles.md) defines both. What separates them is who calls: a handful of things ever originate a logon, where **every process on the system** resolves names. A filesystem walk is millions of lookups, and it must not be able to fill the queue an administrator needs in order to sign in.

## Why the authority, and only the authority

The protocol assigns this role to the authority: sources assert **relative** POSIX identifiers and answer numeric queries in their own relative namespace, while applications ask the authority about **effective**, machine-wide identifiers. The [Identity Lookup contract](/peios/advanced-peios/pgss/logon/identity-lookup.md#why-the-authority-answers-this) defines that boundary; [Numeric Scope](/peios/advanced-peios/pspu/principal-source-interface/numeric-scope.md#the-authority-tells-the-source-its-range) explains how the authority applies a source's range even though the source knows it.

The same holds for names. A bare name may exist in more than one source, and which one wins is a property of the machine rather than of any source in it.

## One answer, systemwide

Whatever asks — a Peios tool, a Linux program through [`getpwnam`](/peios/advanced-peios/linux-compatibility/name-service-switch.md), a future graphical console — the question goes to the same place and gets the same answer.

There is deliberately no way to arrange otherwise. glibc's identity databases are patched to reach the authority and nothing else, there is no `/etc/passwd` behind them, and PAM does not exist on Peios at all. Every route by which another system lets identity come from somewhere the authority cannot see has been closed rather than configured.

That is worth more than it sounds. If two components resolved names separately they could disagree, and a program acting on one principal's behalf while its access is checked against another is a confused-deputy bug rather than a cosmetic inconsistency. It also means a name resolves identically at a logon and at an `ls -l`, because the same code answers both.

## The search order

A name nobody qualified is resolved by asking sources **in a configured order**, stopping at the first that answers.

The order is a value on each source's allowlist entry:

```
Machine\Software\Authd\Sources\lpsd
    SearchOrder    REG_DWORD    1000
```

Lower is consulted first. Sources that share a value are ordered by name. A source that sets nothing resolves at 1000, which leaves room to place a new source either side of the ones already there without renumbering them.

It is deliberately explicit. Resolving in the order sources happened to register would let a slow disk change which principal a name refers to.

> [!NOTE]
> A machine with one source has nothing to order, and can ignore this entirely.

## Warnings about shadowed names

If more than one source holds the same bare name, only the first owner in the
search order answers the logon. After delivering a successful logon reply,
`authd` asks later sources whether they also own that name. These are identity
queries with no credentials or authentication assertion attached.

Each confirmed duplicate produces a warning in authd's service log, naming
the entered name, its owning source and the later source. The warning does not
change the token or select a different principal. Administrators should check
whether an old local account is hiding a directory account, or whether a source
order change has made a different SID win for a familiar name.

An absent, refusing or unavailable source makes the check inconclusive, not a
failed logon. The success reply and normal logon audit record precede the
check. All the later sources share one normal five-second lookup deadline;
there is no extra query on a single-source machine
or when the owning source is last. If the owning registration has disappeared
or been replaced during authentication, authd skips the check rather than
assuming its old position still holds. Absence of a warning therefore does not
prove that a name is unique.

## Bare in, qualified out

You may look a principal up by a bare name. What comes back always carries the **SID** as well as the name.

So a tool can always tell which principal it got, compare two answers for identity, and record something unambiguous in a log. Ambiguity is permitted in what you ask; it is not permitted in what the authority answers.

### Reserved characters

A principal or group name may not contain `@`, `\`, `/`, `:` or `,`, may not begin or end with a space, and must be printable ASCII.

Each of those is a separator somewhere a name ends up — a path, a `passwd` record, a `group` member list — and the damage is done by whatever reads it later, so it cannot be prevented at the point of display. `@` and `\` are reserved for qualified names (`jack@local`), which do not exist yet: reserving them now is what stops an account literally called `jack@local` colliding with the syntax the day it arrives.

The ASCII restriction defers confusables and normalisation rather than getting them wrong. `jack` spelled with a Cyrillic `а` renders identically and is a different principal, and the same name in NFC and NFD is two byte sequences a comparison calls two people. Relaxing this later is safe; tightening it later would mean renaming accounts.

## Absent is not the same as missing

Three answers, and the difference between the last two is the one worth knowing:

| Answer | Means |
|---|---|
| **Found** | Here is the principal. |
| **Not found** | Every source that could have answered was asked, and there is no such principal. |
| **Unavailable** | A source that could have answered did not. |

**A source that is configured and not running does not simply drop out of the order.** If `lpsd` has crashed, `jack` resolves to *unavailable* — not to a directory's `jack` further down the order, which would be a different principal with a different SID, and every security descriptor granting the local one would stop applying to the person signing in under that name.

It also matters because *not found* is safe to remember and *unavailable* is not. A cache told "no such user" during an outage would keep saying so long after the outage ended.

> [!TIP]
> If names stop resolving and start reporting temporary failures, look for a principal source that is not running. `authd` logs which one it was waiting for.

## Groups you cannot list

Ask who is in a local group and you get its members. Ask who is in `Everyone` and you get nothing — and that is the correct answer rather than a limitation.

Membership comes in three kinds:

**Recorded.** Local groups, and the `BUILTIN` groups: `Administrators`, `Users` and `Guests`. A source holds the edges, so it can list them, and `authd` gathers a `BUILTIN` group's members from every source that may record them. (Note that being well-known has nothing to do with it — `Administrators` is well-known and perfectly enumerable.)

**Stapled.** `Everyone`, `Authenticated Users`. Nothing records who is in them; `authd` adds them to every token it mints. Membership is a rule, not data, so there is no list to produce.

**Session-scoped.** `Interactive`, `Remote Interactive`, `Network`, `Batch`, `Service`. These are not properties of an account at all, but of a *logon*: `jack` is in `Interactive` at the console and not in it over the network. No static answer exists even in principle, which is why they have no POSIX group id either.

The asymmetry that falls out is the useful one. *Which groups is this principal in* is cheap and always works. *Who is in this group* is best-effort, and a source is entitled to decline it — listing a directory group can mean listing an entire organisation.

## Every lookup is live

The authority holds no cache. Each lookup it receives is a fresh round trip to the source that holds the answer.

That is a deliberate first step rather than an oversight: a cache is invisible on both sides of the wire, so adding one later changes nothing about how any of this behaves. Sources already declare what one will need — whether they can push a change notification, and how long an answer may be held — so the contract is in place before the thing that uses it.

One thing does cache, and only within a single command. Turning a SID into a name for display — the owner column of `ls -l`, and the same rendering everywhere else in the userland — remembers what it has already asked **for the lifetime of that one process**, so listing a large directory owned by a handful of principals costs a handful of lookups rather than one per file.

It is scoped that way on purpose. Two separate commands share nothing, so an account renamed between them shows its new name on the very next one, and there is no stale answer to invalidate because there is nothing that outlives the command. This is what a Linux system already does for `getpwuid`, so `ls` behaves the way its users expect.

Names that are fixed by specification rather than held by any source — `Everyone`, `Local System`, `BUILTIN\Administrators` and the rest — are answered from a table built into the renderer and never asked about at all. There is nothing a source could add to them, and asking would mean a large listing querying the authority thousands of times about `Everyone`, and printing something different depending on whether the authority happened to be running.

Name service switch lookups — `getpwnam` and friends — are not cached anywhere. Each is its own connection and its own round trip.

## Asking from a program

A Linux program asks through `getpwnam` and friends, and needs nothing more. A Rust program that wants the answer itself — a SID's name, or every principal there is — can use the `libauthd-client` crate in the authd repository. The name service switch module, `ls` and the other userland tools, Principals Manager and the permissions editor all ask through it.

It keeps the three answers apart: *not found* comes back as no principal, and a failure says what it was. Its `transient` says whether asking again could help, which is true when `authd` didn't answer in time or a source that could have answered didn't. Each question has a connection of its own, unless the program holds one for a walk through a long listing, a page at a time.

## Where to go next

For how Linux programs reach this resolution through `getpwnam` and friends, read [Name service switch](/peios/advanced-peios/linux-compatibility/name-service-switch.md).

For where the local principals being resolved actually live, read [The local store](/peios/security-fundamentals/authentication/the-local-store.md).
