Hinatadocs

Configuration reference

Every setting of a Hinata server is an environment variable. Set it in .env (for Docker Compose) or directly on the container. The tables list each area with the purpose, a default or example, and whether the variable is required.

SSO, inbound e-mail, push and the Git OAuth apps live in the database instead. You manage them in the app's Admin area. The last section explains how the two fit together.

!!! tip "You do not need .env" .env is just a convenient way to load values for Compose. Your orchestrator can set any of them as environment variables on the container, with the same names and meaning.

Core / URLs

VariablePurposeDefault / exampleRequired
SPRING_PROFILES_ACTIVEActive profile: prod (replica set, X.509) or dev (standalone)prodYes
HINATA_BASE_URLPublic API base URL. Used as JWT issuer and SSO redirect basehttps://api.track.example.comYes
HINATA_WEB_BASE_URLBase URL of the Flutter web app. E-mail deep links point here. Blank ⇒ falls back to the base URLhttps://track.example.comNo

Container images

VariablePurposeDefault / exampleRequired
HINATA_SERVER_TAGTag of ghcr.io/hinata-platform/hinata-server to runlatest (pin e.g. 10.4.0)No
HINATA_APP_TAGTag of ghcr.io/hinata-platform/hinata-app (web app overlay)latest (pin e.g. 10.4.0)No

Pin both tags to a specific version in production. Every host then runs the same build, and a rollback is a one-line change.

Security / JWT

VariablePurposeDefault / exampleRequired
HINATA_JWT_SECRETHS512 signing secret, ≥ 64 chars. Generate: openssl rand -base64 64 | tr -d '\n'(empty)Yes (prod)

In the prod profile the server will not start without a valid HINATA_JWT_SECRET. Rotating it invalidates all existing tokens.

MongoDB

VariablePurposeDefault / exampleRequired
MONGO_ROOT_USERNAMESCRAM root username (internal admin use only, the app authenticates with X.509)hinataYes
MONGO_ROOT_PASSWORDSCRAM root passwordhinata-dev-secret (change it)Yes (prod)
HINATA_MONGODB_URIMongo connection string. In prod the X.509 URI is set in docker-compose.yml. Only set it yourself for dev or external Mongo(set in compose)No (prod)
HINATA_MONGO_TLS_ENABLEDEnable TLS for the Mongo connectiontrue (prod, in compose)No
HINATA_MONGO_TLS_KEYSTOREPath to the app's PKCS#12 client keystore in the container/etc/hinata/x509/hinata-app.p12No (prod, in compose)
HINATA_MONGO_TLS_KEYSTORE_PASSWORDPassword for the client keystorechangeit (change it)Yes (prod)
HINATA_MONGO_TLS_TRUSTSTOREPath to the CA truststore in the container/etc/hinata/x509/truststore.p12No (prod, in compose)
HINATA_MONGO_TLS_TRUSTSTORE_PASSWORDPassword for the truststorechangeit (change it)Yes (prod)

See MongoDB & X.509 for how to generate the PKI and register the $external user.

Reverse proxies

VariablePurposeDefault / exampleRequired
HINATA_TRUSTED_PROXIESComma-separated CIDRs of reverse proxies allowed to set X-Forwarded-For. Empty = trust none172.16.0.0/12Recommended

Set this to exactly the address range your proxy reaches the container from.

  • Empty: the server ignores forwarded headers. Rate limiting and logs only see the proxy IP.
  • Too wide: clients can spoof their source IP.

SMTP (outbound mail)

VariablePurposeDefault / exampleRequired
HINATA_SMTP_HOSTSMTP relay hostsmtp.example.com (mailpit in dev)Yes (for mail)
HINATA_SMTP_PORTSMTP port587 (1025 for Mailpit)Yes (for mail)
HINATA_SMTP_USERNAMESMTP auth username(empty)If auth
HINATA_SMTP_PASSWORDSMTP auth password(empty)If auth
HINATA_SMTP_AUTHEnable SMTP authenticationtrue (false in dev)No
HINATA_SMTP_STARTTLSEnable STARTTLStrue (false in dev)No
HINATA_MAIL_FROMFrom address on outbound mailhinata@example.comYes (for mail)

