Email & password
Email and password sign-in for self-hosted Proliferate.
Email and password is the default sign-in method for self-hosted Proliferate. It is on out of the box and needs no OAuth app and no identity provider: you claim the server in a browser, invite your team from the desktop app, and everyone signs in with an email and a password.
It works because self-hosted servers run in single-org mode (SINGLE_ORG_MODE
defaults to true whenever PROLIFERATE_TELEMETRY_MODE is not
hosted_product, so no configuration is needed): every account belongs to the
one instance organization, and an invitation to that organization doubles as
permission to register.
How the desktop app picks it
There is nothing to enable. On launch the desktop app probes
GET /auth/desktop/methods; the email and password form is the default
sign-in screen whenever GitHub OAuth is not configured on the server.
Screenshot: Desktop sign-in screen, email and password form against a self-hosted server
The desktop sign-in screen showing the email/password form served when GitHub OAuth is not configured.
The first account: claim the server
A fresh install has no accounts and no open signup. ./bootstrap.sh prints a
one-time setup token and a claim URL when it finishes:
Open https://<site>/setup in a browser and enter an email, a password, an
optional organization name (blank derives a name from your email domain; the
derived default shows as the placeholder), and the setup token. That account
becomes the owner of the instance organization. The /setup page returns 404
forever after the claim; there is no open registration for anyone else. The
full bootstrap walkthrough is on the
Docker page.
Screenshot: the /setup claim page with email, password, optional organization name, and setup token fields
The server-rendered first-run claim form in a browser, with the derived organization name shown as the placeholder.
The owner is an ordinary organization owner, not a permanent superuser: the account can be promoted, demoted, or deleted like any other, within the invariants below (the organization always keeps at least one admin).
Invite the rest of the team
Proliferate uses an invite-as-allowlist model: an invitation allowlists an email address, and the invited person creates their own account with their own password. Registering takes proof of the invitation, not just knowledge of an invited email: the request must carry the invitation token from the invite link. Nobody ever sets a password for someone else, and revoking an invitation closes registration for that email again.
Invite an email and share the link
In the desktop app, open Settings, then Members, and invite the person's email address. Every pending invitation in the list has a Copy invite link action that copies a link to your server's registration page with the token and email prefilled:
Share that link with the person. Invitations work without any email provider
configured; if you set the optional RESEND_API_KEY (and
RESEND_FROM_EMAIL) variables, the server emails the same link through
Resend for you.
Screenshot: Desktop settings, Members pane with a pending invitation and its Copy invite link action
The organization members settings pane showing a pending invitation with the Copy invite link menu item.
Inviting the same email again replaces the pending invitation with a fresh token, so previously shared links stop working. Copy the new link and share it again.
The invitee registers
The invited person opens the link in any browser. The server renders a plain
registration form with their email and invitation token prefilled; they
choose a password and submit. The same thing is available as an API call to
POST /auth/password/register, which requires all three fields:
An unknown, revoked, expired, or mismatched token gets one uniform 403, on purpose: responses never reveal whether an email is invited. An email that already has an account gets a 409 and should sign in instead.
Sign in
The new user points the desktop app at your server (Connect the desktop app) and signs in with the email and password they just registered. They land in the instance organization with the role they were invited with, so inviting someone as an admin makes them an admin from their first sign-in; admins adjust roles in the Members pane afterwards.
Restrict registration to your domains
ALLOWED_EMAIL_DOMAINS is an optional, comma-separated list of email domains
that invited users must belong to before they can self-register:
It is a gate on top of invitations, never a grant: nobody registers without an invitation, and the list assigns no roles. Leaving it empty means invited emails at any domain may register.
Removing a member
Removal is durable. When an admin removes someone in the Members pane, that person stays removed: signing in again, through any method including SSO, never silently reactivates the membership. They get a clear 403 instead.
The one deliberate exception is ADMIN_EMAILS: emails on that list are
reinstated as admin at their next sign-in, because that is the lockout
recovery path (below). So offboarding someone who is listed takes two steps:
remove them from the organization, and remove them from ADMIN_EMAILS and
restart the server. Also revoke any pending invitation for their email, since
a live invitation would let them register again.
ADMIN_EMAILS: the admin floor
ADMIN_EMAILS is a comma-separated list of emails that must always hold at
least the admin role in the instance organization:
Its semantics are deliberately strong:
- The floor is asserted at account creation and again at every sign-in, whichever sign-in method the user came in through.
- It is a floor, not a ceiling: removing an email from the list never demotes anyone. Day-to-day role management happens in the product.
- A listed user cannot be demoted below admin while listed, and the instance organization must always keep at least one active admin. Both invariants are enforced server-side.
- A listed user who was removed from the organization is reinstated as admin
at their next sign-in. That is deliberate (it is the lockout recovery
path), and it means offboarding a listed user requires removing them from
ADMIN_EMAILStoo. See Removing a member.
Lockout recovery
Because the floor is asserted at every sign-in, ADMIN_EMAILS is also the
recovery path when in-product role management has locked every admin out:
Add the email
Add your email to ADMIN_EMAILS in .env.static on the server host.
Restart the server
Apply the change by running ./update.sh in the deploy bundle (or restart
the stack with your usual Compose commands).
Sign in
Sign in from the desktop app. The server asserts the floor during login: you hold at least the admin role again, and if your membership in the organization had been removed entirely, it is reinstated at admin.
Disabling password sign-in
PASSWORD_AUTH_ENABLED is the kill switch for the whole password surface. It
defaults to true; setting it to false disables password login, credential
management, and account creation in one move: the /register page and
POST /auth/password/register return 404, exactly like password login
itself. Use it when your install runs
OIDC SSO or
GitHub sign-in and you
want no password path at all. Weigh it against the break-glass advice on the
SSO page: with password auth off, a broken identity provider has no password
fallback.
Coexistence with other methods
Email and password sign-in coexists with the other methods. If you later enable GitHub sign-in, a GitHub account whose email matches an existing password account is linked to it rather than creating a duplicate. Note that OAuth and SSO sign-ins are not gated by the invitation allowlist; see the method pages for who each one admits.