OpenDevOps supports optional password-based authentication with two roles: admin and user. Auth is disabled by default — set JWT_SECRET to enable it.
Add to your .env:
JWT_SECRET=your-secret-key-here # any long random string
JWT_EXPIRE_MINUTES=1440 # optional, default 24hRestart the server. The login page will appear on next browser load.
Without JWT_SECRET: the app runs in dev/open mode — no login required, all users treated as admin. Suitable for local development or single-user installs on a trusted network.
Auth works with all storage backends, but persistence follows the backend:
| Backend | Auth behavior |
|---|---|
memory |
Users exist only until the server restarts; useful for quick local testing |
sqlite |
Users persist in the local SQLite database |
postgres |
Users persist in PostgreSQL and are suitable for production/team use |
The first user to register automatically gets the admin role. Subsequent registrations default to user.
Important for users upgrading from a pre-RBAC version: if your database already had rows in the
userstable before running migration004_users_rbac.sql, those existing rows don't have passwords. The "first user" check (count_users) only counts rows with apassword_hash, so the first person to register after the migration will correctly get admin.
| Role | Can do |
|---|---|
admin |
Everything — chat, history, dashboard, settings, manage users |
user |
Chat, history, dashboard, settings — cannot access /users (Team page) |
Auth bypass (no JWT_SECRET): all requests are treated as admin regardless of role.
Returns whether auth is required. Called by the frontend on load.
{ "required": true }Registers a new user. Body: { email, name, password }. Returns a JWT.
The first user with a password_hash becomes admin; all subsequent registrations get role = "user".
Body: { email, password }. Returns a JWT on success, 401 on bad credentials.
Requires Authorization: Bearer <token>. Returns the current user's profile.
{ "id": "uuid", "role": "admin", "name": "Ahmad Hammad", "auth_enabled": true }| Endpoint | Description |
|---|---|
GET /users |
List all users |
POST /users |
Create a user (email, name, password, role) |
PATCH /users/{id} |
Update name, role, or password |
DELETE /users/{id} |
Delete a user |
Non-admins receive 403 Forbidden on all /users endpoints.
In the UI: navigate to Team in the sidebar (visible to admins only).
- Tokens are signed with
HS256usingJWT_SECRET - Payload:
{ sub: user_id, role, exp } - Stored in
localStorageasauth-token - Sent as
Authorization: Bearer <token>on every API request - Expiry defaults to 24 hours (
JWT_EXPIRE_MINUTES=1440)
Run migrations/004_users_rbac.sql on your database:
psql $DATABASE_URL -f migrations/004_users_rbac.sqlThis adds password_hash and role columns to users, and drops the unused owner_key column from sessions. Safe to re-run.