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.

Hashboard reads its configuration exclusively from environment variables, which you supply either via a .env file (loaded by Vite in dev, or passed to Node via --env-file in production) or via the process environment directly. There is no config file format to learn. Start from .env.example in the repository — every variable is present and commented.

Environment variables

Database

DATABASE_URL
string
required
Path to the SQLite database file. Hashboard creates the file on first boot if it does not exist and applies pending migrations automatically. In production, this path must be on persistent storage — losing the file means losing all data.
DATABASE_URL=local.db           # dev default
DATABASE_URL=/data/hashboard.db # typical production path

Network and reverse proxy

ORIGIN
string
The public origin of the deployment, including scheme and host with no trailing slash (for example https://hashboard.example.com). Required in production for two reasons: adapter-node cannot infer the public origin on its own, and the OIDC redirect_uri is constructed from this value. Leave unset in development — Vite knows its own origin.
ORIGIN=https://hashboard.example.com
ADDRESS_HEADER
string
The HTTP header from which to read the real client IP address when Hashboard sits behind a reverse proxy. The login and registration rate limiters key on client IP — without this, adapter-node sees every request as originating from the proxy’s IP address.
ADDRESS_HEADER=X-Forwarded-For
XFF_DEPTH
number
The number of trusted reverse proxies in front of the app. Used together with ADDRESS_HEADER to correctly parse the X-Forwarded-For header when multiple proxies are in the chain. For a single reverse proxy, set this to 1.
XFF_DEPTH=1
Without ADDRESS_HEADER and XFF_DEPTH set behind a reverse proxy, every incoming request appears to originate from the same IP address — the proxy’s. The login and registration rate limiters collapse into one shared bucket for all visitors. A single abusive client can exhaust roughly 10 registration attempts per 15 minutes for every user on the instance. Set both variables whenever Hashboard is not directly internet-facing.

File attachments

ATTACHMENTS_DIR
string
default:"data/attachments"
Directory where uploaded file bytes are stored on disk. Attachment bytes are never written to SQLite — they live here. The directory is created on first upload if it does not exist.In production this path must be on the same persistent volume as DATABASE_URL. A backup that captures the database without this directory will restore an instance whose download links all fail. The Docker image places both under /data so a single volume backup is complete.
ATTACHMENTS_DIR=data/attachments
MAX_UPLOAD_MB
number
default:"25"
Maximum size of a single uploaded file, in megabytes. This is the per-upload limit enforced by the application. It must be set in coordination with BODY_SIZE_LIMIT — if BODY_SIZE_LIMIT is lower, uploads will be rejected by the Node adapter before the application-level check ever runs.
MAX_UPLOAD_MB=25
BODY_SIZE_LIMIT
string
default:"512K (adapter-node default)"
Maximum request body size accepted by adapter-node. Set this to a value comfortably above MAX_UPLOAD_MB. Use the suffixed format that adapter-node accepts: K for kilobytes, M for megabytes (for example 32M).
BODY_SIZE_LIMIT=32M
The adapter-node default body size limit is 512K. In development, Vite’s server has no such cap, so uploads of any size work fine locally. Once you build for production, every upload larger than 512K will fail with an opaque HTTP 413 before the request reaches your route handler — and there will be no error in the application log, only in the HTTP layer. Always set BODY_SIZE_LIMIT above MAX_UPLOAD_MB in production.

OIDC authentication

OIDC is entirely optional. If none of the three OIDC_* variables are set, Hashboard runs on local username/password authentication alone and the OIDC sign-in button is not shown. You can add OIDC credentials to an existing instance at any time — local accounts and OIDC-provisioned accounts coexist, and a single user can hold both.
Any standard OIDC provider works: Authentik, Keycloak, Dex, Auth0, Google, or any other IdP that supports the authorization code flow with PKCE. Hashboard performs standard OpenID Connect discovery against the issuer URL.
OIDC_ISSUER
string
The issuer URL of your OIDC provider. Hashboard appends /.well-known/openid-configuration to this URL to discover the authorization, token, and JWKS endpoints. For Authentik, this is the application’s OpenID Connect provider URL.
OIDC_ISSUER=https://authentik.example.com/application/o/hashboard/
OIDC_CLIENT_ID
string
The client ID issued by your OIDC provider for this Hashboard application.
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET
string
The client secret issued by your OIDC provider. Keep this out of version control.
OIDC_CLIENT_SECRET=your-client-secret

TLS requirement

TLS is mandatory in production. The production build sets the Secure flag on the hb_session session cookie. Over plain HTTP on any non-localhost address, the browser accepts the cookie on the login response but silently refuses to send it back on subsequent requests — sign-in appears to succeed and then the session is immediately lost, with no error in the UI or the server log. If sign-in “does nothing” on a LAN address, put TLS in front or test on localhost.

Quick-reference table

VariableDefaultRequiredDescription
DATABASE_URLlocal.dbAlwaysPath to the SQLite file. Must be on persistent storage in production.
ORIGIN(none)ProductionPublic origin, e.g. https://hashboard.example.com. Required for OIDC and adapter-node.
ADDRESS_HEADER(none)Behind proxyHeader to read real client IP from, e.g. X-Forwarded-For.
XFF_DEPTH(none)With ADDRESS_HEADERNumber of trusted proxies in front of the app, e.g. 1.
ATTACHMENTS_DIRdata/attachmentsOptionalDisk path for uploaded file bytes. Must be on persistent storage.
MAX_UPLOAD_MB25OptionalPer-upload size limit in MB. Enforced by the application layer.
BODY_SIZE_LIMIT512KProduction uploadsadapter-node request body cap. Must exceed MAX_UPLOAD_MB or uploads 413.
OIDC_ISSUER(none)OIDC authIssuer URL for OpenID Connect discovery.
OIDC_CLIENT_ID(none)OIDC authOIDC client ID from your identity provider.
OIDC_CLIENT_SECRET(none)OIDC authOIDC client secret from your identity provider.

Runtime admin settings

Some instance-wide toggles are stored in the database rather than in environment variables, and are managed at runtime through the admin API. They apply immediately without a restart. These settings are available to users with the admin or super role via PATCH /api/v1/admin/settings.
Controls whether new users can register local accounts. Defaults to closed after the first user is created — the bootstrap exception that lets the first user register is a one-time gate that runs inside the insert transaction.
# Open registration
curl -X PATCH https://hashboard.example.com/api/v1/admin/settings \
  -H "Authorization: Bearer hb_..." \
  -H "Content-Type: application/json" \
  -d '{"registrationEnabled": true}'

# Close registration
curl -X PATCH https://hashboard.example.com/api/v1/admin/settings \
  -H "Authorization: Bearer hb_..." \
  -H "Content-Type: application/json" \
  -d '{"registrationEnabled": false}'
When enabled alongside open registration, new users must supply a valid single-use invite code to complete registration. Invite codes are issued by admins via POST /api/v1/admin/invites. Only the raw code is shown at creation time — the hash stored in the database cannot be reversed.
# Enable invite-only registration
curl -X PATCH https://hashboard.example.com/api/v1/admin/settings \
  -H "Authorization: Bearer hb_..." \
  -H "Content-Type: application/json" \
  -d '{"registrationEnabled": true, "inviteOnly": true}'

# Create an invite code (shown once)
curl -X POST https://hashboard.example.com/api/v1/admin/invites \
  -H "Authorization: Bearer hb_..."

Backups

A complete backup of a Hashboard instance requires both the database file and the attachments directory. Backing up one without the other produces a partial restore. For a live backup with no downtime, use SQLite’s VACUUM INTO to produce a consistent single-file snapshot:
sqlite3 hashboard.db "VACUUM INTO '/backups/hashboard-$(date +%F).db'"
Then sync the attachments directory — files are immutable once written, so a plain recursive copy is always safe:
rsync -a data/attachments/ /backups/attachments/
Take the database snapshot first. In that order the worst case is a file on disk with no database row pointing at it, which is invisible. The reverse order risks database rows that reference bytes the backup does not contain.

Build docs developers (and LLMs) love