The desk keeps the people
A login without an identity provider: the desk holds the users and their groups, and mints the tokens the engine trusts.
The wizard does this for you. This page is what it does, for when you would rather do it yourself.
For a machine a few people share. The desk becomes a small issuer: it holds usernames and passwords, mints a short-lived token for the person with what their groups and grants give, and the engine trusts the desk’s signature. No identity provider, no accounts on the machine.
The desk
# ~/nils/desk/nils-desk.toml
bind = "127.0.0.1:7200"
origin = "http://127.0.0.1:7200"
mode = "local"
store = "nils-desk.sqlite"
[local]
key = "nils-desk.key" # made at first start, mode 600
audience = "nils"
[engine]
url = "http://127.0.0.1:8437"
Start it once so that it makes its signing key:
cd ~/nils/desk && nils-desk serve --config nils-desk.toml
It publishes the public half at /.well-known/jwks.json, which is what the engine will read.
The first person
The password is read from standard input, one line:
printf 'their password\n' | nils-desk user add anna --display "Anna" --admin
--admin puts the first person in Admins, who may do everything. Everyone else is added the same way without it, into the groups you name, on the People page or on the command line:
printf 'a password\n' | nils-desk user add bo --display "Bo" --group Readers --grant assistant:use
nils-desk user list
nils-desk user access bo --group Reviewers --grant assistant:use
printf 'a new password\n' | nils-desk user password bo
--group names a group and --grant a grant the person holds alone, and each repeats; --detail sets what they see in records on their own. user access changes only what it names, and --none takes every group and own grant away. A desk starts with the groups Readers, Reviewers, Operators and Admins, and groups and grants says what each gives. For this release user grant still does what user access does, and --entitlement still takes an old name such as reader.
The engine, trusting the desk
nils serve --auth oidc \
--oidc-trust issuer=http://127.0.0.1:7200,audience=nils,jwks=http://127.0.0.1:7200/.well-known/jwks.json,keep_subject=true \
--bind 127.0.0.1:8437 --registry ~/nils/registry --pack-dir ~/nils/packs
The issuer and the JWKS are the desk’s own origin, and the audience is the one in the desk’s [local] table. Change the desk’s origin and you change these too. The desk’s tokens carry each person’s grants and detail, which the engine reads as they are, so there are no groups to map here.
Try it
curl -s -c /tmp/jar -X POST http://127.0.0.1:7200/desk/login \
-H 'content-type: application/json' -H 'X-Nils-Desk: 1' \
-d '{"username":"anna","password":"their password"}'
curl -s -b /tmp/jar http://127.0.0.1:7200/desk/capabilities
The second answers with "signed_in": true, that person’s grants, detail and groups, and the engine’s own document as that person sees it. In a browser it is a username and a password, and then the desk.
The order things start in does not matter. The engine fetches the desk’s keys when it first sees a token signed by one it does not know, so either may be restarted alone.
Moving an install to local mode
Run nils setup again, choose Change something, and answer that the desk keeps the people. When the desk keeps nobody yet, the wizard asks for the first person. Say no, or run it without a terminal, and its last lines give the command that adds one; the desk’s sign-in page gives it too:
printf 'their password\n' | nils-desk user add anna --admin --config ~/nils/desk/nils-desk.toml
The wizard sets what it writes in nils-desk.toml (the mode, the addresses, and the tables for the engine and the other parts) and keeps the keys you set there yourself. Its plan says whether the file will be written, kept as it is, or updated.
From the desk, a person with work on the install does the same with Change how people sign in on Settings, People, where the supervisor makes the step. It asks for the first person where the desk keeps nobody with a password yet; see change how people sign in.
A change of how people sign in signs everyone out. The desk remembers how people sign in: the mode, the origin a local desk signs for, or the provider. When it starts with another, every session ends, and a browser that had the desk open signs in again. Opening a local desk to the network is such a change too, since its origin moves.
Updated 17 October 2026