Resolving names
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 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 defines that boundary; Numeric Scope 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, 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.
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.
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.
For where the local principals being resolved actually live, read The local store.