Authentication¶
Hermes Web UI uses one shared secret per instance — a server password — and a session cookie. There is no per-user account system: whoever holds the password (or is admitted by your reverse proxy) is the operator, and gets the full API surface. This keeps a single-user, self-hosted deployment simple, and every client authenticates the same way.
The model at a glance¶
| Mechanism | Use when |
|---|---|
| No auth | The instance has no password set (password_required: false). Call the API directly. |
| Password → session cookie | The default. POST /api/auth/login with the password; the server sets a hermes_session cookie you replay on every request. |
| Custom headers | The instance is behind an authenticating reverse proxy (Authentik, Cloudflare Access) or a token gate. Attach headers like Authorization: Bearer … to every request. |
Probe first: /api/auth/status¶
Always public. Tells a client which path to take.
password_required: false→ open instance, no login needed.authenticated: true→ your current cookie/headers are already valid.authenticated: false+password_required: true→ log in.
Password login: /api/auth/login¶
On success (HTTP 200) the response carries a Set-Cookie: hermes_session=… header. Store it and send it on every subsequent request. On a wrong password the server returns 401 (and rate-limits repeated attempts).
- Cookie name:
hermes_sessionby default. Operators running multiple instances on one hostname can override it viaHERMES_WEBUI_COOKIE_NAME, so a robust client reads the cookie name from theSet-Cookieresponse rather than hard-coding it. - Logout:
POST /api/auth/logoutclears the session.
Native clients¶
Use a cookie-persisting HTTP session and login is transparent after the first call:
- iOS / Swift —
URLSessionwith the defaultHTTPCookieStoragestores and replayshermes_sessionautomatically. (The reference clienthermexdoes exactly this.) - Android / Kotlin — add a
JavaNetCookieJar(CookieManager())to yourOkHttpClient. - Browser / JS — use
fetch(url, { credentials: "include" })(same-origin) so the cookie rides along.
Custom headers (proxied / token-gated deployments)¶
Some self-hosters put Hermes behind an authenticating proxy or a token gate that the password flow can't satisfy. For those, attach one or more fixed headers to every outgoing request:
The reference iOS client persists these in the Keychain and injects them on all requests (see its CustomHeader type). Security note for client authors: do not forward Authorization (and other sensitive headers) across a cross-origin redirect — strip them if the server redirects off-origin, to avoid leaking the secret.
What requires auth¶
Nearly the entire /api/* surface requires a valid session when a password is set. A small set of paths are always public: /api/auth/login, /api/auth/status, static assets under /static/, and the login page. Everything else in this reference assumes an authenticated request.
Errors¶
| Status | Meaning | Client action |
|---|---|---|
401 |
Not authenticated / wrong password | Prompt for the password, call /api/auth/login, retry |
403 |
Authenticated but the action is refused | Surface the message; do not retry blindly |
429 |
Too many login attempts | Back off and tell the user to wait |
See Conventions for the standard error body shape.