E-mails with deep links (verification, password reset, assignment notifications) are only delivered with a real relay. See E-mail & SMTP.

Object storage (S3 / MinIO / GCS / Azure)

VariablePurposeDefault / exampleRequired
HINATA_STORAGE_PROVIDERBackend: s3 (MinIO, AWS S3, GCS interop, R2, Spaces, …) or azure (Azure Blob Storage)s3No
COMPOSE_PROFILESlocal-storage runs the bundled MinIO. Empty when using an external storelocal-storageNo
MINIO_ROOT_USERMinIO root user (also used as the S3 access key in compose)hinataWith bundled MinIO
MINIO_ROOT_PASSWORDMinIO root password (also the S3 secret key in compose)hinata-dev-secret (change it)With bundled MinIO (prod)
HINATA_S3_ENDPOINTS3 endpoint the server talks tohttp://minio:9000 (in compose)External S3
HINATA_S3_ACCESS_KEYS3 access key (dev / external S3)hinataDev / external
HINATA_S3_SECRET_KEYS3 secret key (dev / external S3)hinata-dev-secretDev / external
HINATA_S3_BUCKETBucket (S3) or container (Azure) for attachments and avatarshinataNo
HINATA_S3_REGIONBucket region (AWS and region-aware providers)us-east-1External S3
HINATA_S3_ADDRESSING_STYLES3 URL addressing: auto, virtual-host or pathautoNo
HINATA_AZURE_CONNECTION_STRINGAzure storage account connection string (with provider=azure)(empty)Azure

In production compose, HINATA_S3_ACCESS_KEY / HINATA_S3_SECRET_KEY fall back to MINIO_ROOT_USER / MINIO_ROOT_PASSWORD automatically. See Object storage for setup per provider (AWS, GCS, Azure, R2, …).

App integration

VariablePurposeDefault / exampleRequired
HINATA_PRIVACY_POLICY_URLPrivacy policy URL shown in the app (required for store releases)https://example.com/privacyRecommended
HINATA_APP_MIN_VERSIONMinimum app version. Older clients are forced to update1.0.0No
HINATA_CORS_ALLOWED_ORIGINSComma-separated browser origins allowed for CORS (the web app calls cross-origin)https://track.example.comYes (web)
HINATA_DOCS_ENABLEDExpose the Scalar API docs UIfalseNo

Hinata Connect gateway

VariablePurposeDefault / exampleRequired
HINATA_GATEWAY_BASE_URLGateway URL for push and universal links. Defaults to the hosted gateway. Only override it when you ship your own branded app with your own gatewayhttps://connect.hinata.ahmadre.comNo

See Hinata Connect gateway.

Setup (first run)

VariablePurposeDefault / exampleRequired
HINATA_SETUP_AUTO_COMPLETESkip the in-app first-run wizardfalseNo
HINATA_SETUP_ORGANIZATION_NAMEOrganization name (with auto-complete)(empty)If auto-complete
HINATA_SETUP_ADMIN_EMAILFirst admin e-mail(empty)If auto-complete
HINATA_SETUP_ADMIN_USERNAMEFirst admin username(empty)If auto-complete
HINATA_SETUP_ADMIN_PASSWORDFirst admin password(empty)If auto-complete
HINATA_SETUP_ADMIN_DISPLAY_NAMEFirst admin display name(empty)If auto-complete

See Setup & first run.

Demo seed (dev only)

VariablePurposeDefault / exampleRequired
HINATA_DEMO_SEEDSeed a realistic English demo workspace. Login rebar / hinata-demo-2026. Skipped under prod (seeder is @Profile("!prod"))falseNo
HINATA_DEMO_RESETWipe and re-seed the workspace on every boot. Requires HINATA_DEMO_SEED=truefalseNo

Never enable the demo seed in production

It creates an admin with a known password and throwaway data. The seeder is compiled out under the prod profile. Keep HINATA_DEMO_SEED=false in production anyway.

Rate limiting / brute force

