# Manage local accounts and groups

---

# Manage local accounts and groups

_Peios / Using Peios / Manage local accounts and groups_

> Create and maintain local users and groups, choose credential policy, and inspect the privileges assigned by this machine.

Local accounts and groups are held by `lpsd`, the local principal source.
Use [Principals Manager](/peios/using-peios/managing-local-principals/principals-manager.md)
on the desktop or the [lps command](/peios/using-peios/managing-local-principals/lps-command.md)
in a terminal to manage them. The guides distinguish changes you can make to
your own account from administrator operations.

## Choose a task

- **Create an account:** [Creating accounts](/peios/using-peios/managing-local-principals/creating-accounts.md)
  covers credentials, membership and the different first-account provisioning
  routes. The desktop equivalent is [Making a user](/peios/using-peios/managing-local-principals/principals-manager.md#making-a-user).
- **Change an account or its sign-in policy:** [Changing a user](/peios/using-peios/managing-local-principals/principals-manager.md#changing-a-user)
  or the [lps reference](/peios/using-peios/managing-local-principals/lps-command.md).
  A password being set does not by itself require that password at sign-in.
- **Create a group or change membership:** [Groups in Principals Manager](/peios/using-peios/managing-local-principals/principals-manager.md#groups)
  or [Grouping people together](/peios/using-peios/managing-local-principals/creating-accounts.md#grouping-people-together).
- **Inspect or change privileges and integrity policy:** [Assigning privileges](/peios/using-peios/managing-local-principals/assigning-privileges.md).
  This is the machine's local policy, separate from the account's identity.
  A policy change affects newly minted sign-in tokens, not existing tokens.
- **Change your own password, SSH keys or display name:** [Sign in](/peios/using-peios/signing-in/overview.md#manage-your-own-account)
  links the self-service procedures.

## Keep a usable administrator route

Before changing an administrator's credentials, membership or sign-in access,
keep a working administrator session and verify the replacement route. The
[last-administrator guard](/peios/using-peios/managing-local-principals/creating-accounts.md#the-last-administrator-guard)
checks account state; it does not prove that you can actually sign in through a
usable route. SSH configuration changes can end every SSH session, so follow
the [separate-access precautions](/peios/using-peios/signing-in/signing-in-over-ssh.md) there.

Read [Disabling rather than deleting](/peios/using-peios/managing-local-principals/creating-accounts.md#disabling-rather-than-deleting)
before removing an account. Recreating a name does not restore the old SID or
the access granted to it.

## Understand the model when needed

Security Fundamentals explains [authorities and principal sources](/peios/security-fundamentals/authentication/principal-sources.md),
[local identity storage](/peios/security-fundamentals/authentication/the-local-store.md),
[name resolution](/peios/security-fundamentals/authentication/resolving-names.md) and
[privileges](/peios/security-fundamentals/privileges/overview.md). Tool authors and administrators
investigating a refused request can consult the
[local principal interfaces](/peios/advanced-peios/local-principal-interfaces/the-admin-socket.md)
in the technical reference.

---

# Creating accounts

_Peios / Using Peios / Manage local accounts and groups_

> How the first account on a machine comes to exist, how to create the ones after it, and what happens on an image that ships a development account.

## Creating an account

Run `lps add` with no arguments and it asks for what it needs:

```
$ lps add
Name: alice
Full name [optional]: Alice Chen
Additional groups [comma-separated, optional]: Administrators
Primary group [the daemon's default]:
Home directory [the daemon's default]:
Shell [the daemon's default]:
Password for alice:
Again:

  name           alice
  full name      Alice Chen
  primary group  (the daemon's default)
  home           (the daemon's default)
  shell          (the daemon's default)
  groups         Administrators

Create this principal? [Y/n]:
created alice with RID 1002
```

An empty answer takes the default, and nothing is sent until you confirm. Or say it all on one line:

```
$ lps add alice --group Administrators
```

Anything you supply is not asked for, so either style works and they mix freely.

Groups are named however you like — `Administrators`, a local group's name, or a literal SID such as `S-1-5-32-544`. `lpsd` resolves it.

You need to be an administrator to do this. See [the `lps` command](/peios/using-peios/managing-local-principals/lps-command.md) for the full surface.

### What a new account gets

| | |
|---|---|
| RID | The next one. Never chosen, never reused. |
| uid | The RID, plus this machine's base — so RID 1000 signs in as 1001000. |
| Primary group | `Authenticated Users`, unless you say otherwise. |
| Home | `/home/<name>` |
| Shell | `/bin/sh` |

**No per-user group is created.** That Linux convention exists so a file's *group* ownership means something, and under KACS it means nothing — access is decided by the token, not by the mode bits. A group per user would be ceremony with a RID attached.

**The home directory is not created here.** `lps` records the path and nothing more. The directory appears at the principal's **first sign-in**, made by the authority when it grants the logon — and only when the account has a home recorded, which is why a service principal never grows one.

The shipped authd service explicitly retains `SeRestorePrivilege` so it can
assign the new directory's owner and group to that principal. Removing it
from `RequiredPrivileges` can leave a newly created home accessible only to
SYSTEM and Administrators, preventing an ordinary user's session from
starting there. Authd reports this failure and leaves existing directories
alone; an administrator must repair their descriptors deliberately.

It is created owned by the principal, with a protected DACL: full control for its owner, for SYSTEM and for Administrators, and nothing for anybody else. Protected matters — without it the root's inheritable "Everyone may read" ACE would apply and every account could read every other account's files.

A principal whose home could not be created still signs in, starting in `/`, and `login` says so. A directory is not worth failing a logon over.

An existing directory is left exactly as it is, and one owned by somebody else is reported rather than taken over. So pointing a new account's home at another principal's directory does not hand it over — it produces a warning in the log and an account that starts somewhere it does not own.

## Accounts with no password

`lps add <name> --no-password` creates a principal whose credential policy is `none`. It signs in without being asked for anything where the client and authority permit credential-free sign-in.

The sign-in is otherwise completely ordinary. `lpsd` still vouches for the principal, `authd` still mints the token and still applies this machine's privilege and integrity policy to it, and the session that results is indistinguishable from one reached by typing a password. Nothing is bypassed; there is simply nothing to collect.

**It is a property of the account.** Nothing ties it to a particular terminal: any client that permits credential-free sign-in can use it, subject to the account's logon-type restrictions and the authority's policy for that client. [SSH](/peios/using-peios/signing-in/signing-in-over-ssh.md) does not permit it; SSH requires an actual password or an enrolled key and a policy that allows that credential. A passwordless account enables console autologon, but do not assume that its use is limited to that console.

An empty password is not the same thing and is refused. See [the `lps` command](/peios/using-peios/managing-local-principals/lps-command.md).

## Make the first account an administrator

An ordinary account is usable on a stock image. The descriptor the system tree ships with grants `Everyone` read and execute — and on Peios execute is also traverse — so any principal can list directories, read the system tree and run what is in it. What it cannot do is *write* there. A non-administrator writes only where something grants it: its own home directory, which is created at first sign-in with a protected DACL admitting the owner, `LocalSystem` and `BUILTIN\Administrators` and nobody else, and `/tmp`, where anyone may create files and only their owner may remove them. See [Installing to disk](/peios/using-peios/disks-and-filesystems/installing-to-disk.md) for the root descriptor itself.

What an ordinary account cannot do is administer the machine. `lps` refuses it, so it cannot create the next account; it cannot mount a filesystem, install a package or change a service definition either. The first account is therefore an administrator because nothing else on the machine can make one, and it stays one because `lps` will not leave the machine without an enabled administrator (see *The last-administrator guard* below). The accounts after it need `Administrators` only if they are going to administer.

## Where the first account comes from

A machine with no store provisions one at first boot, and it is **empty** — `lpsd` generates the domain, because a source with no domain can answer nothing, but it does not invent accounts.

So something has to create the first one, and there is a chicken-and-egg problem: `lps` talks to `lpsd`, so `lpsd` must already be running, which means this cannot be done by an early-boot script. Autorun scripts run before any service has started.

The provisioning path depends on the edition. An installed edition can enable first-boot setup; a development image can instead ship a **oneshot service**, ordered after `lpsd`, that runs `lps` to create a known account. An image with no first-account provisioning path needs an already-authorised way to create the first account.

### On an installed edition with first-boot setup

[Complete first-boot setup](/peios/using-peios/disks-and-filesystems/first-boot-setup.md) creates the first administrator after installation. Use the console form, or the browser form when the image includes `oobe-gxwi`, then verify sign-in with the new account. Use `lps add` as that administrator for further accounts.

This path is enabled only when the edition includes `dev.peios.oobe` and opts into its `oobe-service` seed through `install_autoapply`. The `oobed` service runs as SYSTEM to create the account; the user-facing form does not need an existing administrator sign-in. Do not assume every custom image includes or enables it. The development-account seed below is a separate provisioning choice.

### On a development image

Images built for development ship two things: a script that creates a known account, and a registry seed defining the oneshot service that runs it. If your image has them, it boots with an account already present, and `lps list` will show it.

**On a live image that account has no credential at all**, and it is an administrator. The script creates it with `lps add peios --group Administrators --no-password`, so anyone who reaches a prompt that permits credential-free sign-in on that machine can become it — and the console signs in as it automatically, without asking anything.

That is deliberate rather than an oversight. A live ISO is an unauthenticated medium: anyone holding it can boot it and read everything on it, so a password would be a formality rather than a boundary, and one printed in the image at that. What it is *not* is a posture to carry anywhere else.

Give it a password the moment the machine becomes something you care about, then say it signs in with that password. The two together turn it into an ordinary account and stop the console signing in on its own:

```
$ lps password peios
New password for peios:
Again:
set the password for peios
$ lps policy peios password
credential policy updated
```

The password alone changes nothing: until `lps policy` says otherwise, the account still signs in with nothing. Set the password first, since a policy of `password` is refused for the last administrator while they have none.

Better still, build an image without the development-account seed, with first-boot setup or another deliberate account-provisioning path. Omitting the development seed alone does not create an account; see *On an image with no account provisioning* below.

### On an image with no account provisioning

On a fresh image with no accounts, no enabled first-boot setup and no account-provisioning seed, `lpsd` starts, provisions an empty store, and logs that no principals exist and no logon can succeed until one is created. The login prompt will appear and nothing will satisfy it.

That is the correct behaviour rather than a fault, but it does mean you need a way in. Create the first account from a console session that is already `SYSTEM` — `lps` accepts `LocalSystem` as well as administrators, precisely for this case.

## Disabling rather than deleting

```
$ lps disable guest
disabled guest
```

A disabled account keeps its RID, its SID, and its group memberships. It cannot start a new sign-in. Re-enable it with `lps enable`.

Disabling an account, changing its password or removing a key does not revoke already issued tokens or end its existing sessions. Use [Task Manager's sign-out action](/peios/using-peios/threads-and-processes/task-manager.md#signing-someone-out) when existing sessions must end. Signing out ends processes and can lose unsaved work; [session lifecycle](/peios/security-fundamentals/logon-sessions/lifecycle.md) explains its permissions and limits, including token references held outside those processes.

Prefer this to `lps remove`. A removed principal's SID keeps appearing in the security descriptors of everything they owned, and because RIDs are never reused nothing will ever hold that SID again — the files become owned by a principal that no longer resolves.

## The last-administrator guard

This is a guard on the local account store, not a guarantee that a usable sign-in path is reachable.

`lps` refuses to remove the last enabled administrator, to disable them, or to take `Administrators` away from them. It refuses as firmly to leave them no way to sign in: no kind of sign-in at the machine, remotely or over the network (`lps logon-types`), or nothing to sign in with (`lps policy` and `lps key remove`).

```
$ lps disable jack
lps: jack is the only principal who can administer this machine; disabling them would leave no way back in
```

The check counts only *enabled* administrators, so a disabled standby account does not license removing the working one.

The guard does not check that a console, remote service or network route is available. After changing credentials or sign-in policy, inspect the account with `lps show <name>` and verify a fresh sign-in through the intended path before closing your working administrator session. A stored credential and a permitted logon type are not evidence that the whole path works.

This guard exists because there is currently no offline repair. `lps` reaches the store only through `lpsd`, and `lpsd` only authenticates — so a machine with no enabled administrator has no way back short of editing its disk from another system.

## Grouping people together

For anything beyond the built-in groups, create one:

```
$ lps group create developers
created the group developers with RID 1001
$ lps group add alice developers
added alice to developers
```

A local group is a real object with its own SID and gid, so it can be named in a security descriptor and it shows up in `getgroups`. Deleting one is refused while anybody is still in it.

## Changing your own password

Run [`passwd`](/peios/using-peios/signing-in/the-passwd-command.md). It asks for your current password and then the new one:

```
$ passwd
Changing the password for alice
Current password:
New password:
Retype new password:
password changed
```

It goes over PGSS Logon rather than through `lps`, so it works the same way whichever source holds your account — a domain principal changes their password exactly as a local one does. `lps password` is the other operation: an administrator setting somebody else's.

An account created with `--no-password` has no current password to prove, so it cannot give itself one. An administrator sets one with `lps password`, and `lps policy <name> password` to have it used, or with Principals Manager's **Set password**, which does both.

## Where to go next

For the full command surface — every flag, the group and claim subcommands, and the exit statuses — read [The `lps` command](/peios/using-peios/managing-local-principals/lps-command.md).

For what the store holds and why RIDs are never reused, read [The local store](/peios/security-fundamentals/authentication/the-local-store.md).

---

# The lps command

_Peios / Using Peios / Manage local accounts and groups_

> The lps command administers the local principal store — principals, passwords, credential policies and SSH keys, groups and memberships, profiles, and claims.

`lps` administers the **local principal store** — this machine's own accounts and groups, held by `lpsd`.

```
lps subcommand [arguments]
```

It is a client and nothing more. It holds no state, opens no store, and has no privilege of its own: every command is a request over `/run/lpsd/admin.sock` that `lpsd` decides whether to honour. `lpsd` must be running.

You must be a member of `BUILTIN\Administrators` — enabled, not deny-only — or be `LocalSystem`.

## Naming a group

Anywhere `lps` takes a group, you may write it three ways:

```
lps group add jack Administrators              # a well-known name
lps group add jack BUILTIN\\Administrators      # the same, qualified
lps group add jack developers                  # a local group
lps group add jack S-1-5-32-544                # a literal SID
```

Names are matched case-insensitively, and **`lpsd` resolves them, not `lps`**. The tool carries no copy of the well-known table and does not know this machine's domain — a second copy of the machine's identity scheme would be free to disagree with the first.

A well-known name always wins over a local group of the same name, which is why you cannot create a local group called `Administrators`.

## Inspecting

### `lps list`

Every principal, with RID, uid, state, and how many groups each is in.

```
$ lps list
NAME     RID       UID  STATE     GROUPS
jack    1000   1001000  enabled   1
guest   1001   1001001  disabled  0
```

A `-` in the UID column means nothing numbers that principal — `authd` has assigned this machine no identifier range, and they will sign in as `nobody`. See [the local store](/peios/security-fundamentals/authentication/the-local-store.md).

### `lps show <name>`

One principal in full.

```
$ lps show jack
name           jack
display name   Jack Palfrey
rid            1000
sid            S-1-5-21-2847362817-1094533892-3310298447-1000
uid            1001000
state          enabled
primary group  Authenticated Users [S-1-5-11]
home           /home/jack
shell          /bin/sh
logon types    default (interactive, remote-interactive, network, network-cleartext, batch, new-credentials)
credential     password
groups         Administrators [S-1-5-32-544]
               developers [S-1-5-21-2847362817-1094533892-3310298447-1001] (gid 1001001)
claims         Department (string) = "Engineering"
```

`logon types` are the kinds of sign-in the principal may be used for (see `lps logon-types` below); `default` means none were chosen, and the machine's default applies. `credential` is what it signs in with: `password`, `key` (an SSH key), `either`, `none` (nothing is asked for), or `denied`.

Groups show a name where this machine knows one, and always show the SID. The two are kept side by side deliberately: the name is what you recognise, the SID is what a security descriptor actually holds, and the moment they disagree is exactly when you need to see both.

Only *local* groups show a gid. `lpsd` numbers the groups it owns and nothing else — `Administrators` and `Authenticated Users` are numbered by `authd`, from a table below every source's range, so `lpsd` genuinely does not know their gid and does not guess. To see the numbers a running session actually holds, read `Groups:` in `/proc/self/status`.

### `lps domain`

This machine's domain SID — the namespace every local principal and local group is numbered under.

```
$ lps domain
S-1-5-21-2847362817-1094533892-3310298447
```

Generated at first boot and unique to this machine.

## Creating and removing principals

### `lps add [name] [options]`

Creates a principal. Run with no arguments, it asks for what it needs:

```
$ lps add
Name: alice
Full name [optional]: Alice Chen
Additional groups [comma-separated, optional]: Administrators, developers
Primary group [the daemon's default]:
Home directory [the daemon's default]:
Shell [the daemon's default]:
Password for alice:
Again:

  name           alice
  full name      Alice Chen
  primary group  (the daemon's default)
  home           (the daemon's default)
  shell          (the daemon's default)
  groups         Administrators, developers

Create this principal? [Y/n]:
created alice with RID 1002
```

Any option you supply is not asked for, so scripted invocations keep working unchanged:

| Option | Effect |
|---|---|
| `--group <group>` | A membership. Repeatable. |
| `--primary-group <group>` | The group that becomes the POSIX gid. |
| `--home <path>` | Home directory. Must be absolute. |
| `--shell <path>` | Login shell. Must be absolute. |
| `--display-name <text>` | A human's name for a human to read. |
| `--disabled` | Create it unable to sign in. |
| `--no-password` | Create a principal that authenticates with no credential at all. See below. |
| `--service` | Create a principal that exists to run a service, and can do nothing else. See below. |
| `--no-prompt` | Fail rather than ask for anything missing. |

Nothing is sent until you confirm, so answering `n` creates nothing. Everything is sent in one request, so a principal is created whole or not at all: if `lpsd` refuses the shell, say, no principal is made.

**`lps` never prompts when standard input is not a terminal.** A prompt down a pipe would consume the next line of whatever is driving the tool. See *From a script* below.

The RID is allocated by `lpsd` and reported back. You do not choose it, and the uid follows from it.

#### `--service`

Creates a principal that may be used **only** to run a service, and for
nothing else. It cannot sign in at a console, over the network, or as a
batch job — a sign-on of any other kind is refused even with the correct
credential.

Most services do not need one. A service already gets an identity
without an account: it runs as `LocalService` and carries a per-service
SID derived from its name, which you can name in a security descriptor
like any other principal. Reach for `--service` when a service needs
something a bare SID cannot be — membership of a group, a home
directory, or a profile.

A service principal has no credential and cannot be given one. The
authority mints its token on the service manager's attestation instead,
and only the service manager may ask: nothing else on the system can
obtain a token for it, with or without a password.

The restriction is a property of the account rather than of this
machine, so it travels with the principal. It is also the reason the
flag exists at all: without it, an ordinary account could be named as a
service's identity, and the machine would then be able to obtain a token
for that account with no credential.

Every principal created without this flag permits every kind of sign-on
a person uses and never a service logon, so an existing account cannot
become a service identity by accident.

#### `--no-password`

Creates a principal with credential policy `none`, which signs in without being asked for anything where the client and authority permit credential-free sign-in. No password is collected at creation, including in the interactive flow — an operator who has said the account needs no credential is not then asked to invent one.

```
$ lps add kiosk --group Administrators --no-password --no-prompt
created kiosk with RID 1003
```

**This is a property of the account, not of a terminal.** Nothing scopes it to one console: any client that permits credential-free sign-in can use it, subject to logon-type and originator policy. [SSH](/peios/using-peios/signing-in/signing-in-over-ssh.md) requires an actual password or an enrolled key and a policy allowing that credential; it refuses `none`. Credential-free sign-in is the right posture for a live image, where the medium is unauthenticated anyway and anyone holding it can read everything on it. It is the wrong posture for almost anything else.

An empty password is **not** a way to spell this, and `lps` refuses one:

```
$ lps add alice
...
Password for alice:
Again:
lps: an empty password is not a password: pass --no-password to create a
principal that authenticates without one
```

The two produce accounts that behave differently — an empty password is still collected, still prompted for, and still has to be answered — so having both spellings would mean two things that look identical at creation and diverge at every sign-in. `lpsd` refuses an empty password too, so the rule holds however the request arrives.

### `lps remove <name>`

Deletes a principal.

The RID is **not** reclaimed. Files the principal owned keep naming a SID that now resolves to nobody, and nothing will ever hold that SID again. `lps disable` is usually what you wanted — see [creating accounts](/peios/using-peios/managing-local-principals/creating-accounts.md).

### `lps rename <name> <new-name>`

Renames a principal.

```
$ lps rename erin erin.k
renamed erin to erin.k
```

Its SID stays, so every file, permission and group membership that names it still does, and it signs in by the new name from now on. Its **home directory stays** where it was. `lps set --home` changes the recorded path only; it does not move the directory or its files.

A name held by another principal or by a local group is refused. Changing only the case, `erin` to `Erin`, is allowed.

## Enabling and disabling

### `lps enable <name>` / `lps disable <name>`

A disabled principal keeps everything except the ability to start a new sign-in. It does not revoke already issued tokens or end existing sessions. Use [Task Manager’s sign-out action](/peios/using-peios/threads-and-processes/task-manager.md#signing-someone-out) when existing sessions must end. Signing out can lose unsaved work; [session lifecycle](/peios/security-fundamentals/logon-sessions/lifecycle.md) explains its permissions and limits, including token references held outside the session's processes.

Both are refused if they would leave the machine with no enabled administrator.

### `lps logon-types <name> <type>...`

Sets the kinds of sign-in a principal may be used for. Any other kind is refused, even with the right credential.

```
$ lps logon-types backup network batch
set the logon types for backup
$ lps logon-types backup default
set the logon types for backup
```

| Type | Signing in |
|---|---|
| `interactive` | at this machine: its console or desktop |
| `remote-interactive` | to a remote desktop or over SSH, including commands without a PTY and SFTP |
| `network` | to a network resource, such as a file share |
| `network-cleartext` | over the network, with the password sent to this machine |
| `batch` | as a scheduled job |
| `new-credentials` | as a second identity for outgoing connections |
| `service` | as a service, started by the service manager |

The SSH mapping here follows the current [PGSS SSH implementation contract](/peios/advanced-peios/pgss/logon/ssh-public-key-authentication.md), including its release-qualification and compatible-build limits.

`default` returns the principal to the machine's default: every kind a person uses, and never `service`. There is no setting for "no sign-in at all"; `lps disable` is that.

The last enabled administrator must keep `interactive`, `remote-interactive` or `network`. This is an [account-store guard](/peios/using-peios/managing-local-principals/creating-accounts.md#the-last-administrator-guard); it does not check whether the corresponding console, service or network path is reachable.

## Passwords

### `lps password <name>`

Sets a principal's password, prompting twice.

```
$ lps password jack
New password for jack:
Again:
set the password for jack
```

This is an administrator resetting somebody else's password. A principal changes their own with [`passwd`](/peios/using-peios/signing-in/the-passwd-command.md), which goes over PGSS Logon instead, so that it works identically whichever source holds the account.

An empty password is refused here for the same reason it is refused at creation. A password doesn't change what a principal signs in with: one created with `--no-password` keeps signing in with nothing until `lps policy <name> password` says otherwise (see [credentials](#credentials-and-ssh-keys) below). There is no command that deletes a password; `lps policy` decides whether it is used.

**From a script**, `lps` reads a single line from standard input when it is not attached to a terminal, and does not ask for confirmation:

```
printf '%s\n' "$password" | lps add alice --group Administrators --no-prompt
```

## Credentials and SSH keys

A principal's **credential policy** says what it signs in with. Its **SSH keys** are the public keys it may sign in with over SSH. The two are kept apart: adding a key, or setting a password, never changes the policy, and changing the policy never adds or removes either. Enrolling a key and letting it be used are two decisions.

### `lps policy <name> <policy>`

```
$ lps policy erin either
credential policy updated
```

| Policy | Signs in with |
|---|---|
| `password` | their password |
| `key` | one of their SSH keys |
| `either` | their password or one of their SSH keys |
| `none` | nothing, where credential-free sign-in is permitted; SSH refuses this policy |
| `denied` | nothing: every sign-in is refused, whatever is offered |

`none` is what `lps add --no-password` gives, and carries the same warning. `denied` stops a principal signing in and keeps everything else, as `lps disable` does, but nothing shows it as disabled, so `lps disable` is usually clearer.

It applies from their next sign-in. `lps show` and `lps key list` both say what it is now.

It is refused if it would leave the last enabled administrator nothing to sign in with: `denied`, `key` when they have no key, or `password` when they have no password.

### `lps key list <name>`

```
$ lps key list erin
credential  key
ID                                FINGERPRINT                                         ADDED       LABEL
893dab4342ba215def78c4036b598b65  SHA256:QDzrP/cBqV7YSxqoCgCJQqKJlo6ijyJ8U05BIEKmYnI  2026-10-04  laptop
```

The first line is the credential policy. `ID` is what `lps key remove` takes, `FINGERPRINT` is as `ssh-keygen -l` prints it, and `ADDED` is the day the key was added, in UTC.

### `lps key add <name> <file> [label]`

```
$ lps key add erin erin.pub laptop
key enrolled; credential policy unchanged
```

The file is one line of an OpenSSH public key file, such as `~/.ssh/id_ed25519.pub`. Ed25519 keys are accepted, and RSA keys of 3072 to 8192 bits; nothing else is. Without a label, the key's own comment is its label.

A key the principal already has is refused, as is a 33rd.

### `lps key remove <name> <id>`

```
$ lps key remove erin 893dab4342ba215def78c4036b598b65
key removed
```

Removing the last key of the last enabled administrator, when a key is all they may sign in with, is refused.

### Changing your own keys

`lps` is for administrators, but a principal does not need one to manage their own SSH keys. Anyone signed in to an account this machine holds can add a key to it or remove one, through a program that asks on their behalf, such as a desktop settings app. Nothing is changed for anybody else's account: the account is always the one the program's token belongs to.

Three things differ from `lps key`:

- **The current password is asked for, every time.** Being signed in shows that someone signed in as you, not that you are at the keyboard now, and a key added from an unattended session would be a way back into the account. A wrong password changes nothing and says `Authentication failed.`
- **An account with no password cannot do it.** That is an account whose policy is `key` or `none`, or one whose policy is `password` but that has no password set. There is nothing to prove who you are with, so the change is refused and you are told to ask an administrator, who can make it with `lps key`.
- **A key is removed by its fingerprint**, the `SHA256:` value `ssh-keygen -l` prints for your `.pub` file, rather than by `lps key list`'s `ID`.

Everything else is as `lps key add` and `lps key remove`: the same key types and sizes, a key you already have or a 33rd refused, the key's comment as its label. Adding a key does not let you sign in with it unless your credential policy is `key` or `either`, which only an administrator can set.

Each change, and each refusal once the account is known, is recorded as an `lpsd.credential.changed` event, as a change of your own password is. `operation.name` is `ssh-key-added` or `ssh-key-removed`, and `outcome.reason` says what stopped a refused one, such as `wrong-credential`. A change an administrator makes with `lps key` is recorded as `lpsd.account.modified` instead: see [what is recorded](/peios/advanced-peios/local-principal-interfaces/the-admin-socket.md#what-is-recorded).

On the desktop, the program is **My Settings**, the app for your own account; type `my` in the launcher to open it. Its **SSH Keys** section lists your keys by label, fingerprint, type and the day each was added. **Add Key…** opens a form under the list for the one line of your `.pub` file and your password. **Remove** asks for your password under the key before it is taken away, and asks again if it was wrong. An account with no password sees why its keys are an administrator's to change, in place of these forms.

Your password is changed with [`passwd`](/peios/using-peios/signing-in/the-passwd-command.md), or in My Settings.

### Changing your own display name

Programs acting on your behalf can show you your own account as `lpsd` holds it — your name, display name, credential policy, whether you have a password, and your keys — and set your **display name**, the human name shown beside your account name. Setting a display name asks for no password, since it is not something you sign in with. Both are served on `/run/lpsd/self.sock`; see [the administrative socket](/peios/advanced-peios/local-principal-interfaces/the-admin-socket.md#the-self-socket).

Open **My Settings** (type `my` in the launcher). In **Account**, **Display Name** sets your display name, with **Apply** once it is changed. The desktop reads it when you sign in, so it shows from your next session.

## Profiles

### `lps set <name> [options]`

Changes part of a profile, leaving the rest alone.

```
$ lps set jack --shell /bin/bash --display-name "Jack Palfrey"
updated jack
```

| Option | Effect |
|---|---|
| `--home <path>` | Home directory. Absolute. |
| `--shell <path>` | Login shell. Absolute. |
| `--display-name <text>` | Empty clears it. |
| `--primary-group <group>` | Which group projects to the POSIX gid. |

None of this is identity — no security descriptor names a home directory, and the token carries none of it. It is what `login` needs in order to start a session, and it reaches `login` on the logon reply itself.

**`lps` does not create the home directory.** It records the path. A principal whose home does not exist still signs in, starting in `/`, and `login` says so.

The primary group need not be a membership: `authd` adds it to the token if it is missing, so the order of two commands does not matter.

## Groups

Local groups are objects with a name, a RID, a gid and a description — unlike well-known groups, which exist on every Peios machine and are not stored here.

### `lps group list`

```
$ lps group list
NAME          RID       GID  MEMBERS  SID                                             DESCRIPTION
developers   1001   1001001        2  S-1-5-21-2847362817-1094533892-3310298447-1001  Builds the software
```

`MEMBERS` counts principals in *this* store, which is the only count `lpsd` can answer for. `DESCRIPTION` appears when any group has one.

### `lps group create <name> [description]` / `lps group delete <name>`

```
$ lps group create developers "Builds the software"
created the group developers with RID 1001
```

A group's description says what it is for. Anyone may read it — `getent` doesn't show it, but Principals Manager does, to everyone.

Deleting is refused while anyone is still a member, or while the group is anybody's primary group. Either would leave a record pointing at something that no longer exists.

### `lps group rename <name> <new-name>`

```
$ lps group rename developers engineers
renamed the group developers to engineers
```

It keeps its SID, so its members, and every permission that names it, stay as they are. A name held by a principal or another group is refused.

### `lps group describe <name> <description>`

```
$ lps group describe engineers "Builds and ships the software"
set the description of the group engineers
```

An empty description, `lps group describe engineers ""`, clears it.

### `lps group add <name> <group>` / `lps group remove <name> <group>`

```
$ lps group add alice Administrators
added alice to Administrators
```

Removing `BUILTIN\Administrators` is refused if it would leave no enabled administrator.

## Claims

Claims are named, typed attributes carried on the token and read by conditional ACEs. They are how a security descriptor can say *anyone in Engineering* rather than naming principals one by one.

### `lps claim set <name> <claim> <type> [value]...`

```
$ lps claim set jack Department string Engineering
set the claim Department on jack
$ lps claim set jack Level int64 7
set the claim Level on jack
```

| Type | Values |
|---|---|
| `int64` | Signed integers |
| `uint64` | Unsigned integers |
| `boolean` | `true`/`yes`/`1` or `false`/`no`/`0` |
| `string` | Text |
| `sid` | SIDs, in `S-1-…` form |
| `octet` | Hexadecimal, even length |

A claim may hold several values, and may hold none — `lps claim set jack Department string` empties it, which is different from removing it.

Claim names are matched case-insensitively, so setting `department` replaces `Department`.

### `lps claim remove <name> <claim>`

```
$ lps claim remove jack Department
removed the claim Department from jack
```

**A claim reaches a principal at their next sign-in.** A token's claims are fixed when it is minted, so setting one changes nothing for a session already running.

## Exit status

| Code | Meaning |
|---|---|
| 0 | Succeeded |
| 1 | `lpsd` refused the request, or could not be reached |
| 2 | The command line was wrong |

The split lets a script distinguish "I called this incorrectly" from "it was refused".

## Common failures

**`cannot reach lpsd … it does not appear to be running`** — `lpsd` is not up. It exits if it cannot reach `authd`, or if the store will not read; check its logs before restarting it.

**`permission denied`** — you are not an administrator, or the socket's descriptor does not admit you. See [the administrative socket](/peios/advanced-peios/local-principal-interfaces/the-admin-socket.md).

**`there is no principal named …`** — names are case-insensitive, so this means the account genuinely is not there. `lps list` will show what is.

**`there is no group named … and it is not a SID`** — the name matched no well-known group and no local one, and does not parse as a SID. `lps group list` shows the local ones.

---

# Principals Manager

_Peios / Using Peios / Manage local accounts and groups_

> See this machine's users and groups from the desktop — who each is, what groups they are in, who is in each group, how they may sign in, what claims they carry and what privileges they get — and, as an administrator, make, change, rename, disable and delete users and groups, set what users sign in with, their SSH keys and their claims, and change the privileges this machine gives.

**Principals Manager** shows this machine's **principals**, the users and
groups it knows, and lets an administrator manage the local ones. A
*local* principal is one this machine holds itself, in `lpsd`'s store; a
*built-in* one, such as `SYSTEM` or `Administrators`, is the same on
every Peios machine and is defined by `authd`.

Start it from the launcher: **Principals Manager**.

## Users and groups

**Users** and **Groups**, in the bar, switch between the two lists.

- A user is listed with their full name, if they have one, whether they
  are local or built-in, and whether they are enabled. A disabled user
  keeps everything but can't sign in, and is greyed.
- A group is listed with whether it is local or built-in, how many
  members it has, and its description: what it is for. Some built-in
  groups, such as `Everyone`, have no list of members at all: who is in
  them is decided by a rule as each person signs in, and they say **By
  rule**.

Type in **Find** to narrow the list to names, full names, descriptions or
SIDs containing what you type.

## A user or group in full

Select one to see it in full on the right.

For a user, that is their user ID, primary group, home folder, shell
and SID; the kinds of sign-in they may use, such as at the machine or
over the network; the groups they are in; and their **claims**, the
named values that permissions written with conditions are checked
against. **Copy SID** copies their SID.

An administrator also sees what a local user signs in with, such as a
password or an SSH key, and their **SSH keys**: the public keys they may
sign in with over SSH, each with its label, its fingerprint and the day
it was added.

For a group, it is its description, group ID and SID, and who is in it.
Who is in a built-in group such as `Administrators` is recorded on this
machine, as a local group's is, so it is listed too. Someone whose
primary group it is, is in it.

Select a group a user is in, or a member of a group, to go to it.

## Making a user

**New user**, in the bar, or **Ctrl+N**, asks for:

- a **name**, which they sign in with;
- a **full name**, if you want one shown;
- a **password**, typed twice; or tick **Signs in without a password**,
  and nothing is asked for on sign-in paths that permit credential-free
  access; SSH does not permit this;
- whether they are an **Administrator**, who may change the machine and
  its users and groups;
- a **home** folder and a **shell**, if not `/home/<name>` and
  `/bin/sh`.

**Create** makes the user with all of it, or not at all: if anything is
refused, such as a name already taken, nothing is made and the window
says why.

## Changing a user

Select a local user, and their buttons are below their details:

- **Edit** changes their full name, home, shell and primary group. Their
  home folder is made at their first sign-in, and is not moved if you
  change it. Their primary group is the one their new files belong to,
  and they are in it whatever their other groups.
- **Rename** changes the name they sign in with. They keep their SID, so
  their files, permissions and groups stay theirs. Their home stays where
  it is; change it under **Edit** if it should follow.
- **Set password** gives them a new password. A user who signed in
  without one needs it from then on. Their sessions already signed in
  carry on.
- **Sign-in** chooses what they sign in with, and the ways they may sign
  in.
  - **Signs in with**: a password, an SSH key, either, nothing (no
    credential is asked for where credential-free sign-in is permitted,
    excluding SSH), or
    nothing accepted (every sign-in is refused, though nothing shows them
    as disabled). Adding a key doesn't change this, so
    a key added isn't used until this allows one.
  - **May sign in**: as the machine's default allows (at the machine, on
    a remote desktop, over the network, and the rest a person uses), or
    only the ways you tick. **To run a service** lets the service manager
    start a service as them without any password, and is never part of
    the default.
- **Add to group** puts them in a local group, or in a built-in one whose
  members are recorded here, such as `Administrators` or `Users`.
  **Remove**, beside a group they are in, takes them out of it.
- **Add**, beside **SSH keys**, adds a public key: paste one line of their
  `.pub` file, such as `~/.ssh/id_ed25519.pub`. Ed25519 keys are
  accepted, and RSA keys of 3072 to 8192 bits. Without a label, the key's
  own comment is its label. **Remove**, beside a key, removes it.
- **Add**, beside **Claims**, gives them a claim: its name, its type, and
  its values, one to a line. Each value is checked against the type, and
  one that doesn't fit is named. A claim of SIDs may name a user or group
  instead of giving a SID. **Edit** changes a claim's type and values,
  and **Remove** takes it away. A claim with no values is still a claim
  they have, which conditions treat differently from one they don't.
- **Disable** stops them signing in and keeps everything else; **Enable**
  lets them again.
- **Delete** removes them, after asking. Their SID is never given to
  anyone again, so files and permissions that name it name nobody;
  **Disable instead** is offered, and is usually what you want.

Changes to how a user signs in, to their keys, to the groups they are in
and to their claims apply from their next sign-in. Disabling an account does
not end existing sessions. Use [Task Manager’s sign-out action](/peios/using-peios/threads-and-processes/task-manager.md#signing-someone-out)
when those must end; it can lose unsaved work. [Session lifecycle](/peios/security-fundamentals/logon-sessions/lifecycle.md)
explains the permissions and limits.

## Groups

In **Groups**, **New group** (or **Ctrl+N**) makes a local group, with a
name and a description of what it is for. Anyone may read the
description.

Select a local group, and its buttons are below its details:

- **Edit** changes its description; empty, it has none.
- **Rename** changes its name. It keeps its SID, so its members, and the
  permissions that name it, stay as they are.
- **Add member** puts a local user in it. **Remove**, beside a member,
  takes them out; a member whose primary group it is has to be given
  another under **Edit** first.
- **Delete** removes it, after asking, once nobody is in it. Its SID is
  never given to anything again.

A built-in group whose members are recorded here, such as
`Administrators`, has **Add member** and **Remove** too, but can't be
renamed, described or deleted: `authd` defines it, the same on every
Peios machine.

The local account store protects the last enabled administrator under
its sign-in checks.
Disabling or deleting the last one, taking away every way they could
sign in to administer, or leaving them nothing to sign in with, such as
removing the only key of one who signs in only with a key, is refused,
and the window says why.

This [last-administrator guard](/peios/using-peios/managing-local-principals/creating-accounts.md#the-last-administrator-guard)
does not check whether a console, remote service or network path is
reachable. Verify fresh access before closing a working administrator session.

## Privileges

What a person may do beyond what permissions allow them, such as back up
any file or shut the machine down, is this machine's **local policy**:
one **record** per user or group in the registry, which `authd` reads at
every sign-in. Each record may give **privileges**, an **integrity**
level, an **owner** for what they make, and a **default DACL**, the
permissions an object they make gets when it has no parent to inherit
from. See [Assigning privileges](/peios/using-peios/managing-local-principals/assigning-privileges.md)
for what each does and how records combine.

**Privileges**, in the bar, lists the records: who each is for, how many
privileges it gives, and its integrity. A service's record is named by
the service. Select one to see it in full, each privilege with what it
lets its holder do. **Denied to everyone**, the first row, is the
privileges no record can give.

A user's page says what they would get in all if they signed in now: the
privileges, integrity, owner and default DACL that their own record,
their groups' records and `Everyone`'s come to, and which records those
are. **Signed in** chooses the kind of sign-in, such as at the machine or
over the network, since a record may be for one kind of sign-in. A
group's page says what its record gives its members.

Changing them:

- **New record** (or **Ctrl+N**) makes a record, for a user or group
  here, a well-known name such as `Everyone` or `Network`, or a SID.
  **Give them their own record**, on a user's page, and **Give it a
  record**, on a group's, do the same for them.
- **Edit** changes what a record gives: tick its privileges, choose its
  integrity, and type an owner or a default DACL, in SDDL. Anything left
  empty, the record says nothing about. A default DACL that isn't SDDL
  `authd` could use is refused.
- **Delete** removes a record, after asking. What it gave is no longer
  given.
- **Edit**, on **Denied to everyone**, chooses what no record may give.

Taking `SeChangeNotifyPrivilege` from `Everyone` or `Administrators` is
asked about first: without it, nobody who has it only from there can
pass through a folder, so no shell starts for them.

A record names a user or local group by its SID, since `authd` doesn't
ask `lpsd` what a name means; the window does this for you. What `authd`
warns about in the records is shown above the list, with why: a record it
ignores, such as one named by a name it doesn't know, or two records for
the same principal, such as `Administrators` and `S-1-5-32-544`, which
both apply and should be one.

On a machine with no policy at all, `authd` gives `Everyone`
`SeChangeNotifyPrivilege` and nothing more, and the list says so. The
first record saved makes the policy, with that in it, since once there
is a policy it is all there is.

Changes apply from each person's next sign-in.

## Changes made elsewhere

The lists are read again every few seconds, so a user, group or
privileges record added, changed or removed with `lps` or `reg`, or by
another administrator, appears without your doing anything. **Refresh**, or **F5**, reads everything at
once.

## What you may see and change

Anyone may see the users and groups: the identity socket that
Principals Manager reads them from answers everyone.

What a user signs in with, and their SSH keys, are shown here only to an
administrator: `lpsd` holds them, and tells nobody else, except each user
about their own (see [Changing your own keys](/peios/using-peios/managing-local-principals/lps-command.md#changing-your-own-keys)). The privileges
records are shown to anyone signed in, and changed by whoever the
registry key's permissions let, which as shipped is `Administrators`.

Changing them needs `BUILTIN\Administrators`, enabled in your token, as
`lps` does. Without it, the window says **You may look, but not change
anything**, and why, and offers no changes. Built-in principals and
groups can't be renamed or deleted, and the window says so when one is
selected.

## In a terminal

`lps list` lists the users, `lps show NAME` shows one in full, and `lps
group list` lists the local groups. `getent group Administrators` lists
who is in a group, and `lps key list NAME` lists a user's keys. `lps add`,
`lps set`, `lps rename`, `lps password`, `lps policy`, `lps key`, `lps
logon-types`, `lps claim`, `lps group`, `lps disable` and `lps remove` make
the same changes as the window. See [The `lps` command](/peios/using-peios/managing-local-principals/lps-command.md).
The privileges records are registry keys under
`Machine\Generic\Authn\Policy`, read and changed with `reg`, and `token
show --all` lists what a session's token actually holds.

---

# Assigning privileges

_Peios / Using Peios / Manage local accounts and groups_

> The policy records under Machine\Generic\Authn\Policy that authd reads at every logon — which privileges a principal gets, and at what integrity level.

A principal source says **who someone is** — their SID, their memberships, their POSIX identifiers. It never says how much this machine trusts them. Privileges and integrity are **local policy**: `authd`'s alone, decided here, and there is no message with which a source could ask for one.

That policy lives in the registry:

```
Machine\Generic\Authn\Policy
```

`authd` reads it at **every logon**, not once at startup. A policy change takes effect the next time someone signs in, rather than the next time you restart the one daemon on the system that is most disruptive to restart.

[Principals Manager](/peios/using-peios/managing-local-principals/principals-manager.md) shows and edits it from the desktop. Its **Privileges** list is the records on this page, and each user's page says what they would get in all, worked out by the same code `authd` mints from.

## One record per principal

Each subkey is a principal, and holds everything this machine grants them:

```
Machine\Generic\Authn\Policy
  DeniedPrivileges  REG_MULTI_SZ  ["SeDebugPrivilege"]
  \Everyone
      Privileges    REG_MULTI_SZ  ["SeChangeNotifyPrivilege"]
      DefaultDacl   REG_SZ        "D:(A;;GA;;;S-1-3-4)(A;;GA;;;SY)(A;;GA;;;BA)"
  \Administrators
      Privileges    REG_MULTI_SZ  ["SeBackupPrivilege", "SeRestorePrivilege"]
      Integrity     REG_SZ        "High"
      Owner         REG_SZ        "Administrators"
```

Keyed by principal rather than by privilege on purpose. One key shows the totality of a principal's authority — `reg ls` on `\Administrators` answers "what can an administrator do on this machine?" completely. Authority scattered across twenty per-privilege values is authority nobody audits: a right granted somewhere unexpected does not surface when you look at the principal, and you would have to know to check every other place.

It is also the only shape that holds more than privileges. Integrity, the default owner, the default DACL and the logon types a peer may originate all live on the same record.

### Naming a principal

A subkey's name is either a **well-known name** or a **literal SID**:

```
\Administrators
\S-1-5-21-2847362817-1094533892-3310298447-1000
```

Names are matched case-insensitively. The recognised ones are `SYSTEM`, `Everyone`, `Authenticated Users`, `Administrators`, `Users`, `Guests`, `Local Service`, `Network Service`, `Local` (everyone signed in on this machine), and the logon types `Interactive`, `Remote Interactive`, `Network`, `Batch`, `Service` and `Anonymous`.

Two limits are worth knowing before you hit them:

**`BUILTIN\Administrators` cannot be a subkey name.** A backslash is the registry's path separator, so the qualified spelling is unrepresentable. Write the bare name.

**A local group's name does not resolve.** `authd` cannot know what `developers` means without asking `lpsd`, and policy must never depend on a principal source being up — that would make "what may this principal do" unanswerable exactly when a source is broken. Name local groups by SID, which `lps group list` will show you.

A subkey that is neither a known name nor a parseable SID is **ignored with a warning**. It is worth checking for that warning after editing: a record naming nobody sits in the key looking authoritative and applying to no one.

Two subkeys naming one principal, such as `\Administrators` and `\S-1-5-32-544`, **both apply**, and `authd` warns about them. Their privileges add up like any two records', but for integrity, `Owner` and `DefaultDacl` whichever is read first wins, which is an order nobody chose. Merge them into one. Principals Manager shows the warning above its Privileges list.

## If the key exists, the key is the whole policy

This is the most important behaviour on the page.

`authd` carries a compiled-in floor, but it applies **only when the key is absent entirely**. It is not merged in value by value. So a policy you write is the complete policy: anything not granted below is not granted.

| State | What a principal gets |
|---|---|
| Key absent | The compiled floor — `Everyone` gets `SeChangeNotifyPrivilege`, and nothing else |
| Key present | Exactly what the records say |
| Key present but unreadable | Nothing, and a loud log line |

The alternative — a compiled default that each record replaces — fails in the wrong direction. An administrator who writes one record believing they have locked the machine down would still be handing out whatever a compiled table said for every principal they did not mention, from a table they cannot read. That is a security failure, and a silent one. Failing towards less privilege makes things stop working, which is far easier to diagnose than authority you did not know you were granting.

An unreadable key is treated as granting nothing rather than as absent, for the same reason: reading it as absent would silently restore every privilege an administrator may have deliberately removed, at the one moment nobody can check.

## What a machine ships with

The defaults are **registry data, not compiled in** — `authd-policy.reg`, shipped to `/usr/share/regim/` and applied if the image opts in. So they can be read, edited, and replaced wholesale by an image that wants a different policy, rather than being invisible inside a binary.

A stock machine grants:

| Principal | Privileges | Integrity |
|---|---|---|
| `Everyone` | `SeChangeNotifyPrivilege`, `SeCreateSymbolicLinkPrivilege` | (Medium, by default) |
| `Administrators` | the operational set below, plus both of the above | High |

The operational set is `SeBackupPrivilege`, `SeRestorePrivilege`, `SeShutdownPrivilege`, `SeRemoteShutdownPrivilege`, `SeSystemtimePrivilege`, `SeSecurityPrivilege`, `SeLoadDriverPrivilege`, `SeImpersonatePrivilege`, `SeIncreaseQuotaPrivilege`, `SeIncreaseBasePriorityPrivilege` and `SeProfileSingleProcessPrivilege`.

**`SeDebugPrivilege` is deliberately not granted.** It writes to any process regardless of integrity label, so it is the single privilege that most directly defeats the integrity boundary, and ordinary administration does not need it. Add it when a machine genuinely does.

Three privileges are never granted by the shipped policy and should not be added: `SeCreateTokenPrivilege` mints any identity without going near `authd`, which would make every other control here decorative; `SeTcbPrivilege` and `SeAssignPrimaryTokenPrivilege` belong to the trusted computing base.

Note that `Administrators` is granted `SeChangeNotifyPrivilege` **directly**, not only through `Everyone`. Privileges accumulate across every SID on a token, so that repetition is insurance: if someone edits the key and drops the `Everyone` record, ordinary users lose the ability to traverse a directory — but administrators keep working and can repair it.

## How the values compose

**Privileges accumulate.** A token holds the union of every record naming a SID it carries — the principal's own, and each of their groups. A group marked `USE_FOR_DENY_ONLY` contributes nothing.

**`DeniedPrivileges` wins.** Listed on the `Policy` key itself rather than on a record, it is applied *after* the union, so no record you have not read can defeat it. It is on the parent key so it cannot collide with a principal who happens to be called `Denied`.

**Integrity, `Owner` and `DefaultDacl` are single values**, so they cannot accumulate. The principal's own record wins outright; otherwise the groups decide:

- **Integrity** takes the **maximum** across the groups that name one.
- **`Owner`** and **`DefaultDacl`** have no ordering to compare, so two groups naming *different* values is a misconfiguration: it logs a warning and falls back to the default rather than picking by whichever the registry enumerated first. Two groups naming the same value is not a conflict.

The user's record winning outright is what makes a principal possible to **lower**. Under a plain maximum, a guest whose own record said `Low` would still come out Medium the moment any group they belonged to named Medium. The consequence is worth stating plainly: a group cannot impose an integrity floor on a member. If `Administrators` names High and a member's own record names Low, that member gets Low.

## Writing each value

### `Privileges` — `REG_MULTI_SZ`

Full ABI names, `SeBackupPrivilege` rather than `SeBackup`, matched **case-sensitively**. A name this build does not recognise is dropped with a warning and the rest of the list still applies — a policy written for a newer Peios should not lose the privileges it spelled correctly.

An empty list is meaningful: it grants nothing, and is different from the value being absent.

### `Integrity` — `REG_SZ` or `REG_DWORD`

A tier name, matched case-insensitively:

| Name | Value |
|---|---|
| `Untrusted` | 0 |
| `Low` | 4096 |
| `Medium` | 8192 |
| `High` | 12288 |
| `System` | 16384 |

Or a raw `REG_DWORD`. The kernel compares integrity numerically and any value is legal, so the numeric form reaches levels between the tiers — `8193` sits just above Medium. Use the name unless you need that.

A principal no record names gets **Medium**. That default is compiled in and is not affected by the key existing, because unlike a privilege it is not a grant: every token must carry some level to be valid at all.

### `Owner` — `REG_SZ`

Which principal owns objects this token creates. Names a principal, exactly like a subkey name does; `authd` converts it to the index the token actually carries, which is a number meaningful only within one token and different on the next logon.

Absent — the ordinary case — means objects are owned by their creator.

The case this exists for is a shared administrative estate: objects an administrator creates being owned by `Administrators` rather than by the individual, so they remain manageable when that person's account goes away.

### `DefaultDacl` — `REG_SZ`

The DACL an object gets when this token creates it with no parent to inherit from, written as **SDDL**. The shipped policy names it on the `Everyone` record:

```
D:(A;;GA;;;S-1-3-4)(A;;GA;;;SY)(A;;GA;;;BA)
```

That is the owner, `LocalSystem` and `BUILTIN\Administrators`, full control, and nobody else — the same shape `authd` stamps on a home directory. `S-1-3-4` is `OWNER RIGHTS`, which the access check resolves against the object's owner at the time of the check. You can also write it as the alias `OW`. It has to be `OWNER RIGHTS` rather than `CREATOR OWNER`: the kernel copies a default DACL onto a new file verbatim and substitutes no placeholders, so a `CREATOR OWNER` ACE would name nobody.

The value is a fallback and is reached rarely. Whenever the parent has inheritable ACEs the child takes those and the default DACL is not consulted (see [Inheritance](/peios/security-fundamentals/security-descriptors/inheritance.md)), and every filesystem root on Peios carries inheritable ACEs. What reaches it is an object with no parent, such as an abstract socket, or a child of a container whose descriptor was written without inheritable ACEs. There is no kernel fallback beneath it: a token with no default DACL leaves such an object with a null DACL, which grants every caller everything.

SDDL rather than raw bytes because the whole argument for policy living in the registry is that an operator can read it. Conditional ACEs are preserved, so `D:(XA;;GA;;;WD;(@USER.Department == "Engineering"))` works and keeps its condition.

A value that does not parse is dropped with a warning and the token is minted with no default DACL — a typo costs the customisation, not the session, but it does leave the null-DACL case open until it is corrected.

### `LogonTypes` — `REG_MULTI_SZ`

The logon types this principal may **originate** — request of `authd` over `/run/logon.sock` on somebody else's behalf. This is a statement about the asker, not about the account being signed in: `login` originates `Interactive` logons for whoever is at the console, a graphical greeter would too, and a web console would originate `Network` ones.

```
\S-1-5-80-…-…   (a greeter's service SID)
    LogonTypes  REG_MULTI_SZ  ["Interactive"]
```

The names are `Interactive`, `Network`, `Batch`, `Service`, `NetworkCleartext`, `NewCredentials` and `RemoteInteractive`, matched case-sensitively; an unrecognised name is dropped with a warning and the rest still applies.

The proposed logon type is not cosmetic. It selects the group SIDs the minted token carries — `Interactive` puts `S-1-5-4` on it, `RemoteInteractive` puts `S-1-5-4` *and* `S-1-5-14` — and policy records can key on those SIDs, so the type must be what the *peer* is permitted, never merely what it proposed. A greeter granted `["Interactive"]` cannot mint itself a network-shaped token, nor the reverse.

A principal with no `LogonTypes` originates nothing. `SYSTEM` is the exception and is permitted every type unconditionally — it administers this very key, so a registry constraint on it would enforce nothing, and `authd` does not pretend otherwise.

An empty list revokes: it is "may originate nothing", distinct from the value being absent only in that it says so on the record.

### `LogonSocketDescriptor` — `REG_SZ`, on the `Policy` key itself

The security descriptor `/run/logon.sock` carries, as SDDL, read once at `authd` startup. The built-in value gives `SYSTEM` and `Administrators` full access, and gives every authenticated principal connect access alone, so that anyone signed in can [change their own password](/peios/using-peios/signing-in/the-passwd-command.md) and [sign themselves out](/peios/security-fundamentals/logon-sessions/lifecycle.md):

```
O:SYG:SYD:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;0x100082;;;AU)
```

Connect access is `FILE_WRITE_DATA`; `0x100082` grants it with stat and sync. Reaching the socket does not let a peer originate a logon: that is the `LogonTypes` record above, and a peer without one originates nothing however it connected. So admitting a new originator that is not already an authenticated principal takes both edits, and one that is takes only the record.

A replacement descriptor that leaves out the `AU` entry turns off self-service password change, and signing yourself out, for every principal it leaves out.

### `SessionEndSecurity` — `REG_SZ`, on the `Policy` key itself

Who may sign somebody **else** out: end a logon session that is not their own. `authd` does the work when asked over `/run/logon.sock` ([Session lifecycle](/peios/security-fundamentals/logon-sessions/lifecycle.md)); this value, as SDDL, is the security descriptor it access-checks the caller's token against, for the one right `0x1` (every generic right maps to it). It is read on every request, so an edit applies to the next one. With the value absent, the descriptor is:

```
O:SYG:SYD:(A;;0x1;;;SY)(A;;0x1;;;BA)
```

— `SYSTEM` and `Administrators`. Granting `0x1` to a group lets its members sign anybody out.

Signing yourself out needs nothing here: a person may always end their own session. And nothing here lets anyone end `SYSTEM`'s session, `Anonymous`'s, or a service's — a service is stopped through the service manager.

A value that is present but unusable — not a `REG_SZ`, empty, or not valid SDDL — lets **nobody** sign anybody else out until it is fixed or removed, and `authd` logs why on each request. It does not fall back to the default, which might grant more than the site meant.

## Locking a machine down

To forbid a privilege regardless of what any record says:

```
Machine\Generic\Authn\Policy
  DeniedPrivileges  REG_MULTI_SZ  ["SeDebugPrivilege", "SeLoadDriverPrivilege"]
```

That is one edit, in one place, that a record you have not read cannot defeat. Removing the privilege from each record individually relies on having found them all.

## What this cannot express yet

**Account sign-in restrictions are a separate control.** These privilege records decide what a session gets. The account store also supports [`lps logon-types`](/peios/using-peios/managing-local-principals/lps-command.md#lps-logon-types-name-type) to limit which kinds of sign-in an account may use, and `lps disable` to prevent new sign-ins altogether. `LogonTypes` above constrains what the asking service may request; it does not replace the account restriction. Follow the account command reference for supported types and the last-administrator guard.

**Privilege constants do not establish assignment support.** Earlier privilege guidance described `SeTakeOwnershipPrivilege`, `SeRelabelPrivilege` and `SeSystemProfilePrivilege` as absent from the ABI and therefore impossible to assign. The current [generated KACS ABI](/peios/advanced-peios/peios-kernel/kacs/kacs-abi.md) defines constants for all three. That contradicts the ABI-absence claim, but does not by itself establish the accepted privilege-name table in `authd` or a service configuration, or a release in which assignment became available. Confirm support for the deployed build and inspect the actual token rather than relying on either blanket claim. Even when take-ownership is available, [changing the owner does not guarantee DACL repair](/peios/security-fundamentals/security-descriptors/ownership.md#recovery-patterns).

## Seeing what a token actually got

```
$ token show --all
```

`[privileges]` lists what the token holds and `integrity` shows the label. A privilege the tool cannot name appears as `<privilege bit N>` rather than being omitted, so the list is always complete even when the name table is not.

To see what a sign-in *would* get before anyone signs in, open the user in Principals Manager: it works the policy out for them, for each kind of sign-in, and says which records it came from.

## See also

- [Privileges](/peios/security-fundamentals/privileges/overview.md) — the model these records feed.
- [The token command](/peios/using-peios/peiosutils/system-and-processes/token.md) — reading the privileges and integrity a live token actually carries.
- [Authorities and principal sources](/peios/security-fundamentals/authentication/principal-sources.md) — the accounts these records apply to.
