Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cryguy/hashboard/llms.txt

Use this file to discover all available pages before exploring further.

Local accounts let users register with a username and password without needing an external identity provider. This method works on any Hashboard instance and is the only option when OIDC is not configured. Passwords are hashed with scrypt using OWASP-recommended parameters, and the hash is stored in a dedicated local_credentials table that is deliberately kept separate from the principals table that flows into API responses.

Registration

1

First registration on a fresh instance

The very first registration on an instance with zero humans always succeeds, regardless of instance_settings. The registrant automatically receives the super role (superadmin). The zero-humans check runs inside the same database transaction as the INSERT, so two racing requests cannot both become superadmin.
curl -X POST https://hashboard.example.com/api/v1/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"hunter2"}'
2

Subsequent registrations

After bootstrap, registration is closed by default. An admin or super must open it before new accounts can be created.To open registration:
curl -X PATCH https://hashboard.example.com/api/v1/admin/settings \
  -H 'Authorization: Bearer hb_YOUR_ADMIN_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"registrationEnabled":true}'
3

Invite-only mode (optional)

If both registrationEnabled and inviteOnly are true, the register endpoint requires a valid invite code. Enable invite-only mode:
curl -X PATCH https://hashboard.example.com/api/v1/admin/settings \
  -H 'Authorization: Bearer hb_YOUR_ADMIN_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"registrationEnabled":true,"inviteOnly":true}'
Then create invite codes and distribute them:
curl -X POST https://hashboard.example.com/api/v1/admin/invites \
  -H 'Authorization: Bearer hb_YOUR_ADMIN_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'
The raw invite code is returned exactly once in this response and is never stored — only its hash is kept. Recipients pass it at registration:
curl -X POST https://hashboard.example.com/api/v1/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"username":"bob","password":"s3cret","inviteCode":"<raw-code>"}'

Registration request body

FieldTypeRequiredDescription
usernamestringUnique username. Stored lowercase.
passwordstringPlaintext password — hashed immediately, never stored raw.
displayNamestringDisplay name shown in the UI and API responses.
inviteCodestringRequired when inviteOnly is enabled.
A successful registration returns HTTP 201 with the principal row and sets the hb_session cookie.

Logging in

# Login — sets hb_session cookie
curl -X POST https://hashboard.example.com/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"hunter2"}' \
  -c cookies.txt

# Authenticated request using the session cookie
curl https://hashboard.example.com/api/v1/me -b cookies.txt
Login failures return a generic 403 regardless of whether the username exists. This prevents username enumeration. Rate limiting is applied per username + IP combination — a successful login clears the window, so honest retries never accumulate a penalty.

Logging out

POST /api/v1/auth/logout revokes the session behind the current hb_session cookie. The cookie is cleared from the browser.
curl -X POST https://hashboard.example.com/api/v1/auth/logout -b cookies.txt

Changing your password

POST /api/v1/me/password requires your current password and your new password. On success, it revokes every active session for your account and re-issues a fresh session for the current caller — you stay logged in on the device where you changed the password, and all other sessions are invalidated.
curl -X POST https://hashboard.example.com/api/v1/me/password \
  -H 'Content-Type: application/json' \
  -b cookies.txt \
  -d '{"currentPassword":"hunter2","newPassword":"correct-horse-battery"}'
FieldTypeRequired
currentPasswordstring
newPasswordstring

Adding local login to an OIDC account

If your account was provisioned by OIDC and you want to also be able to sign in with a username and password, POST /api/v1/me/credentials sets up local credentials on your existing account. Your username must be unique across the instance.
curl -X POST https://hashboard.example.com/api/v1/me/credentials \
  -H 'Authorization: Bearer hb_YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"correct-horse-battery"}'
Returns 201 on success.

Removing local credentials

DELETE /api/v1/me/credentials removes the local username and password from your account.
curl -X DELETE https://hashboard.example.com/api/v1/me/credentials \
  -H 'Authorization: Bearer hb_YOUR_TOKEN'
This will be refused with a 403 if local credentials are your only sign-in method. You must have an active OIDC identity on the account before removing your password.

How passwords are stored

Hashboard uses scrypt from Node’s built-in node:crypto module — no third-party dependency. The parameters follow OWASP recommendations:
ParameterValue
N131072 (2¹⁷)
r8
p1
key length32 bytes
The stored hash format is:
scrypt$N$r$p$<salt-base64url>$<key-base64url>
The parameters are embedded in the hash string itself, not hardcoded at verification time. This means the N, r, and p values can be increased for new accounts in a future release without invalidating any existing credentials — verification always replays the parameters that were used when the hash was created. Password hashes live in the local_credentials table, which is intentionally separate from the principals table. The principals row is the Actor type that flows into every API response, so keeping hashes in a separate table ensures the hash can never be accidentally included in an API response by a stray SELECT *.

Admin: resetting a user’s password

Admins can reset another user’s local password via POST /api/v1/admin/users/{id}/reset-password. This revokes all of that user’s sessions.
curl -X POST https://hashboard.example.com/api/v1/admin/users/USER_ID/reset-password \
  -H 'Authorization: Bearer hb_ADMIN_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"newPassword":"temporary-password-123"}'
The superadmin (super role) cannot have their password reset by another admin. Resetting a password is equivalent to taking over an account, and that route is closed around the no-demotion rule.

Build docs developers (and LLMs) love