VariablePurposeDefault / exampleRequired
HINATA_RATE_LIMIT_ENABLEDEnable per-IP rate limiting (bucket4j)trueNo
HINATA_RATE_LIMIT_APIGeneral API budget (requests / minute)300No
HINATA_RATE_LIMIT_AUTHAuth endpoints budget (requests / minute)10No
HINATA_RATE_LIMIT_REFRESHBudget for exchanging a refresh token (requests / minute). Its own, because presenting a token this server signed is not a guess at a password, and every client behind one address shares the sign-in budget60No
HINATA_RATE_LIMIT_SSOBudget for the two redirects of a single sign-on (requests / minute). Its own, because a whole office behind one address signs in through it while the callback is reachable by anybody60No
HINATA_MAX_LOGIN_FAILURESFailed logins before an account is blocked5No
HINATA_LOGIN_BLOCK_MINUTESHow long a blocked account stays locked (minutes)15No

Login blocking is stored in the database, so it survives restarts. See the Security model.

Ports

VariablePurposeDefault / exampleRequired
HINATA_PORTPublished host port for the API (container 8080). The reverse proxy forwards here3356No
HINATA_APP_PORTPublished host port for the web app (container 80)3456No

Git integration

Platform-wide OAuth credentials for connecting projects to GitHub, GitLab or Bitbucket. You can also set them at runtime in Admin → Git integration, which overrides env. See Git integration.

VariablePurposeDefault / exampleRequired
HINATA_GIT_GITHUB_CLIENT_IDGitHub OAuth app client ID(empty)If GitHub
HINATA_GIT_GITHUB_CLIENT_SECRETGitHub OAuth app client secret(empty)If GitHub
HINATA_GIT_GITLAB_CLIENT_IDGitLab OAuth app client ID(empty)If GitLab
HINATA_GIT_GITLAB_CLIENT_SECRETGitLab OAuth app client secret(empty)If GitLab
HINATA_GIT_BITBUCKET_CLIENT_IDBitbucket OAuth consumer key(empty)If Bitbucket
HINATA_GIT_BITBUCKET_CLIENT_SECRETBitbucket OAuth consumer secret(empty)If Bitbucket
HINATA_GIT_WEBHOOK_BASE_URLPublic API base for the OAuth callback and webhook registration. Falls back to HINATA_BASE_URL + /api/v1https://api.track.example.com/api/v1No
HINATA_GIT_TOKEN_SECRETAES-GCM key that encrypts stored access tokens at rest. Change the default in production(default; change it)Recommended

Calendars, holidays and working hours

Organization admins keep holiday calendars on the Organization page and can import a year of holidays from a calendar address, for example a public holiday calendar from Google, Apple or Outlook. The server fetches that address itself. It never connects to a private or loopback address, whatever the lists below say. People plan their own working hours in their settings, and their absences in time tracking. All of it needs extended time tracking (HINATA_TIME_TRACKING_ADVANCED_ENABLED). See Tracking your time.

VariablePurposeDefault / exampleRequired
HINATA_ICS_SECRETKey that stored calendar addresses are encrypted with. Base64 of at least 32 bytes. Generate: openssl rand -base64 32. Without it the server refuses to store a calendar address, and holidays can only be entered by hand(empty)For calendar addresses
HINATA_ICS_ALLOWED_HOSTSComma-separated hosts calendars may be fetched from: a host name or *.example.org for its subdomains. Empty means any public host(empty)No
HINATA_ICS_DENIED_HOSTSComma-separated hosts calendars are never fetched from, in the same notation. Checked before the allow list(empty)No
HINATA_AVAILABILITY_DEFAULT_WEEKDAY_MINUTESPlanned minutes per weekday, Monday first, for everyone who has not set their own hours480,480,480,480,480,0,0No
HINATA_TIME_TRACKING_ABSENCE_MANAGEMENT_ENABLEDTurns absence management on: types, entitlements, balances, requests and sick reports. Needs extended time tracking. The switch under Organization → Time tracking takes precedence over this valuefalseNo
HINATA_TIME_TRACKING_ABSENCE_CALENDAR_VISIBILITYThe team absence calendar: OFF, BUSY_ONLY (only that somebody is away) or TYPE (the type, never sickness). Only in force with absence management on. The choice under Organization → Time tracking takes precedence over this valueOFFNo
HINATA_TIME_TRACKING_WORKLOAD_REPORTS_ENABLEDSwitches on the workload report under Time tracking → Reports: capacity against booked time per person, for organization admins and project leads only. Needs extended time tracking. The switch under Organization → Time tracking takes precedence over this value. See Time tracking privacyfalseNo
HINATA_RATE_LIMIT_TIME_OFF_REQUESTS_PER_DAYAbsence requests and sick reports per person per day. A day rather than a minute, because filing leave is a deliberate act and a per-minute budget would let a loop through anyway50No

