> For the complete documentation index, see [llms.txt](https://mercure-technologies.gitbook.io/xprem/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mercure-technologies.gitbook.io/xprem/dashboard/users.md).

# Users

Every dashboard visitor signs in with an email address and a password. Accounts live in the `users` table of your PostgreSQL database, and administrators manage them at runtime from **Users** in the dashboard sidebar.

### The first admin

On its first boot the server runs a migration that creates the `users` table, then seeds it with one administrator built from `ADMIN_EMAIL` and `ADMIN_PASSWORD`. Both variables must be set for that first boot: with an empty users table and nothing to seed it with, the dashboard would be a door nobody can ever open, so the server refuses to start instead.

`ADMIN_PASSWORD` is held to the same password policy as accounts created from the UI: at least 8 characters, with an uppercase letter, a lowercase letter, a digit and a special character. A non-compliant value fails the migration, and therefore the boot, with an error listing the missing rules; pick a compliant password and change it from the dashboard later if you wish.

Once that migration has run, the pair has done its job. You can remove both variables from the deployment, since logins are checked against the database from then on, and changing the variables later has no effect on existing accounts. If you restore a database that already contains users, the seeding step notices and skips itself.

### Admins and members

An account is either an **admin** or a **member**, and the difference is simple: members are read-only. They can browse every app with its branches, channels, updates and API-key list, and they can change their own password from the **Account** page. Everything that mutates the server is reserved for admins:

| Action                                                 | Member | Admin |
| ------------------------------------------------------ | :----: | :---: |
| Browse apps, branches, channels and updates            |    ✅   |   ✅   |
| Change their own password                              |    ✅   |   ✅   |
| Create, edit or delete apps                            |    ❌   |   ✅   |
| Create or delete branches and channels, remap channels |    ❌   |   ✅   |
| Create or revoke API keys                              |    ❌   |   ✅   |
| Download an app's signing certificate                  |    ❌   |   ✅   |
| Create or delete users, grant or revoke admin          |    ❌   |   ✅   |
| Enable, disable or approve an account                  |    ❌   |   ✅   |

API keys and the signing certificate sit on the admin side because they are release power: an API key can publish updates to your users, and the certificate is the key material those updates are verified against.

An admin demotion takes effect immediately, because the server re-checks the flag in the database on every admin action rather than trusting the session token, so revoking someone's access does not wait for their session to expire.

On Enterprise deployments this all-or-nothing split can be refined: user roles let you grant a member access to specific apps only, each with its own set of permissions, so a member can for example manage the rollouts of one app without being able to touch any other. See [User roles & RBAC](/xprem/security/user-roles-and-rbac.md).

#### Active, disabled and pending accounts

Whatever its role, every account also carries an enabled state, which the **Users** page surfaces in its **Access** column as an **Active**, **Disabled** or **Pending** badge with a switch beside it. An enabled account reads Active. A disabled account that has already signed in at least once reads Disabled, while one that has never connected reads Pending, meaning nobody has let it in yet.

An admin uses that switch for two things. Turning it off revokes an account's access without deleting it: the row stays, its per-app grants stay with it, open sessions are dropped, and sign-in is refused until you turn it back on, at which point the account finds exactly the access it had before. Turning it on approves an account that was waiting for validation, which is what the **Require admin approval** option of single sign-on relies on: with that option enabled, someone arriving from your identity provider for the first time gets an account created in the pending state, and can only sign in once an admin has approved it.

The usual rails apply here too. You cannot disable your own account, and the last enabled admin cannot be disabled either.

### Creating a user

From **Users**, select **Create user** and enter the person's email, an initial password, and whether the account is an admin. Passwords must be at least 8 characters long and mix an uppercase letter, a lowercase letter, a digit and a special character. The server stores only a bcrypt hash, so the password itself cannot be recovered later: pass it on to the person now, and they can change it from their **Account** page afterwards.

### Safety rails

Three rules keep the dashboard from locking itself out:

* You cannot change your own admin flag.
* You cannot delete your own account.
* The last remaining admin can be neither demoted nor deleted, so there is always at least one admin.

If you personally need to leave a deployment, promote someone else first, then have them remove your account.

### Changing your password

Open **Account** in the sidebar and enter your current password along with the new one. The current password is required even though you are already signed in, because a session left open on a shared machine should not be enough to take the account over.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mercure-technologies.gitbook.io/xprem/dashboard/users.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
