Installer user accounts are entirely separate from DataMind OS application users. They are stored in the Installer's own database, created and removed through the Installer itself, and have no shared record, credential or lifecycle with the DataMind OS product.
Everything below is how identity is implemented in the Installer backend, not a description of a policy you are expected to follow.
Accounts live in the Installer's own PostgreSQL database, in a users table created and updated by
the Installer's TypeORM migrations. The Installer runs its own postgres container
(delamain-postgres) with a private data volume; that database serves the Installer and nothing
else.
There is no external identity provider and no environment-provided account. The JWT signing secret,
the encryption key, the PostgreSQL password and the service token are generated into the
delamain_secrets volume on first boot — none of them is an operator-supplied account.
A user record has: a UUID id, a unique email (stored lower-cased), a bcrypt password hash, an
isActive flag, a role, a passwordChangedAt timestamp, and creation/update audit columns. The
legacy name column is retained but auto-populated with the email and is not part of the API.
The very first account is created from the Installer's login screen on first run. When no user exists, the screen shows a registration form and calls:
# Creates the first administrator. Public — no session required. Fails once any user exists.
curl -X POST http://<installer-host>:<port>/api/user/register/initial \
-H 'Content-Type: application/json' \
-d '{"email":"admin@example.com","password":"<strong-password>"}'The backend refuses initial registration as soon as any user row exists, so this endpoint is a
bootstrap path only. The account it creates is always an admin. From then on the login screen shows
the sign-in form and self-registration is gone.
All of these require the admin role, except password change and reset.
POST /api/user/register with email, password and an optional role
(admin or user, defaulting to user). Admin-only. Re-registering the email of a previously
deleted user restores that account with the role you specify.PATCH /api/user/:id with isActive and/or role.
Admin-only. Deactivating invalidates the user's refresh tokens immediately; an inactive user
cannot sign in.POST /api/user/:id/change-password with oldPassword and
newPassword. Only the account owner, and only for their own id.POST /api/user/:id/reset-password with newPassword. An admin may
reset any account, including their own; a non-admin only their own. This revokes the target
user's refresh tokens.DELETE /api/user/:id. Admin-only, and a soft delete. The refresh tokens are
deleted and the account can no longer sign in; the row is retained. You cannot delete or
deactivate your own account, and you cannot change your own role.Roles are exactly two: admin and user. There are no finer-grained permissions or capabilities.
Passwords come from the API, validated by the backend's IsStrongPassword rule:
Passwords are hashed with bcrypt (a per-user salt) before storage. The same rules apply to registration, password change and admin reset. There is no password-expiry policy and no password history — the Installer does not enforce either.
Sign-in issues two JWTs, both set as httpOnly cookies:
| Token | Cookie | Path | Default lifetime |
|---|---|---|---|
| Access | delamain_access_token | /api | JWT_ACCESS_EXPIRY (shipped .env: 30m) |
| Refresh | delamain_refresh_token | /api/auth | JWT_REFRESH_EXPIRY (shipped .env: 7d) |
The JWT strategy also accepts the access token as an Authorization: Bearer header, which is how
the web UI and scripted calls usually present it. Both lifetimes are configurable in the deployment
.env; the code's own fallback when the variables are unset is 1h for the access token and 7d
for the refresh token.
Behaviour worth knowing:
sub, email and type: 'access'; the refresh token carries
type: 'refresh' and is stored hashed (SHA-256) in the refresh_tokens table.delamain_secrets volume (file jwt.secret), with file mode 0600. It is not set from the
environment.Issued cookies are httpOnly, and secure when the request arrived over HTTPS or behind a
TLS-terminating proxy that sets x-forwarded-proto: https. On plain HTTP they are not secure.
They are entirely separate. An Installer account is not a DataMind OS account, and a DataMind OS account is not an Installer account. The two systems keep their own user tables, their own roles and their own sign-in flows.
The consequences you must plan for:
GET /api/user list.This separation is the safe default: a compromise or a mistake in the DataMind OS application does not by itself hand anyone the Installer's host-level control, and vice versa.
There is one documented, first-party exception to "entirely separate", and it is not a user login.
The DataMind OS backend (referred to in the code as curato) can drive deployments on the Installer
on behalf of a DataMind OS user it has already authorized. It authenticates with a shared service
token (CURATO_SERVICE_TOKEN), not with an Installer account:
delamain_secrets volume (file curato.token) and
handed to the DataMind OS side through the platform .env. It is not operator-supplied and is
not an Installer user password.admin. The acting
DataMind OS user's id and email are forwarded in the x-actor-user-id and x-actor-email headers
purely so the deployment audit attributes the action to a person — those headers are not
authentication and never grant access on their own.So the practical rule stands: to sign in to the Installer you need an Installer account. The service-token path exists for the DataMind OS product to request deployment actions, and it leaves an audit record naming the DataMind OS user who asked.