Who keeps absences

Organization admins do, unless you say otherwise. Under Organization → Time tracking you can also name individual people as absence keepers. They keep types, grant entitlements, book corrections and decide requests without holding any other admin right. Their way in is in their own settings. The list is deliberately empty by default: whoever is on it sees sick days as sick days (Art. 9 GDPR), and the narrower circle is the right default. Once someone is named, organization admins only see that a person is away when they are sick, not why. With nobody named, organization admins keep absences themselves as before.

The limit never blocks a sick report

A sick report is counted against the same daily budget but is never refused by it: § 5 EFZG knows a notification, not a permission. Reaching the limit only refuses further requests.

Keep the key

A calendar address encrypted with one HINATA_ICS_SECRET cannot be read with another. If the key changes, imports from stored addresses fail until an organization admin enters the address again. Holidays that were already imported stay.

Project templates and relative deadlines

A project can be copied, marked as a template and given an event date that its issues' deadlines hang on. With this off, projects behave exactly as they do today and there is no way to copy one. See Project templates.

VariablePurposeDefault / exampleRequired
HINATA_PROJECT_TEMPLATES_ENABLEDTurns project templates and relative deadlines on: copy a project, mark one as a template, create a project from a template, and keep a deadline as an offset from the project's event date. The switch under Admin area → Platform takes precedence over this valuefalseNo
HINATA_PROJECT_TEMPLATES_DEFAULT_BASISWhat new relative deadlines count in while neither the organization nor the project sets its own: CALENDAR (calendar days) or WORKING (working days). Organization admins choose the default on the Organization page, and each project can differ from it. Existing deadlines keep their basisCALENDARNo

Working days need a holiday calendar

A deadline that counts in working days always skips weekends. It only skips public holidays when the project has a holiday calendar selected. Without one it counts a holiday like any other working day.

Runtime (DB) settings vs environment

Hinata has two configuration planes.

Environment variables (this page) are read at startup. They cover infrastructure and secrets: URLs, the JWT secret, database and storage connection, TLS, SMTP transport, ports, CORS, trusted proxies and rate limits. To change one, edit .env and restart the container.

Runtime settings are stored in MongoDB. You edit them in the app's Admin area while the server runs:

  • SSO providers: OpenID Connect, OAuth 2.0, SAML 2.0, LDAP (SSO)
  • IMAP ingestion for E-mail → ticket (E-mail to ticket). Administrators can only route mail into projects they are a member of
  • Push configuration via the gateway
  • OAuth app credentials for Git integration (the HINATA_GIT_* values above)
  • Platform settings under Admin → Platform: minVersion, the privacy URL, how people sign in (localAuthEnabled, registrationEnabled, requireAdminApproval) and what the platform offers

Time tracking, absences, holidays and the default for relative deadlines are stored in MongoDB too. They are not part of the Admin area, though, but of the Organization page, which organization admins edit.

Three rules apply:

  1. DB overrides env. If a setting exists in both, the database value wins. This mainly affects Git OAuth credentials and the app settings (hinata.app.*). Env values are only the initial default and fallback.
  2. No restart needed. Changes in the Admin area apply immediately, without a redeploy.
  3. Secrets are write-only. The admin API never returns OAuth secrets, tokens or passwords after they are saved. You can set or replace them, but not read them.

Rule of thumb

If it is a connection string, a transport secret or something the process needs before it can serve a request, it is an environment variable. If it is an integration you would change on a live system, it is a runtime setting in the Admin area.