diff --git a/docs/gateway.md b/docs/gateway.md index 592a5269..2be01b92 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -466,56 +466,3 @@ follow-up. display in the credential dialog. Defaults to `"hyperhive"`. Must not contain `"` or `$`. -**Custom 401 page:** when credentials are absent or wrong, nginx serves -a Catppuccin-styled `unauthorized.html` page (built into the same Nix -derivation as the agent error pages) that tells the operator which -`hivectl` command to run to create a user. The response status is still -`401` (`error_page 401 =401 /__hive_auth_unauthorized`) so browsers -present the login dialog on the first visit — users who dismiss the -dialog see the human-readable hint. The internal exact-match location -(`= /__hive_auth_unauthorized`) beats `location /` in nginx's prefix -ordering, preventing the subrequest from looping back through -`auth_basic`. - -## Security headers - -The following headers are emitted at server scope on every gateway -vhost (`_`, `forge.`, `matrix.`): - -| Header | Value | -|--------|-------| -| `X-Frame-Options` | `SAMEORIGIN` | -| `X-Content-Type-Options` | `nosniff` | -| `Referrer-Policy` | `strict-origin-when-cross-origin` | - -nginx's `add_header` inheritance rule: a `location` block that sets its -own `add_header` does **not** inherit server-scope headers. API locations -that carry their own CORS headers (e.g. `/.well-known/matrix/client`, -`/_matrix/`) are therefore unaffected. HTML-serving and proxy locations -with no `add_header` of their own pick the security headers up -automatically. - -### HSTS (`gateway.hsts`) - -HSTS is **opt-in** and disabled by default: - -```nix -services.hyperhive.gateway.hsts = { - enable = true; # default: false - maxAge = 31536000; # default: 1 year (required for preload list) - includeSubDomains = true; # default: true -}; -``` - -When enabled, a `Strict-Transport-Security: max-age=...[; includeSubDomains]` -header is added alongside the other security headers. - -**Opt-in rationale**: HSTS pins HTTPS in the browser's preload cache; -enabling it on a deployment that later loses TLS locks browsers out -until `max-age` expires. Only enable when TLS is permanent. - -**Assertion**: `hsts.enable = true` without a TLS mode configured -(`selfSignedTls`, `tls.certDir`, or `tls.acme.enable`) is a NixOS -build-time assertion failure — HSTS over plain HTTP is harmless but -almost always a misconfiguration. -