docs: suppress reviewed write-good.Passive false positives
133 hits across 38 files, all previously classified during #4548's sweep and deliberately left un-rewritten (predicate-adjective state/necessity description, design-intent idiom, structural/type-description idiom, no-single-actor topology claim, parallel-triple exception, vale substring-match artifact — see hyperhive#4548's per-PR bodies for the per-hit reasoning). Wraps each one in a scoped <!-- vale write-good.Passive = NO/YES --> pair (the supported mechanism — TokenIgnores has a known offset-drift bug) rather than a blanket per-file or per-rule silence, so a *new* passive-voice hit anywhere in these files still fails once the rule gates CI (next commit). Table/list false positives (docs/swarm/credentials.md's renewal-table cells) wrap the whole block, not each cell. Part of #4546.
This commit is contained in:
parent
7abb16e6d5
commit
04e27c4fb6
38 changed files with 368 additions and 71 deletions
|
|
@ -19,7 +19,9 @@ This host's nginx fronts the hyperhive web surfaces running on it — next to hi
|
|||
|
||||
Only the host that **runs** authelia declares the authelia vhost, not every hive that uses it — a client hive knows the swarm's `authelia.url` but must not answer for a name it doesn't serve. Its server name is exactly `swarm.authelia.domain`: authelia validates `authelia_url ⊂ session cookie domain` at startup, so a near-miss is a container that refuses to boot. It carries no `auth_basic` — the login page must not sit behind the login mechanism it replaces — and sets the four `X-Forwarded-{Proto,Host,Uri,For}` headers, since authelia decides by the *original* request rather than the hop it sees.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
⚠️ **A `502` from this vhost typically means authelia has no users yet, not that the proxy is misconfigured.** Authelia treats an empty user store as a fatal startup error, so an enabled-but-unbootstrapped swarm crash-loops the container while the vhost in front of it works perfectly. Check `journalctl -M swarm-authelia -u authelia-swarm` before suspecting anything here; the bootstrap step is in [`swarm/sso.md`](../swarm/sso.md).
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
Per-agent UIs stay sub-path, forge and matrix get sub-domains — see
|
||||
[Sub-domain shape (rationale)](#sub-domain-shape-rationale) below for why.
|
||||
|
|
@ -34,7 +36,9 @@ Operator points client at `<hive>`. Sequence:
|
|||
|
||||
matrix-dart-sdk (FluffyChat etc.) hardcodes `https` for the well-known fetch regardless of input scheme, so the discovery endpoint MUST be https — see "Self-signed TLS" below for the cert generation that backs the default-on path.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
Federation peers fetch `.well-known/matrix/server` → `{"m.server":"chat.<swarm>:<httpsPort>"}` (the federation delegation always carries an explicit port, even the HTTPS default 443 — the https-implies-443 elision only applies to the client base_url above). Gateway only listens on configured `port` (+ `httpsPort` when TLS on); cross-hive federation needs either an SRV record (`_matrix._tcp.chat.<swarm>` → port 80 / 443) OR `matrix.openFirewall = true` so peers reach tuwunel's federation port directly. Hyperhive is closed/internal in most deployments, so this rarely bites.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
## SPA fallback (Accept-header pattern)
|
||||
|
||||
|
|
@ -134,12 +138,14 @@ now set unconditionally for every agent. The mechanism:
|
|||
retry and report on (`RELOAD_PENDING`), not two units racing on an
|
||||
inotify event.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
c0re regenerates `agents.conf` (and triggers a reload) on two
|
||||
triggers: every topology change (new/removed agents) and every 10s
|
||||
marker poll tick (`agent_sockets::spawn_poll`). `write()` is
|
||||
idempotent — skips the rename when content is unchanged.
|
||||
`gateway_nginx::reload_if_pending` automatically retries failed reloads
|
||||
on subsequent poll ticks.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
`agents.conf` uses atomic `<path>.tmp` + `rename()` writes so a crashing
|
||||
c0re process never leaves a partial or unparseable file behind.
|
||||
|
|
@ -229,7 +235,9 @@ swarm-services fallback below is for.
|
|||
|
||||
**Cert shape**: leaf subject CN = bare hive domain; subjectAltName is `<hive>` plus wildcard `*.<hive>`, so all current and future sub-domain vhosts validate under the same leaf + the hive CA. You can't add a swarm service whose name is *not* under this hive's domain here — the hive CA is name-constrained to `<hive>`, and a violating SAN invalidates the whole leaf, not just that name. Those names get the swarm-services leaf instead ([`swarm/ca.md`](../swarm/ca.md)).
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
**Rotation**: `hive-tls-ca.service` is idempotent — it re-signs the leaf when it's missing or within 30 days of expiry, always under the same CA (so consumer trust is undisturbed). It regenerates the CA itself only if missing or already expired. To force a leaf rotation, delete `gateway.pem` under the state dir and restart the unit, then reload `nginx`.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
**Cert prompts**: browsers still warn once per host until the operator adds the hive's `trust-bundle.pem` to the browser/OS trust store (an anchor, not the leaf, is the thing to trust). A separate mechanism wires agent trust (see the agent-trust work for `/run/hive-ca`).
|
||||
|
||||
|
|
@ -292,6 +300,7 @@ TLS (see [TLS modes](#tls-modes) above).
|
|||
Every agent hashes into the same port range (no special case), so
|
||||
one range opening covers every container.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
The dashboard port (`services.hyperhive.c0re.dashboardPort`, default 7000) is *not*
|
||||
listed in either case — it binds `127.0.0.1` only, so a firewall
|
||||
hole would be a no-op. Remote dashboard access flows through the
|
||||
|
|
@ -299,6 +308,7 @@ gateway. Operators who opt out of the gateway lose external
|
|||
dashboard reach by design — the surface is privileged (approve /
|
||||
deny / destroy), and operators must not expose it without a real reverse
|
||||
proxy in front.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
## `HIVE_FORGE_URL`: agents reach the forge via the gateway by domain
|
||||
|
||||
|
|
@ -308,7 +318,7 @@ shared-netns mode no longer exists), so agents run in a private netns and
|
|||
can never reach the host's loopback.
|
||||
`nix/host-modules/hive-c0re/environment.nix` sets `HIVE_FORGE_URL` to
|
||||
`http://<forge.domain>` (default `forge.<swarm-domain>` — a swarm runs
|
||||
one forge; `services.hyperhive.domain` is required). Agents
|
||||
one forge; you must set `services.hyperhive.domain`). Agents
|
||||
get the bridge dnsmasq as their resolver, resolve the hostname →
|
||||
bridge IP, then reach nginx on port 80 (the bridge firewall opens
|
||||
80+443). nginx proxies to forgejo — the same path an operator browser
|
||||
|
|
@ -357,6 +367,7 @@ only if you already have another process bound to 3000.
|
|||
uses for `git clone/push/pull` over SSH (`git@<domain>:owner/repo.git`
|
||||
via `-p 2222`). Port 22 stays alone on the host for openssh.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
`openFirewall` (default **false**) controls whether the host firewall
|
||||
opens `httpPort` and `sshPort`. Off by default (secure by
|
||||
default): agents reach Forgejo through the gateway (`forge.<swarm-domain>` on
|
||||
|
|
@ -365,6 +376,7 @@ the bridge), not the raw port, so no firewall hole is needed. Flip to
|
|||
- The operator's browser to reach `http://<host>:<httpPort>/` directly
|
||||
(not behind the gateway).
|
||||
- External git clients that push/pull via SSH directly to the host.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
Forgejo served through the gateway (`deploy.forgejo.behindGateway = true`) does
|
||||
not need `openFirewall` — the gateway's own `openFirewall` option covers
|
||||
|
|
@ -495,12 +507,14 @@ palette (`#1e1e2e` bg, `#cdd6f4` text, `#cba6f7` heading). No
|
|||
dependencies on the frontend dist — these pages render even when
|
||||
hive-c0re itself is down.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
Scope is intentionally narrow: a route earns a custom page when the
|
||||
default status code would point at the wrong component. The per-agent
|
||||
routes qualify (a 502 there means the harness is restarting, not that
|
||||
the gateway is broken), and so does `auth.<swarm>` — a dead authelia
|
||||
upstream almost always means the user store was never bootstrapped, and
|
||||
a bare 502 blames the proxy, which is the one part that's working.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
Forge / matrix / fluffychat still get nginx defaults: their upstreams
|
||||
being down means what the status code says, so a themed page would add
|
||||
|
|
@ -508,10 +522,12 @@ styling and no information.
|
|||
|
||||
## HTTP Basic auth
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
`services.hyperhive.gateway.auth.enable = true` gates every request to
|
||||
the main vhost (`_`) behind HTTP Basic auth. nginx's built-in `auth_basic`
|
||||
module validates credentials; no extra service or host-side daemon is
|
||||
required.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
**Setup:**
|
||||
|
||||
|
|
@ -522,10 +538,12 @@ services.hyperhive.gateway.auth = {
|
|||
};
|
||||
```
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
The credential store lives at the fixed path
|
||||
`/var/lib/hive-gateway/conf/gateway.htpasswd` on the host. A tmpfiles
|
||||
rule pre-creates the file on first boot; no manual path configuration
|
||||
is required. nginx reads it at that path directly.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
Manage users with `hivectl gateway`. `hivectl` sends the request over the
|
||||
host admin socket and the `hive-c0re` daemon performs the write at its
|
||||
|
|
@ -545,9 +563,11 @@ hivectl gateway delete-user bob
|
|||
hivectl gateway list-users
|
||||
```
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
The daemon hashes passwords with BCrypt (cost 12) and writes
|
||||
`$2y$`-prefixed hashes that nginx accepts natively. No external
|
||||
`htpasswd` binary is required.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
**What's not gated:** per-agent UI routes emitted into `agents.conf`
|
||||
(served under `/agent/<name>/`) inherit no auth from `/` — nginx
|
||||
|
|
@ -612,6 +632,7 @@ TLS-less mode that could violate it.
|
|||
|
||||
## Dialing another vhost by name (`verifiedProxyTo`)
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
`vhost-lib.nix`'s `verifiedProxyTo` builds the `proxy_ssl_*` /
|
||||
`proxy_set_header` block a module uses to dial another service on this
|
||||
same gateway BY NAME over https, verified. One definition rather than a
|
||||
|
|
@ -619,6 +640,7 @@ copy per module: nginx verifies nothing by default
|
|||
(`proxy_ssl_verify` is off), so a `proxy_pass https://…` without these
|
||||
lines is encrypted and unauthenticated. That failure is invisible — it
|
||||
works, and keeps working, against any certificate at all.
|
||||
<!-- vale write-good.Passive = YES -->
|
||||
|
||||
Every line earns its place, each confirmed against a real nginx with
|
||||
the opposite arm run as a control:
|
||||
|
|
|
|||
Loading…
Reference in a new issue