hms-server authentication and authorization¶
Modes¶
| Mode | How | Who it is for |
|---|---|---|
| Per-tenant keys | --keys-file keys.json (HMS_KEYS_FILE) | Production. Keys are bound to tenants and scopes, stored only as SHA-256 digests, expire and rotate without a restart. |
| One shared key | HMS_API_KEY (environment only) | Small deployments and the original SDK setup. The key is valid for every tenant with every scope. May be combined with a key file. |
| No authentication | --no-auth (HMS_NO_AUTH) | Local development. Every caller has every scope. Refused unless the listener is loopback or a unix socket, refused together with credentials, and logged as a warning at startup. |
With neither a key nor --no-auth the server does not start. There is no implicit open mode.
Tokens and storage¶
A token looks like hms_<id>.<64 hex digits>: the key id, then 256 bits from the operating system's random generator. It is shown once, by hms-server keygen. The server keeps sha256(token) only. A presented token is hashed and the digests are compared in constant time, so neither the comparison time nor the token length reveals anything about a stored value. SHA-256 is the right primitive here because tokens carry 256 bits of entropy; a slow password hash would only cost throughput.
The key id appears in access logs (key_id), never the secret.
Creating keys¶
hms-server keygen --id acme-reader --tenants acme --scopes read --keys-file keys.json
# prints the token on stdout; the entry is appended to keys.json (mode 0600, written atomically)
hms-server keygen --id ops --tenants '*' --scopes admin --expires-in-days 90 --keys-file keys.json
Without --keys-file the entry is printed on stderr for you to place yourself.
Key file format¶
{
"keys": [
{
"id": "acme-reader",
"hash": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"tenants": ["acme"],
"scopes": ["read"],
"not_before": 1767225600,
"expires_at": 1798761600,
"disabled": false
}
]
}
| Field | Meaning |
|---|---|
id | 1 to 32 characters of [A-Za-z0-9_-], unique. |
hash | sha256: plus 64 hex digits of the full token. |
tenants | Tenant ids the key may use, or ["*"] for every tenant (not combinable with ids). |
scopes | Any of read, write, admin. |
not_before, expires_at | Optional Unix seconds. Outside the window the key is rejected. |
disabled | Optional. A disabled key is rejected. |
An invalid file is refused at startup. On reload, an invalid file is rejected and the previous keys stay in force.
Scopes¶
| Scope | Allows |
|---|---|
read | Query, list, stats, compaction status. |
write | Add, replace and delete documents (single, batch, stream), index maintenance. |
admin | Everything read and write allow, plus snapshots, restore, compaction runs, and /metrics (unless --metrics-public). Key reload and tenant listing additionally need a key valid for every tenant. |
write does not include read; give a service that ingests and queries both. The required scope of every operation is in the OpenAPI document as its security requirement, and a test checks the server against it.
Tenants¶
A key bound to one tenant needs no X-Tenant-ID; its tenant is the default. A key bound to several tenants, or to *, selects with X-Tenant-ID (default default). Naming a tenant outside the key's list is 403 tenant_forbidden. Because a key can only reach its own tenants, a leaked key cannot create stores for tenants it was not issued for.
Rotation¶
hms-server keygena new key for the same tenants and scopes; add it to the key file.- Reload:
kill -HUP <pid>orPOST /api/v1/admin/keys/reloadwith an all-tenant admin key. Both keys are now valid. - Roll the new token out to clients.
- Set
"disabled": true(or remove the entry) on the old key and reload. Or issue keys with--expires-in-daysso the old one lapses by itself.
Rotating HMS_API_KEY needs a restart; prefer a key file where rotation matters.
What this does not do¶
- No per-request signing and no mutual TLS; transport security is TLS (
--tls-cert) or a terminating proxy. - No lockout of repeated failures.
hms_auth_failures_totalcounts them; apply network-level controls for guessing attacks (the tokens are 256-bit random, so online guessing is not practical). - No per-key rate limits; limits are per tenant (
--rate-limit-rps).