Users and registration
Conatus has exactly two instance-level roles: administrator and member. That is separate from project roles, which are per project. See Collaboration.
The first account
Section titled “The first account”The first account created on an empty database becomes the instance administrator. There are two ways to create it.
Register in the browser
Section titled “Register in the browser”Go to /register on a fresh instance. Because there are no accounts yet,
registration is open for exactly one signup. Fill in a username and password and
you are the administrator.
Bootstrap from .env
Section titled “Bootstrap from .env”Set both values before the first docker compose up:
CONATUS_ADMIN_USERNAME=adminCONATUS_ADMIN_PASSWORD=replace-with-a-long-random-passwordA one-shot job creates a verified administrator, but only if the database is empty. On an instance that already has accounts it does nothing.
Requirements: the username must be 3–32 characters of letters, numbers, dots, underscores or hyphens, and the password at least 8. Both must be set together or the job errors out rather than half-configuring the instance.
After the first login, remove both values from .env and delete the stopped
bootstrap container.
Both paths take the same database lock, so two of them racing cannot both decide they are first.
Registration modes
Section titled “Registration modes”REGISTRATION_MODE controls what happens after that first account.
| Value | Behaviour |
|---|---|
invite-only (default) |
Signup requires a one-time link from an administrator |
open |
Anyone who can reach /register can create an account |
Invitations
Section titled “Invitations”Administrators get a Registration section in Settings.
Generate a link and you get a URL of the form
https://tasks.example.com/register?invite=…. Send it to the person by whatever
channel you trust. Conatus does not email it.
An invitation is:
- one-time, consumed by the account it creates;
- valid for 7 days;
- optionally locked to a username. Leave the username blank for a shareable link that anyone can use once; fill it in and the link only works for that exact username.
The Settings list shows active invitations only, and lets you revoke one before it is used. A username can hold only one active invitation at a time.
Rejected signups are told why: the link is invalid, expired or already used; it was issued for a different username; or registration requires an invitation.
Generating a link requires the instance to know its own public URL. Behind a
proxy, that means AUTH_URL and PUBLIC_BASE_URL
must be set, otherwise the request fails rather than producing a link pointing at
the wrong host.
The administrator role
Section titled “The administrator role”The administrator is the account that bootstrapped the instance. It can:
- generate and revoke registration invitations.
That is the whole of it. There is no user-management screen, no ability to reset another account’s password, no impersonation and no cross-account data access. An administrator has no privileged view of anyone else’s tasks.
One invariant is enforced: an administrator cannot delete their own account while other accounts exist, so an instance cannot be orphaned. The last remaining account may delete itself, which reopens first-user bootstrap.
Accounts and passwords
Section titled “Accounts and passwords”| Rule | Value |
|---|---|
| Username | 3–32 characters, lowercase letters, numbers, dots, underscores, hyphens |
| Username uniqueness | Case-insensitive, normalised to lowercase |
| Password | Minimum 8 characters, no other requirement |
| Optional, unique, and only needed for reminder emails and password reset |
Usernames cannot be changed after registration.
Password reset and email verification need SMTP configured. Without
SMTP_HOST, SMTP_PORT and SMTP_FROM, an account with a forgotten password
cannot be recovered from inside the app. Plan for that when running a
username-only instance.
Deleting an account cascades everything it owns, including projects shared with other people.
Rate limits
Section titled “Rate limits”All counted in memory, per app container:
| Action | Limit |
|---|---|
| Login and device-token, per username | 5 per 5 minutes |
| Login and device-token, per IP | 20 per 5 minutes |
| Registration, per IP | 5 per 15 minutes |
| Registration, per username | 5 per 15 minutes |
| Password reset request, per IP | 10 per 15 minutes |
| Password reset request, per email | 3 per 15 minutes |
| Verification resend, per IP | 10 per 15 minutes |
| Verification resend, per email | 3 per 15 minutes |
| Client error reports, per IP | 30 per 5 minutes |
Login and device-token share one budget: both accept a password, so separate budgets would let an attacker double their allowance by switching entry points.
Rate-limited responses are 429 with a Retry-After header.
Client IPs come from X-Forwarded-For or X-Real-IP, so a reverse proxy in
front of Conatus must set them, and must strip client-supplied copies, or the
limits are easy to bypass. See
Reverse proxy.
