docs: scrub #NNN self-references (closes #719)
Last pass of the docs-from-code → docs/ epic (#708). Drops every attribution cookie from docs/ + README.md + CLAUDE.md so the source-tree files no longer reference the issue tracker. Issue threads + commit history retain the references — those are the canonical record. - README.md: drop #701 / #660×2 / #551 from matrix + display-name sections, rephrase to convey the semantics directly - CLAUDE.md: scrub 18 cookies from the file map (#655, #15, #784, #832, #444, #425, #361, #548, #598, #539, #544, #589, #701, #658, #280, #660, #551, #764, #772, #793, #14, #805) - docs/agent-hierarchy.md: drop #658 ×3 (per-agent user is the current shape, not a transition) - docs/conventions.md: drop #571 (replaced with a docs xref to persistence.md::matrix-avatar-sync) - docs/gateway.md: scrub vhost-map table cookies + Sub-domain rationale + Per-agent unix-socket upstream + Self-signed TLS + Firewall posture + HIVE_FORGE_URL + Per-agent error pages sections; drop the trailing 'Sequencing history' issue list + the 'Next-up' issue-link footnote - docs/matrix.md: scrub serverName/gatewayHost + Default-closed firewall + Provisioning flow + Initial rollout + Assertion rationale + fluffychat-web build fixes; drop the trailing 'Sequencing history' issue list - docs/network.md: drop 'Why ship before #14' #805 quote + Container shape #805 attribution + trailing 'Sequencing history' + Cross-references issue links; rename v2 column to 'after netns isolation' - docs/web-ui.md: drop #784 from Container row, replace with a docs xref to docs/gateway.md::Per-agent unix-socket upstream Only remaining #NNN in docs/ is the literal markdown-heading example in docs/forge.md (`#tag`, `#123`, `#!/bin/bash`) which demonstrates the renderer's behaviour — not an attribution cookie.
This commit is contained in:
parent
2a2d76d733
commit
6bdfe52386
8 changed files with 142 additions and 187 deletions
52
CLAUDE.md
52
CLAUDE.md
|
|
@ -25,9 +25,9 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
|
||||||
deny / destroy [--purge] / request-spawn /
|
deny / destroy [--purge] / request-spawn /
|
||||||
set-parent (--parent / --root); periodic
|
set-parent (--parent / --root); periodic
|
||||||
vacuum tasks.
|
vacuum tasks.
|
||||||
src/bin/hivectl.rs `hivectl` binary (#655): operator-facing host
|
src/bin/hivectl.rs `hivectl` binary: operator-facing host CLI
|
||||||
CLI for ad-hoc administration that doesn't
|
for ad-hoc administration that doesn't go
|
||||||
go through the broker. v0 verbs: `forge
|
through the broker. v0 verbs: `forge
|
||||||
create-user <name>`, `matrix create-user
|
create-user <name>`, `matrix create-user
|
||||||
<name>` — wrap the same idempotent
|
<name>` — wrap the same idempotent
|
||||||
`forge::ensure_user_for` /
|
`forge::ensure_user_for` /
|
||||||
|
|
@ -39,15 +39,15 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
|
||||||
src/manager_server.rs manager-privileged socket (ManagerRequest)
|
src/manager_server.rs manager-privileged socket (ManagerRequest)
|
||||||
src/agent_ports.rs writes `/var/lib/hyperhive/agent-ports.json` on
|
src/agent_ports.rs writes `/var/lib/hyperhive/agent-ports.json` on
|
||||||
meta sync; gateway reads name→port map for
|
meta sync; gateway reads name→port map for
|
||||||
per-agent `/agent/<name>/` routing (#15).
|
per-agent `/agent/<name>/` routing.
|
||||||
src/agent_sockets.rs writes `/var/lib/hyperhive/agent-sockets.json` on
|
src/agent_sockets.rs writes `/var/lib/hyperhive/agent-sockets.json` on
|
||||||
meta sync; gateway reads name→socket-path
|
meta sync; gateway reads name→socket-path
|
||||||
map for unix-domain per-agent UI binding (#784).
|
map for unix-domain per-agent UI binding.
|
||||||
Entries are filtered by a `.bound` marker file
|
Entries are filtered by a `.bound` marker file
|
||||||
the harness drops next to its socket after a
|
the harness drops next to its socket after a
|
||||||
successful `bind()` (#832) — pre-bind agents
|
successful `bind()` — pre-bind agents stay out
|
||||||
stay out of the map so the gateway never races
|
of the map so the gateway never races a
|
||||||
a missing-socket upstream. A 10s poll loop
|
missing-socket upstream. A 10s poll loop
|
||||||
(`spawn_poll`) re-fires the writer so the map
|
(`spawn_poll`) re-fires the writer so the map
|
||||||
reconverges if a marker appears between
|
reconverges if a marker appears between
|
||||||
meta-sync ticks.
|
meta-sync ticks.
|
||||||
|
|
@ -76,7 +76,7 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
|
||||||
src/scheduled_prompts.rs sqlite layer for scheduled prompts —
|
src/scheduled_prompts.rs sqlite layer for scheduled prompts —
|
||||||
schema, CRUD helpers (`cancel_all`,
|
schema, CRUD helpers (`cancel_all`,
|
||||||
`cancel_targets`, `reap_cancelled`), catch-up
|
`cancel_targets`, `reap_cancelled`), catch-up
|
||||||
clamp on resume (#444)
|
clamp on resume
|
||||||
src/scheduled_prompts_worker.rs 5s poll loop: fires due rows,
|
src/scheduled_prompts_worker.rs 5s poll loop: fires due rows,
|
||||||
fans out one Message per active target,
|
fans out one Message per active target,
|
||||||
re-arms recurring rows, deletes fired one-shots
|
re-arms recurring rows, deletes fired one-shots
|
||||||
|
|
@ -86,7 +86,7 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
|
||||||
when a previously-running container disappears
|
when a previously-running container disappears
|
||||||
without an operator-initiated transient (or a
|
without an operator-initiated transient (or a
|
||||||
RECENT_TRANSIENT_GRACE tombstone within the
|
RECENT_TRANSIENT_GRACE tombstone within the
|
||||||
last 30s, closes #425)
|
last 30s)
|
||||||
src/container_view.rs ContainerView struct + build_all helper;
|
src/container_view.rs ContainerView struct + build_all helper;
|
||||||
shared between dashboard.rs (cold-load via
|
shared between dashboard.rs (cold-load via
|
||||||
/api/state) and coordinator.rs's
|
/api/state) and coordinator.rs's
|
||||||
|
|
@ -128,7 +128,7 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
|
||||||
and the eventual cap-enforcement plumbing all
|
and the eventual cap-enforcement plumbing all
|
||||||
read). Reconciled by `meta::sync_agents`;
|
read). Reconciled by `meta::sync_agents`;
|
||||||
operator/manager edits land via the
|
operator/manager edits land via the
|
||||||
eventual write API (#361 follow-ups).
|
eventual write API.
|
||||||
src/forge.rs optional Forgejo wiring: per-agent users +
|
src/forge.rs optional Forgejo wiring: per-agent users +
|
||||||
tokens, the `agent-configs` org (`push_config`),
|
tokens, the `agent-configs` org (`push_config`),
|
||||||
and meta read access; mirrors each applied repo
|
and meta read access; mirrors each applied repo
|
||||||
|
|
@ -142,7 +142,7 @@ hive-c0re/ host daemon + sibling operator CLI (lib + 2 bins)
|
||||||
persists each agent's access_token to
|
persists each agent's access_token to
|
||||||
`<state>/matrix-token`. Idempotent — skips
|
`<state>/matrix-token`. Idempotent — skips
|
||||||
registration when token file already exists.
|
registration when token file already exists.
|
||||||
No-op when `hive-matrix` container absent (#548).
|
No-op when `hive-matrix` container absent.
|
||||||
src/dashboard.rs axum HTTP: /api/state JSON + actions
|
src/dashboard.rs axum HTTP: /api/state JSON + actions
|
||||||
+ journald viewer + bind-with-retry (SO_REUSEADDR)
|
+ journald viewer + bind-with-retry (SO_REUSEADDR)
|
||||||
+ deployed_sha chip per container +
|
+ deployed_sha chip per container +
|
||||||
|
|
@ -169,7 +169,7 @@ frontend/ npm workspaces (esbuild → static dist). Built
|
||||||
+ agent.css → dist/{*.html, static/*}.
|
+ agent.css → dist/{*.html, static/*}.
|
||||||
|
|
||||||
hive-ag3nt/ in-container harness crate; produces ONE `hive`
|
hive-ag3nt/ in-container harness crate; produces ONE `hive`
|
||||||
binary (post-#598) picking role from `HIVE_ROLE` env
|
binary picking role from `HIVE_ROLE` env
|
||||||
src/lib.rs re-exports + DEFAULT_SOCKET, DEFAULT_WEB_PORT
|
src/lib.rs re-exports + DEFAULT_SOCKET, DEFAULT_WEB_PORT
|
||||||
src/client.rs generic JSON-line request/response over unix socket
|
src/client.rs generic JSON-line request/response over unix socket
|
||||||
src/web_ui.rs per-container axum HTTP page (incl /api/cancel,
|
src/web_ui.rs per-container axum HTTP page (incl /api/cancel,
|
||||||
|
|
@ -194,15 +194,15 @@ hive-ag3nt/ in-container harness crate; produces ONE `hive`
|
||||||
escaped) and surfaces any @mention lines that
|
escaped) and surfaces any @mention lines that
|
||||||
fell outside the truncation window in a
|
fell outside the truncation window in a
|
||||||
trailing `mentions (truncated from body):`
|
trailing `mentions (truncated from body):`
|
||||||
block (#539 / #544).
|
block.
|
||||||
src/identity.rs hive-qualified agent label (#589 phase A):
|
src/identity.rs hive-qualified agent label: `label()` /
|
||||||
`label()` / `qualified_label()` / `qualify(label)`.
|
`qualified_label()` / `qualify(label)`. Reads
|
||||||
Reads `HYPERHIVE_HIVE_DOMAIN`; falls back to short
|
`HYPERHIVE_HIVE_DOMAIN`; falls back to short
|
||||||
name when unset. Display-name accessors (#701):
|
name when unset. Display-name accessors:
|
||||||
`hive_name()` / `swarm_name()` read
|
`hive_name()` / `swarm_name()` read
|
||||||
`HYPERHIVE_HIVE_NAME` / `HYPERHIVE_SWARM_NAME`,
|
`HYPERHIVE_HIVE_NAME` / `HYPERHIVE_SWARM_NAME`,
|
||||||
both `Option<String>`.
|
both `Option<String>`.
|
||||||
src/login.rs probe $HOME/.claude/ (post-#658 `/home/<agent>/.claude`)
|
src/login.rs probe $HOME/.claude/ (= `/home/<agent>/.claude`)
|
||||||
for a valid session
|
for a valid session
|
||||||
src/login_session.rs drives `claude auth login` over stdio pipes
|
src/login_session.rs drives `claude auth login` over stdio pipes
|
||||||
src/prompt.rs system prompt renderer: filters the unified
|
src/prompt.rs system prompt renderer: filters the unified
|
||||||
|
|
@ -234,9 +234,9 @@ hive-forge/ Forgejo CLI wrapper (`hive-forge` binary)
|
||||||
close, labels, list, milestone, branches,
|
close, labels, list, milestone, branches,
|
||||||
tree-sha, diff, subscription, attach-issue,
|
tree-sha, diff, subscription, attach-issue,
|
||||||
attach-comment, lint). Replaces the 600-line
|
attach-comment, lint). Replaces the 600-line
|
||||||
hive-forge-tools.nix bash script (closes #280).
|
hive-forge-tools.nix bash script.
|
||||||
|
|
||||||
hive-matrix-mcp/ per-agent matrix-sdk integration (#548 phase 3).
|
hive-matrix-mcp/ per-agent matrix-sdk integration.
|
||||||
src/main.rs `hive-matrix-daemon` binary entry — long-running
|
src/main.rs `hive-matrix-daemon` binary entry — long-running
|
||||||
matrix-sdk Client + sync per agent; serves the
|
matrix-sdk Client + sync per agent; serves the
|
||||||
MCP bridge over /run/hive-matrix/socket; emits
|
MCP bridge over /run/hive-matrix/socket; emits
|
||||||
|
|
@ -277,9 +277,9 @@ nix/
|
||||||
modules/hive-matrix.nix optional in-container matrix-tuwunel
|
modules/hive-matrix.nix optional in-container matrix-tuwunel
|
||||||
homeserver (`services.hyperhive.matrix.enable`,
|
homeserver (`services.hyperhive.matrix.enable`,
|
||||||
default off); server_name defaults to bare
|
default off); server_name defaults to bare
|
||||||
`services.hyperhive.domain` (#660); gateway serves
|
`services.hyperhive.domain`; gateway serves
|
||||||
`.well-known/matrix/{client,server}` for
|
`.well-known/matrix/{client,server}` for
|
||||||
auto-discovery; federation on, e2ee deferred (#551)
|
auto-discovery; federation on, e2ee deferred
|
||||||
templates/harness-base.nix shared harness for all containers; `hyperhive.role`
|
templates/harness-base.nix shared harness for all containers; `hyperhive.role`
|
||||||
(`"agent"` | `"manager"`) drives service unit + forge
|
(`"agent"` | `"manager"`) drives service unit + forge
|
||||||
defaults; `hyperhive.model` option (HIVE_DEFAULT_MODEL)
|
defaults; `hyperhive.model` option (HIVE_DEFAULT_MODEL)
|
||||||
|
|
@ -304,10 +304,10 @@ docs/
|
||||||
persistence.md sqlite dbs, retention, state dir layout
|
persistence.md sqlite dbs, retention, state dir layout
|
||||||
terminal-rendering.md per-agent terminal row taxonomy (as built)
|
terminal-rendering.md per-agent terminal row taxonomy (as built)
|
||||||
boundary.md operator/agent trust model rationale
|
boundary.md operator/agent trust model rationale
|
||||||
agent-hierarchy.md tree-shape topology design + manager-privilege audit (#361)
|
agent-hierarchy.md tree-shape topology design + manager-privilege audit
|
||||||
damocles-migration.md future migration plan for damocles → hyperhive
|
damocles-migration.md future migration plan for damocles → hyperhive
|
||||||
gateway.md nginx vhost map, matrix discovery flow, firewall posture,
|
gateway.md nginx vhost map, matrix discovery flow, firewall posture,
|
||||||
HIVE_FORGE_URL loopback rationale (#764, #772, #793)
|
HIVE_FORGE_URL loopback rationale
|
||||||
forge.md per-agent forge accounts + agent-configs mirror;
|
forge.md per-agent forge accounts + agent-configs mirror;
|
||||||
forge_notify poller (gates, self-filter, body excerpt
|
forge_notify poller (gates, self-filter, body excerpt
|
||||||
pipeline, wrapper formats, reason drop-list,
|
pipeline, wrapper formats, reason drop-list,
|
||||||
|
|
@ -315,7 +315,7 @@ docs/
|
||||||
matrix.md matrix container shape, serverName/gatewayHost split,
|
matrix.md matrix container shape, serverName/gatewayHost split,
|
||||||
firewall + federation, provisioning flow, fluffychat-web build
|
firewall + federation, provisioning flow, fluffychat-web build
|
||||||
network.md host-side bridge + dnsmasq resolver, v1/v2 roadmap,
|
network.md host-side bridge + dnsmasq resolver, v1/v2 roadmap,
|
||||||
foundation for #14 netns isolation (#805)
|
foundation for netns isolation
|
||||||
```
|
```
|
||||||
|
|
||||||
## Reading paths
|
## Reading paths
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ the wider swarm human-readable display labels. Distinct from
|
||||||
the dashboard chrome and the per-agent system prompt; the domain is
|
the dashboard chrome and the per-agent system prompt; the domain is
|
||||||
how things are addressed on the wire. Federated hives at different
|
how things are addressed on the wire. Federated hives at different
|
||||||
domains can share a swarm name. Both default to null — chrome falls
|
domains can share a swarm name. Both default to null — chrome falls
|
||||||
back to the domain, the prompt simply doesn't mention them (#701).
|
back to the domain, the prompt simply doesn't mention them.
|
||||||
|
|
||||||
Optional: set `services.hyperhive.matrix.enable = true;` to spin up a
|
Optional: set `services.hyperhive.matrix.enable = true;` to spin up a
|
||||||
private [matrix-tuwunel](https://github.com/matrix-construct/tuwunel)
|
private [matrix-tuwunel](https://github.com/matrix-construct/tuwunel)
|
||||||
|
|
@ -120,16 +120,17 @@ homeserver in a nixos-container. Requires either
|
||||||
`services.hyperhive.domain` or `services.hyperhive.matrix.serverName`
|
`services.hyperhive.domain` or `services.hyperhive.matrix.serverName`
|
||||||
to be set (eval fails with a clear error if both are absent). The
|
to be set (eval fails with a clear error if both are absent). The
|
||||||
`server_name` (embedded irrevocably in every user and room ID)
|
`server_name` (embedded irrevocably in every user and room ID)
|
||||||
defaults to `<domain>` (bare hive domain, since #660); the hive-gateway serves
|
defaults to `<domain>` (bare hive domain); the hive-gateway serves
|
||||||
`.well-known/matrix/{client,server}` endpoints so clients auto-discover the
|
`.well-known/matrix/{client,server}` endpoints so clients auto-discover the
|
||||||
homeserver without a subdomain. **Existing homeservers** must set
|
homeserver without a subdomain. **Existing homeservers** must set
|
||||||
`services.hyperhive.matrix.serverName = "matrix.<domain>";` explicitly to
|
`services.hyperhive.matrix.serverName = "matrix.<domain>";` explicitly to
|
||||||
preserve pre-#660 user/room IDs. Override with
|
preserve their existing user/room IDs (the `server_name` is embedded
|
||||||
|
into every ID). Override with
|
||||||
`services.hyperhive.matrix.serverName = "chat.example.com";` for a custom
|
`services.hyperhive.matrix.serverName = "chat.example.com";` for a custom
|
||||||
server name. State
|
server name. State
|
||||||
lives at `/var/lib/nixos-containers/hive-matrix/`. Federation is
|
lives at `/var/lib/nixos-containers/hive-matrix/`. Federation is
|
||||||
enabled with an empty `trusted_servers` list; e2ee is deferred to
|
enabled with an empty `trusted_servers` list; e2ee is deferred to
|
||||||
a follow-up (#551).
|
a follow-up.
|
||||||
|
|
||||||
The hive-gateway nginx container serves a
|
The hive-gateway nginx container serves a
|
||||||
[fluffychat-web](https://fluffychat.im) matrix client at `/matrix/`
|
[fluffychat-web](https://fluffychat.im) matrix client at `/matrix/`
|
||||||
|
|
|
||||||
|
|
@ -251,9 +251,9 @@ boundary.
|
||||||
### Environment variables set on the unit
|
### Environment variables set on the unit
|
||||||
|
|
||||||
- `HOME = /home/<userName>` — systemd defaults `HOME` to `/` for
|
- `HOME = /home/<userName>` — systemd defaults `HOME` to `/` for
|
||||||
services without `User=` set; with the per-agent user (#658) the
|
services without `User=` set; with the per-agent user the harness
|
||||||
harness needs the right home so claude finds its bind-mounted
|
needs the right home so claude finds its bind-mounted `~/.claude/`
|
||||||
`~/.claude/` session dir.
|
session dir.
|
||||||
- `HIVE_STATIC_DIR = <mergedDist>` — `tower_http::ServeDir` root for
|
- `HIVE_STATIC_DIR = <mergedDist>` — `tower_http::ServeDir` root for
|
||||||
the per-agent web UI; merged dist = agent default + every
|
the per-agent web UI; merged dist = agent default + every
|
||||||
`hyperhive.frontend.extraFiles` overlay.
|
`hyperhive.frontend.extraFiles` overlay.
|
||||||
|
|
@ -275,9 +275,9 @@ resolve before bare nix-store binaries. NixOS's
|
||||||
`systemd.services.<unit>.path` appends `/bin` to every entry via
|
`systemd.services.<unit>.path` appends `/bin` to every entry via
|
||||||
`lib.makeBinPath`; passing `/run/wrappers/bin` directly produces
|
`lib.makeBinPath`; passing `/run/wrappers/bin` directly produces
|
||||||
`/run/wrappers/bin/bin` which doesn't exist (`docs/gotchas.md::
|
`/run/wrappers/bin/bin` which doesn't exist (`docs/gotchas.md::
|
||||||
systemd.services.*.path appends /bin to every entry`). Post-#658
|
systemd.services.*.path appends /bin to every entry`). With the
|
||||||
when the harness runs as the per-agent user this matters: without
|
harness running as the per-agent user this matters: without the
|
||||||
the wrapper dir on PATH, `sudo` resolves to the un-setuid nix-store
|
wrapper dir on PATH, `sudo` resolves to the un-setuid nix-store
|
||||||
binary and rejects with `must be owned by uid 0 and have the setuid
|
binary and rejects with `must be owned by uid 0 and have the setuid
|
||||||
bit set` regardless of `hyperhive.user.passwordlessSudo`.
|
bit set` regardless of `hyperhive.user.passwordlessSudo`.
|
||||||
|
|
||||||
|
|
@ -292,7 +292,7 @@ bit set` regardless of `hyperhive.user.passwordlessSudo`.
|
||||||
`claude-{mcp-config,settings,system-prompt}` files there
|
`claude-{mcp-config,settings,system-prompt}` files there
|
||||||
(`paths::config_dir`). Deliberately separate from `/run/hive`,
|
(`paths::config_dir`). Deliberately separate from `/run/hive`,
|
||||||
which the host bind-mounts in root-owned and which holds
|
which the host bind-mounts in root-owned and which holds
|
||||||
hive-c0re's `mcp.sock` (#658 fixup).
|
hive-c0re's `mcp.sock`.
|
||||||
- `User = Group = userName` — drops root inside the container; sudo
|
- `User = Group = userName` — drops root inside the container; sudo
|
||||||
is the explicit escalation surface
|
is the explicit escalation surface
|
||||||
(`hyperhive.user.passwordlessSudo`).
|
(`hyperhive.user.passwordlessSudo`).
|
||||||
|
|
|
||||||
|
|
@ -350,8 +350,8 @@ Shape contract — every one of these:
|
||||||
`systemctl restart` gymnastics).
|
`systemctl restart` gymnastics).
|
||||||
5. **Re-runnable**: a second invocation produces the same final
|
5. **Re-runnable**: a second invocation produces the same final
|
||||||
state (idempotent uploads, idempotent config rewrites). Used
|
state (idempotent uploads, idempotent config rewrites). Used
|
||||||
by the `.path` watchers that re-fire on token appearance
|
by the `.path` watchers that re-fire on token appearance (see
|
||||||
(#571 — see `docs/persistence.md::matrix-avatar-sync`).
|
`docs/persistence.md::matrix-avatar-sync`).
|
||||||
|
|
||||||
The artefact lives under the agent user's home where applicable
|
The artefact lives under the agent user's home where applicable
|
||||||
(`~/.config/tea/config.yml`) and is chown'd to that user, but the
|
(`~/.config/tea/config.yml`) and is chown'd to that user, but the
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,15 @@ Single nginx in front of every hyperhive web surface. Container `hive-gateway`,
|
||||||
| URL | vhost | upstream | source |
|
| URL | vhost | upstream | source |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `<hive>/` | `_` (catch-all) | hive-c0re dashboard (`7000`) | always |
|
| `<hive>/` | `_` (catch-all) | hive-c0re dashboard (`7000`) | always |
|
||||||
| `<hive>/agent/<name>/` | `_` | per-agent harness on `agent_web_port(name)` | `agentPortsFile` JSON, #15 |
|
| `<hive>/agent/<name>/` | `_` | per-agent harness on `agent_web_port(name)` | `agentPortsFile` JSON |
|
||||||
| `<hive>/.well-known/matrix/{client,server}` | `_` | inline JSON (no upstream) | `matrix.enable && domain != null`, #660 / #747 |
|
| `<hive>/.well-known/matrix/{client,server}` | `_` | inline JSON (no upstream) | `matrix.enable && domain != null` |
|
||||||
| `<hive>/matrix/` (deprecated) | `_` | 301 → `matrix.<hive>/` | `matrix.gui.enable`, #772 |
|
| `<hive>/matrix/` (deprecated) | `_` | 301 → `matrix.<hive>/` | `matrix.gui.enable` |
|
||||||
| `forge.<hive>/` | `forge.<hive>` | forgejo (`3000`) | `forge.behindGateway`, #754 |
|
| `forge.<hive>/` | `forge.<hive>` | forgejo (`3000`) | `forge.behindGateway` |
|
||||||
| `matrix.<hive>/_matrix/*` | `matrix.<hive>` | tuwunel (`8008`) | `matrix.gatewayHost != null`, #764 |
|
| `matrix.<hive>/_matrix/*` | `matrix.<hive>` | tuwunel (`8008`) | `matrix.gatewayHost != null` |
|
||||||
| `matrix.<hive>/` | `matrix.<hive>` | fluffychat-web static | `matrix.gui.enable`, #772 |
|
| `matrix.<hive>/` | `matrix.<hive>` | fluffychat-web static | `matrix.gui.enable` |
|
||||||
| `matrix.<hive>/config.json` | `matrix.<hive>` | inline JSON (FluffyChat boot config) | `matrix.gui.enable && domain != null`, #736 |
|
| `matrix.<hive>/config.json` | `matrix.<hive>` | inline JSON (FluffyChat boot config) | `matrix.gui.enable && domain != null` |
|
||||||
|
|
||||||
Per-agent UIs stay sub-path because they're hyperhive-internal and base-path-aware (iris #731). External standard apps (forge / matrix) get sub-domains because their defaults work cleanly at sub-domain root + per-origin cookies / storage isolation matters.
|
Per-agent UIs stay sub-path because they're hyperhive-internal and base-path-aware. External standard apps (forge / matrix) get sub-domains because their defaults work cleanly at sub-domain root + per-origin cookies / storage isolation matters.
|
||||||
|
|
||||||
## Discovery flow (matrix)
|
## Discovery flow (matrix)
|
||||||
|
|
||||||
|
|
@ -34,9 +34,9 @@ Federation peers fetch `.well-known/matrix/server` → `{"m.server":"matrix.<hiv
|
||||||
The `<hive>` catch-all and the `matrix.<hive>` vhost both serve a flutter SPA (per-agent UI, fluffychat). Two requirements collide:
|
The `<hive>` catch-all and the `matrix.<hive>` vhost both serve a flutter SPA (per-agent UI, fluffychat). Two requirements collide:
|
||||||
|
|
||||||
- hard-refresh on a sub-route must serve `index.html` (SPA's client-side router takes over after JS bootstrap)
|
- hard-refresh on a sub-route must serve `index.html` (SPA's client-side router takes over after JS bootstrap)
|
||||||
- missing assets must surface as 404, not as HTML with wrong content-type (the original #643 bug)
|
- missing assets must surface as 404, not as HTML with wrong content-type
|
||||||
|
|
||||||
Solution: an `nginx http`-context `map $http_accept $matrix_spa_target { ... }` keyed on the request's Accept header. Browser navigations (`Accept: text/html,...`) get `index.html`; asset fetches (`Accept: image/*`, `*/*`, etc.) get a sentinel nonexistent path → `try_files` falls through to `=404`. No extension allowlist, no `if` block, no regex heuristics. #686 + #729 thread for the design history.
|
Solution: an `nginx http`-context `map $http_accept $matrix_spa_target { ... }` keyed on the request's Accept header. Browser navigations (`Accept: text/html,...`) get `index.html`; asset fetches (`Accept: image/*`, `*/*`, etc.) get a sentinel nonexistent path → `try_files` falls through to `=404`. No extension allowlist, no `if` block, no regex heuristics.
|
||||||
|
|
||||||
## Local dev (`localHostsEntry`)
|
## Local dev (`localHostsEntry`)
|
||||||
|
|
||||||
|
|
@ -50,14 +50,14 @@ Solution: an `nginx http`-context `map $http_accept $matrix_spa_target { ... }`
|
||||||
|
|
||||||
## Sub-domain shape (rationale)
|
## Sub-domain shape (rationale)
|
||||||
|
|
||||||
mara verdict at #749:9609 + #747:9722: sub-domain over sub-path for forge + matrix, sub-path for per-agent UIs.
|
Operator decision: sub-domain over sub-path for forge + matrix, sub-path for per-agent UIs.
|
||||||
|
|
||||||
- forgejo's default `ROOT_URL = http://<host>/` works without any `X-Forwarded-Prefix` gymnastics — sub-domain hosting is the canonical Forgejo deploy shape.
|
- forgejo's default `ROOT_URL = http://<host>/` works without any `X-Forwarded-Prefix` gymnastics — sub-domain hosting is the canonical Forgejo deploy shape.
|
||||||
- matrix-spec deployments universally use `matrix.<server_name>` for the actual API listener — federation already expects this.
|
- matrix-spec deployments universally use `matrix.<server_name>` for the actual API listener — federation already expects this.
|
||||||
- per-agent UIs are hyperhive-internal; iris's #731 made them base-path-aware specifically for `/agent/<name>/`. Sub-domain per agent would multiply DNS + TLS-per-subdomain cost without per-app config wins.
|
- per-agent UIs are hyperhive-internal and base-path-aware specifically for `/agent/<name>/`. Sub-domain per agent would multiply DNS + TLS-per-subdomain cost without per-app config wins.
|
||||||
- cookie / storage isolation: a future forge XSS can't reach the dashboard session because they're different origins.
|
- cookie / storage isolation: a future forge XSS can't reach the dashboard session because they're different origins.
|
||||||
|
|
||||||
`services.hyperhive.{forge.domain,matrix.gatewayHost}` take the full hostname (`forge.darkest.space`, `git.example.com`) rather than a label that gets concatenated with hive-domain — mara on #754:9684 wanted operator control over the full shape, not a forced `<label>.<hive-domain>` pattern.
|
`services.hyperhive.{forge.domain,matrix.gatewayHost}` take the full hostname (`forge.darkest.space`, `git.example.com`) rather than a label that gets concatenated with hive-domain — operators want control over the full shape, not a forced `<label>.<hive-domain>` pattern.
|
||||||
|
|
||||||
## Tuning knobs
|
## Tuning knobs
|
||||||
|
|
||||||
|
|
@ -69,29 +69,28 @@ Per-vhost timeouts + body-size limits live in the location blocks:
|
||||||
|
|
||||||
SSH for forge stays direct on `cfg.sshPort` — separate listener protocol, not HTTP-over-nginx.
|
SSH for forge stays direct on `cfg.sshPort` — separate listener protocol, not HTTP-over-nginx.
|
||||||
|
|
||||||
## Per-agent unix-socket upstream (#784)
|
## Per-agent unix-socket upstream
|
||||||
|
|
||||||
Sub-agent `/agent/<name>/` upstreams flip from TCP loopback to a
|
Sub-agent `/agent/<name>/` upstreams flip from TCP loopback to a
|
||||||
unix-domain socket as each agent opts in. The mechanism:
|
unix-domain socket as each agent opts in. The mechanism:
|
||||||
|
|
||||||
1. **Agent side** (`hyperhive.web.useUnixSocket = true` in
|
1. **Agent side** (`hyperhive.web.useUnixSocket = true` in
|
||||||
`agent.nix`, #815). Sets `HIVE_WEB_SOCKET=/run/hive-agent/<name>/web.sock`
|
`agent.nix`). Sets `HIVE_WEB_SOCKET=/run/hive-agent/<name>/web.sock`
|
||||||
on the harness service env; `web_ui::serve` binds a `UnixListener`
|
on the harness service env; `web_ui::serve` binds a `UnixListener`
|
||||||
at that path instead of TCP.
|
at that path instead of TCP.
|
||||||
2. **Host side**. `hive-c0re` bind-mounts the per-agent subdir
|
2. **Host side**. `hive-c0re` bind-mounts the per-agent subdir
|
||||||
(`/run/hive-agent/<name>/`) into the agent's container (#813). Dir
|
(`/run/hive-agent/<name>/`) into the agent's container. Dir
|
||||||
bind, not file bind — file bind-mounts don't survive the
|
bind, not file bind — file bind-mounts don't survive the
|
||||||
harness's `unlink + bind(2)` cycle on socket replace. Per-agent
|
harness's `unlink + bind(2)` cycle on socket replace. Per-agent
|
||||||
subdir keeps each agent's container blind to siblings'
|
subdir keeps each agent's container blind to siblings' sockets.
|
||||||
sockets (mara on #800).
|
|
||||||
3. **Marker gate**. After successful `bind_unix`, the harness drops
|
3. **Marker gate**. After successful `bind_unix`, the harness drops
|
||||||
`<dir>/.bound` next to the socket. c0re's `agent_sockets::write`
|
`<dir>/.bound` next to the socket. c0re's `agent_sockets::write`
|
||||||
filters its JSON map by marker presence — only agents whose
|
filters its JSON map by marker presence — only agents whose
|
||||||
harness has actually bound the socket appear there (#784 atlas
|
harness has actually bound the socket appear there. Without this
|
||||||
gate). Without this filter, the gateway would `proxy_pass` to a
|
filter, the gateway would `proxy_pass` to a non-existent socket
|
||||||
non-existent socket for every sub-agent that hasn't opted in yet.
|
for every sub-agent that hasn't opted in yet.
|
||||||
4. **Gateway side** (#829). Reads `agent-sockets.json` at
|
4. **Gateway side**. Reads `agent-sockets.json` at request-handling
|
||||||
request-handling time and routes `/agent/<name>/` to
|
time and routes `/agent/<name>/` to
|
||||||
`http://unix:/run/hive-agent/<name>/web.sock:/`. Whole
|
`http://unix:/run/hive-agent/<name>/web.sock:/`. Whole
|
||||||
`/run/hive-agent/` is bind-mounted read-only into the gateway
|
`/run/hive-agent/` is bind-mounted read-only into the gateway
|
||||||
container so it can reach every published socket.
|
container so it can reach every published socket.
|
||||||
|
|
@ -103,7 +102,7 @@ one stat per agent per tick.
|
||||||
|
|
||||||
Transition: agents that haven't flipped `useUnixSocket = true` still
|
Transition: agents that haven't flipped `useUnixSocket = true` still
|
||||||
appear in `agent-ports.json` (the legacy TCP map) and the gateway
|
appear in `agent-ports.json` (the legacy TCP map) and the gateway
|
||||||
falls back to TCP for them. Step 4 of #784 will drop the TCP map +
|
falls back to TCP for them. A future cleanup will drop the TCP map +
|
||||||
the harness's TCP bind once every agent's flipped.
|
the harness's TCP bind once every agent's flipped.
|
||||||
|
|
||||||
## Dashboard link shape (gateway vs direct)
|
## Dashboard link shape (gateway vs direct)
|
||||||
|
|
@ -122,22 +121,11 @@ tracked by `forge.behindGateway`); `external`-kind links are
|
||||||
already absolute. See `docs/web-ui.md::Container row` for the
|
already absolute. See `docs/web-ui.md::Container row` for the
|
||||||
frontend-side derivation.
|
frontend-side derivation.
|
||||||
|
|
||||||
## Sequencing history
|
|
||||||
|
|
||||||
- #15 v0 (per-agent routing, #740) — first sub-app behind the gateway, JSON port table from c0re.
|
|
||||||
- #686 / #729 — Accept-header SPA fallback pattern.
|
|
||||||
- #749 / #754 — forge to sub-domain (mara: sub-domain over sub-path).
|
|
||||||
- #747 / #764 — matrix sub-domain vhost + `.well-known` delegation.
|
|
||||||
- #772 / #775 — fluffychat hops from `<hive>/matrix/` to `matrix.<hive>/`.
|
|
||||||
- #784 / #800 / #813 / #815 / #822 / #829 — sub-agent UI flips to unix-domain socket upstream, opt-in per agent.
|
|
||||||
|
|
||||||
Next-up tracked separately: #14 (container netns isolation), TLS (#594).
|
|
||||||
|
|
||||||
## Self-signed TLS (`selfSignedTls`)
|
## Self-signed TLS (`selfSignedTls`)
|
||||||
|
|
||||||
On by default since #837. The gateway generates a self-signed RSA-4096 cert at first boot (10-year validity) and listens on `httpsPort` (default 443) on every vhost beside the plain-http `port` (default 80).
|
On by default. The gateway generates a self-signed RSA-4096 cert at first boot (10-year validity) and listens on `httpsPort` (default 443) on every vhost beside the plain-http `port` (default 80).
|
||||||
|
|
||||||
**Why on by default**: matrix-dart-sdk (FluffyChat's SDK) hardcodes `https://<host>/.well-known/matrix/client` for homeserver discovery and refuses to fall back to plain http. Without TLS the browser client cannot bootstrap (#837). For headless agent traffic and operator dashboard reach plain http is fine, so the http listen stays in parallel — operators can keep using `http://<hive>/` from the dashboard if they don't care about the cert prompt.
|
**Why on by default**: matrix-dart-sdk (FluffyChat's SDK) hardcodes `https://<host>/.well-known/matrix/client` for homeserver discovery and refuses to fall back to plain http. Without TLS the browser client cannot bootstrap. For headless agent traffic and operator dashboard reach plain http is fine, so the http listen stays in parallel — operators can keep using `http://<hive>/` from the dashboard if they don't care about the cert prompt.
|
||||||
|
|
||||||
**Cert shape**: subject CN = bare hive domain; subjectAltName covers `<hive>` + wildcard `*.<hive>` so all current and future sub-domain vhosts (matrix, forge, ...) validate under the same cert. Stored at `/var/lib/hive-gateway/tls/{cert,key}.pem` inside the gateway container (`ephemeral = false`, so persisted across container restart).
|
**Cert shape**: subject CN = bare hive domain; subjectAltName covers `<hive>` + wildcard `*.<hive>` so all current and future sub-domain vhosts (matrix, forge, ...) validate under the same cert. Stored at `/var/lib/hive-gateway/tls/{cert,key}.pem` inside the gateway container (`ephemeral = false`, so persisted across container restart).
|
||||||
|
|
||||||
|
|
@ -156,25 +144,24 @@ On by default since #837. The gateway generates a self-signed RSA-4096 cert at f
|
||||||
`services.hyperhive.gateway.enable = false`**. With the gateway on
|
`services.hyperhive.gateway.enable = false`**. With the gateway on
|
||||||
(default), it's the sole external entry point and proxies to
|
(default), it's the sole external entry point and proxies to
|
||||||
`127.0.0.1:<port>` internally — leaving the per-agent ports
|
`127.0.0.1:<port>` internally — leaving the per-agent ports
|
||||||
firewall-open would defeat the single-front-door story (closes
|
firewall-open would defeat the single-front-door story.
|
||||||
#621).
|
|
||||||
|
|
||||||
`services.hyperhive.gateway.openFirewall = true` opens `port` plus
|
`services.hyperhive.gateway.openFirewall = true` opens `port` plus
|
||||||
`httpsPort` when `selfSignedTls = true` (default). Operators who
|
`httpsPort` when `selfSignedTls = true` (default). Operators who
|
||||||
flip `selfSignedTls = false` to front the gateway with a real
|
flip `selfSignedTls = false` to front the gateway with a real
|
||||||
TLS-terminating reverse proxy on the host get only `port` opened.
|
TLS-terminating reverse proxy on the host get only `port` opened.
|
||||||
|
|
||||||
Manager hashes into the same range since #753 (no more
|
The manager hashes into the same port range as sub-agents (no
|
||||||
"manager pinned at 8000" special case), so one range opening covers
|
"manager pinned at 8000" special case), so one range opening covers
|
||||||
every container.
|
every container.
|
||||||
|
|
||||||
The dashboard port (`cfg.dashboardPort`, default 7000) is *not*
|
The dashboard port (`cfg.dashboardPort`, default 7000) is *not*
|
||||||
listed in either case — since #652 it binds `127.0.0.1` only, so a
|
listed in either case — it binds `127.0.0.1` only, so a firewall
|
||||||
firewall hole would be a no-op. Remote dashboard access flows
|
hole would be a no-op. Remote dashboard access flows through the
|
||||||
through the gateway. Operators who opt out of the gateway lose
|
gateway. Operators who opt out of the gateway lose external
|
||||||
external dashboard reach by design — the surface is privileged
|
dashboard reach by design — the surface is privileged (approve /
|
||||||
(approve / deny / destroy) and must not be exposed without a real
|
deny / destroy) and must not be exposed without a real reverse
|
||||||
reverse proxy in front.
|
proxy in front.
|
||||||
|
|
||||||
## `HIVE_FORGE_URL`: loopback for in-cluster, sub-domain for the operator
|
## `HIVE_FORGE_URL`: loopback for in-cluster, sub-domain for the operator
|
||||||
|
|
||||||
|
|
@ -182,14 +169,13 @@ Agents poll `HIVE_FORGE_URL` for Forgejo notifications + run all
|
||||||
`hive-forge` calls against it. `hive-c0re.nix` pins this to
|
`hive-forge` calls against it. `hive-c0re.nix` pins this to
|
||||||
`http://127.0.0.1:<forge.httpPort>` for the in-cluster path: every
|
`http://127.0.0.1:<forge.httpPort>` for the in-cluster path: every
|
||||||
agent container shares the host's network namespace, so loopback
|
agent container shares the host's network namespace, so loopback
|
||||||
reaches the forge container directly with no DNS lookup needed
|
reaches the forge container directly with no DNS lookup needed.
|
||||||
(closes #761).
|
|
||||||
|
|
||||||
The post-#754 sub-domain default (`forge.<hive-domain>`) is for
|
The sub-domain default (`forge.<hive-domain>`) is for **operator
|
||||||
**operator browsers + cross-host clients**, not in-cluster traffic.
|
browsers + cross-host clients**, not in-cluster traffic. Using the
|
||||||
Using the sub-domain URL inside agent containers would fail every
|
sub-domain URL inside agent containers would fail every `hive-forge`
|
||||||
`hive-forge` invocation with "Name or service not known" — the
|
invocation with "Name or service not known" — the agent's nspawn
|
||||||
agent's nspawn doesn't have DNS for the external hostname.
|
doesn't have DNS for the external hostname.
|
||||||
|
|
||||||
## hive-forge container shape
|
## hive-forge container shape
|
||||||
|
|
||||||
|
|
@ -213,7 +199,7 @@ container.
|
||||||
## Per-agent error pages
|
## Per-agent error pages
|
||||||
|
|
||||||
`/agent/<name>/` requests hit two failure modes; both get static
|
`/agent/<name>/` requests hit two failure modes; both get static
|
||||||
HTML pages instead of nginx's default error chrome (#755):
|
HTML pages instead of nginx's default error chrome:
|
||||||
|
|
||||||
- **Agent not found** (`/agent/<unknown>/...`) — name isn't in
|
- **Agent not found** (`/agent/<unknown>/...`) — name isn't in
|
||||||
`agentPortsTable`. nginx's prefix match falls back to the bare
|
`agentPortsTable`. nginx's prefix match falls back to the bare
|
||||||
|
|
@ -238,8 +224,8 @@ palette (`#1e1e2e` bg, `#cdd6f4` text, `#cba6f7` heading). No
|
||||||
dependencies on the frontend dist — these pages render even when
|
dependencies on the frontend dist — these pages render even when
|
||||||
hive-c0re itself is down.
|
hive-c0re itself is down.
|
||||||
|
|
||||||
Scope is intentionally narrow per mara on #755: "only for routes
|
Scope is intentionally narrow: only routes already special-cased in
|
||||||
already special cased in the nginx config". Other gateway routes
|
the nginx config get custom error pages. Other gateway routes
|
||||||
(forge / matrix / fluffychat) get nginx defaults — extending the
|
(forge / matrix / fluffychat) get nginx defaults — extending the
|
||||||
custom-error pattern there is a separate follow-up.
|
custom-error pattern there is a separate follow-up.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,34 +34,33 @@ Two distinct hostnames:
|
||||||
*irrevocably* in every `@user:<server_name>` and `!room:<server_name>`
|
*irrevocably* in every `@user:<server_name>` and `!room:<server_name>`
|
||||||
identifier minted on this homeserver. Cannot be changed later
|
identifier minted on this homeserver. Cannot be changed later
|
||||||
without abandoning every account and chat history. Defaults to the
|
without abandoning every account and chat history. Defaults to the
|
||||||
bare `services.hyperhive.domain` per mara on #660; clients
|
bare `services.hyperhive.domain`; clients auto-discover the actual
|
||||||
auto-discover the actual API endpoint via the
|
API endpoint via the `.well-known/matrix/{client,server}` routes
|
||||||
`.well-known/matrix/{client,server}` routes the hive-gateway serves
|
the hive-gateway serves at that domain.
|
||||||
at that domain.
|
|
||||||
- **`gatewayHost`** — the API listener hostname, where the gateway's
|
- **`gatewayHost`** — the API listener hostname, where the gateway's
|
||||||
matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to
|
matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to
|
||||||
`matrix.<services.hyperhive.domain>` (sub-domain shape per mara on
|
`matrix.<services.hyperhive.domain>` (sub-domain shape). Set to
|
||||||
#749:9609). Set to `null` to skip the gateway vhost (tuwunel stays
|
`null` to skip the gateway vhost (tuwunel stays direct on
|
||||||
direct on `httpPort`).
|
`httpPort`).
|
||||||
|
|
||||||
**Breaking change** (#660): `serverName` used to default to
|
**Breaking change**: `serverName` used to default to
|
||||||
`matrix.${services.hyperhive.domain}`. Existing homeservers must set
|
`matrix.${services.hyperhive.domain}`. Existing homeservers must set
|
||||||
the option explicitly to preserve their pre-#660 user / room IDs
|
the option explicitly to preserve their existing user / room IDs
|
||||||
before rebuilding. The default flipped because the bare hive-domain
|
before rebuilding. The default flipped because the bare hive-domain
|
||||||
makes for cleaner matrix IDs and `.well-known` delegation hides the
|
makes for cleaner matrix IDs and `.well-known` delegation hides the
|
||||||
sub-domain from the user-facing identifier.
|
sub-domain from the user-facing identifier.
|
||||||
|
|
||||||
## Default-closed firewall
|
## Default-closed firewall
|
||||||
|
|
||||||
`openFirewall` defaults to `false` (#651, secure-by-default): the
|
`openFirewall` defaults to `false` (secure-by-default): the
|
||||||
homeserver is reachable from the host + every agent container via
|
homeserver is reachable from the host + every agent container via
|
||||||
loopback either way (shared netns), so the firewall hole only
|
loopback either way (shared netns), so the firewall hole only
|
||||||
matters for access from *outside* the host. Flip to `true` when
|
matters for access from *outside* the host. Flip to `true` when
|
||||||
announcing the homeserver to other hives or when an external matrix
|
announcing the homeserver to other hives or when an external matrix
|
||||||
client needs to reach the client-server API directly.
|
client needs to reach the client-server API directly.
|
||||||
|
|
||||||
**Breaking change** (#651): used to default to `true`. Operators
|
**Breaking change**: used to default to `true`. Operators relying on
|
||||||
relying on external reach must add
|
external reach must add
|
||||||
`services.hyperhive.matrix.openFirewall = true;` before rebuilding.
|
`services.hyperhive.matrix.openFirewall = true;` before rebuilding.
|
||||||
|
|
||||||
Federation port 8448 is intentionally not opened here — tuwunel
|
Federation port 8448 is intentionally not opened here — tuwunel
|
||||||
|
|
@ -82,10 +81,10 @@ see it. The agent only receives the resulting `access_token`.
|
||||||
writes when the file is missing or empty; always re-applies 0600
|
writes when the file is missing or empty; always re-applies 0600
|
||||||
(normalises any 0640 / world-readable carry-over from
|
(normalises any 0640 / world-readable carry-over from
|
||||||
pre-LoadCredential deployments). This runs at activation time
|
pre-LoadCredential deployments). This runs at activation time
|
||||||
(not first container start) to dodge the argus #565 race where
|
(not first container start) to dodge a race where nspawn creates
|
||||||
nspawn creates an empty file when the bind-mount target is
|
an empty file when the bind-mount target is missing and tuwunel
|
||||||
missing and tuwunel reads `registration_token_file=""` rejecting
|
reads `registration_token_file=""`, rejecting every registration
|
||||||
every registration until next restart.
|
until next restart.
|
||||||
2. **Read-only bind-mount** maps the host file into the tuwunel
|
2. **Read-only bind-mount** maps the host file into the tuwunel
|
||||||
container at the same path.
|
container at the same path.
|
||||||
3. **systemd `LoadCredential=`** inside the container copies the
|
3. **systemd `LoadCredential=`** inside the container copies the
|
||||||
|
|
@ -93,8 +92,7 @@ see it. The agent only receives the resulting `access_token`.
|
||||||
`/run/credentials/tuwunel.service/registration_token`, owned by
|
`/run/credentials/tuwunel.service/registration_token`, owned by
|
||||||
tuwunel's dynamic user with mode `0400`, at service start. The
|
tuwunel's dynamic user with mode `0400`, at service start. The
|
||||||
host file stays `root:root 0600` — no `chown :tuwunel` /
|
host file stays `root:root 0600` — no `chown :tuwunel` /
|
||||||
`chmod 0640` / GID-pin gymnastics required (per iris on #644
|
`chmod 0640` / GID-pin gymnastics required. Keeps
|
||||||
8043, dropping the shape #649 originally shipped with). Keeps
|
|
||||||
`DynamicUser = true` + `PrivateUsers = true` intact.
|
`DynamicUser = true` + `PrivateUsers = true` intact.
|
||||||
4. tuwunel's `registration_token_file` points at the credentials
|
4. tuwunel's `registration_token_file` points at the credentials
|
||||||
path, not the original bind-mount path.
|
path, not the original bind-mount path.
|
||||||
|
|
@ -104,7 +102,7 @@ see it. The agent only receives the resulting `access_token`.
|
||||||
matrix MCP client authenticates with that access_token and
|
matrix MCP client authenticates with that access_token and
|
||||||
never touches the shared registration token.
|
never touches the shared registration token.
|
||||||
|
|
||||||
Initial rollout settings (#548):
|
Initial rollout settings:
|
||||||
|
|
||||||
- `allow_federation = true` at the protocol level so swarms can be
|
- `allow_federation = true` at the protocol level so swarms can be
|
||||||
wired up later by extending `trustedServers` without a homeserver
|
wired up later by extending `trustedServers` without a homeserver
|
||||||
|
|
@ -114,25 +112,25 @@ Initial rollout settings (#548):
|
||||||
engage). The absent
|
engage). The absent
|
||||||
`yes_i_am_very_very_sure_…_open_registration_…` flag keeps the
|
`yes_i_am_very_very_sure_…_open_registration_…` flag keeps the
|
||||||
server closed to anyone without the token.
|
server closed to anyone without the token.
|
||||||
- `allow_encryption = false` per operator call (#548). E2EE
|
- `allow_encryption = false` per operator call; E2EE re-enabling is
|
||||||
re-enabling tracked at #551.
|
deferred to a follow-up.
|
||||||
|
|
||||||
## Assertion rationale
|
## Assertion rationale
|
||||||
|
|
||||||
Two `config.assertions` entries fail eval early rather than ship
|
Two `config.assertions` entries fail eval early rather than ship
|
||||||
surprising behaviour:
|
surprising behaviour:
|
||||||
|
|
||||||
- **`hyperhiveDomain != null || cfg.serverName != null`** (mara on
|
- **`hyperhiveDomain != null || cfg.serverName != null`** —
|
||||||
#548) — `server_name` is embedded into every user / room ID
|
`server_name` is embedded into every user / room ID irrevocably;
|
||||||
irrevocably; we refuse to spawn the homeserver with a bogus
|
we refuse to spawn the homeserver with a bogus `server_name` we
|
||||||
server_name we can never change later.
|
can never change later.
|
||||||
- **`cfg.gatewayHost != ""`** (argus 🟡 on #764) — same footgun as
|
- **`cfg.gatewayHost != ""`** — same footgun as `forge.domain`:
|
||||||
`forge.domain` (#754). Empty string renders `.<hive>`-shaped
|
empty string renders `.<hive>`-shaped garbage in both nginx
|
||||||
garbage in both nginx `server_name` (treated as wildcard
|
`server_name` (treated as wildcard catch-all, surprising) and
|
||||||
catch-all, surprising) and `/etc/hosts` (invalid entry). `null`
|
`/etc/hosts` (invalid entry). `null` is the right opt-out shape;
|
||||||
is the right opt-out shape; empty string is rejected explicitly.
|
empty string is rejected explicitly.
|
||||||
|
|
||||||
## fluffychat-web build fixes (#685)
|
## fluffychat-web build fixes
|
||||||
|
|
||||||
`pkgs.fluffychat-web` ships from `flutter341.buildFlutterApplication`,
|
`pkgs.fluffychat-web` ships from `flutter341.buildFlutterApplication`,
|
||||||
which has two upstream gaps for fluffychat's web target:
|
which has two upstream gaps for fluffychat's web target:
|
||||||
|
|
@ -163,37 +161,18 @@ Both fixed in `nix/modules/hive-matrix.nix` via two derivations:
|
||||||
|
|
||||||
Two non-obvious fixes from review history:
|
Two non-obvious fixes from review history:
|
||||||
|
|
||||||
- **`make -C js`** instead of `cd js; make` (argus 🟡 on #697 v2)
|
- **`make -C js`** instead of `cd js; make` — keeps the build-phase
|
||||||
— keeps the build-phase pwd at the source root so `installPhase`
|
pwd at the source root so `installPhase` doesn't have to know
|
||||||
doesn't have to know about the cd. Robust against future
|
about the cd. Robust against future reorders / `dontBuild`.
|
||||||
reorders / `dontBuild`.
|
|
||||||
- **`web/native_executor.dart`** as a build-CWD-relative path,
|
- **`web/native_executor.dart`** as a build-CWD-relative path,
|
||||||
*not* `$src/web/...` (#685 / #733 fixup) — `dart`'s
|
*not* `$src/web/...` — `dart`'s `package_config.json` walk-up
|
||||||
`package_config.json` walk-up needs to hit
|
needs to hit `buildFlutterApplication`'s pub-get output
|
||||||
`buildFlutterApplication`'s pub-get output (`.dart_tool/` in the
|
(`.dart_tool/` in the build CWD). Walking up from a read-only
|
||||||
build CWD). Walking up from a read-only `$src/` store path finds
|
`$src/` store path finds no `.dart_tool/` and errors with
|
||||||
no `.dart_tool/` and errors with "Couldn't resolve the package
|
"Couldn't resolve the package 'matrix'".
|
||||||
'matrix'". Confused two PRs.
|
|
||||||
|
|
||||||
Drop both derivations when nixpkgs's flutter builder grows worker
|
Drop both derivations when nixpkgs's flutter builder grows worker
|
||||||
+ emcc support upstream.
|
+ emcc support upstream.
|
||||||
|
|
||||||
Mount point is `matrix.<hive>/` (#772); upstream `--base-href "/"`
|
Mount point is `matrix.<hive>/`; upstream `--base-href "/"` is
|
||||||
is correct at sub-domain root, no override.
|
correct at sub-domain root, no override.
|
||||||
|
|
||||||
## Sequencing history
|
|
||||||
|
|
||||||
- #548 — initial rollout (federation enabled, registration enabled,
|
|
||||||
E2EE disabled)
|
|
||||||
- #565 — first-boot empty-token race fix → activation-time token
|
|
||||||
generation
|
|
||||||
- #644 / iris 8043 / #649 — registration token ownership shape
|
|
||||||
(dropped chown/GID-pin; LoadCredential delivers as 0400 dynamic-user)
|
|
||||||
- #651 — `openFirewall` default flipped to `false`
|
|
||||||
- #660 — `serverName` default flipped to bare hive-domain (was
|
|
||||||
`matrix.<hive>`)
|
|
||||||
- #685 / #697 / #733 — fluffychat-web build fixes (Imaging emscripten,
|
|
||||||
native_executor dart worker, build-CWD path)
|
|
||||||
- #736 — fluffychat config.json inline JSON at sub-domain root
|
|
||||||
- #749 / #764 — gateway sub-domain shape verdict
|
|
||||||
- #772 / #775 — fluffychat hops from `<hive>/matrix/` to `matrix.<hive>/`
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,21 @@
|
||||||
# hive-network
|
# hive-network
|
||||||
|
|
||||||
Host-side bridge + per-agent DNS resolver — the foundation that
|
Host-side bridge + per-agent DNS resolver — the foundation that
|
||||||
makes [`#14` container netns isolation](http://localhost:3000/hyperhive/hyperhive/issues/14)
|
makes container netns isolation safe to land. Configured via
|
||||||
safe to land. Configured via `services.hyperhive.network.*`; off by
|
`services.hyperhive.network.*`; off by default during rollout.
|
||||||
default during rollout.
|
|
||||||
|
|
||||||
## Why ship before #14
|
## Why ship before netns isolation
|
||||||
|
|
||||||
Mara on #805#issuecomment-11541: "we need it before #14 so nothing
|
If netns isolation lands first, agent containers lose
|
||||||
breaks when we switch over". If netns isolation lands first, agent
|
`/etc/resolv.conf` propagation from the host and DNS breaks until a
|
||||||
containers lose `/etc/resolv.conf` propagation from the host and DNS
|
separate resolver is up. Inverting the sequence — bridge + dnsmasq
|
||||||
breaks until a separate resolver is up. Inverting the sequence —
|
first, netns flip second — makes the flag day boring: the resolver
|
||||||
bridge + dnsmasq first, netns flip second — makes the flag day
|
endpoint is already live, agents just discover it via veth instead
|
||||||
boring: the resolver endpoint is already live, agents just discover
|
of shared netns.
|
||||||
it via veth instead of shared netns.
|
|
||||||
|
|
||||||
## v1 vs v2
|
## v1 vs v2
|
||||||
|
|
||||||
| feature | v1 (this PR) | v2 (after #14) |
|
| feature | v1 (this PR) | v2 (after netns isolation) |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| bridge interface | created on host, no slave NICs | per-agent veth pairs attach |
|
| bridge interface | created on host, no slave NICs | per-agent veth pairs attach |
|
||||||
| dnsmasq binding | bridge IP (reachable via host loopback in shared netns) | bridge IP (reachable via veth in private netns) |
|
| dnsmasq binding | bridge IP (reachable via host loopback in shared netns) | bridge IP (reachable via veth in private netns) |
|
||||||
|
|
@ -27,18 +25,17 @@ it via veth instead of shared netns.
|
||||||
|
|
||||||
The `address` rules ship pointing at the bridge IP from v1 so the
|
The `address` rules ship pointing at the bridge IP from v1 so the
|
||||||
DNS contract is fixed before any container actually depends on it
|
DNS contract is fixed before any container actually depends on it
|
||||||
— minimises the things that flip on #14 day.
|
— minimises the things that flip on netns day.
|
||||||
|
|
||||||
## Container shape (where dnsmasq lives)
|
## Container shape (where dnsmasq lives)
|
||||||
|
|
||||||
Co-located in the existing `hive-gateway` container per mara on
|
Co-located in the existing `hive-gateway` container — single
|
||||||
#805:10957 — single front-door for both DNS and HTTP, saves a
|
front-door for both DNS and HTTP, saves a sibling container, single
|
||||||
sibling container, single systemd-unit / state surface to monitor.
|
systemd-unit / state surface to monitor. The gateway shares host
|
||||||
The gateway shares host netns (`privateNetwork = false`) so
|
netns (`privateNetwork = false`) so dnsmasq's `bind-interfaces`
|
||||||
dnsmasq's `bind-interfaces` listener on `bridgeIp` works without
|
listener on `bridgeIp` works without any veth gymnastics today; when
|
||||||
any veth gymnastics today; when #14 flips agent containers to
|
agent containers flip to private netns the binding doesn't change
|
||||||
private netns the binding doesn't change (it's still on the host's
|
(it's still on the host's bridge interface).
|
||||||
bridge interface).
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
@ -98,16 +95,6 @@ agent containers.
|
||||||
interface only. Other interfaces stay closed. The hive resolver
|
interface only. Other interfaces stay closed. The hive resolver
|
||||||
isn't an external-facing service.
|
isn't an external-facing service.
|
||||||
|
|
||||||
## Sequencing history
|
|
||||||
|
|
||||||
- mara on #805 (`comment-10957`): "can we put the resolver into the
|
|
||||||
gateway container?" — yes, this v1 does that.
|
|
||||||
- mara on #805 (`comment-11541`): "we need it before #14 so nothing
|
|
||||||
breaks when we switch over" — flipped the dependency direction;
|
|
||||||
v1 ships now, #14 flips containers later.
|
|
||||||
|
|
||||||
## Cross-references
|
## Cross-references
|
||||||
|
|
||||||
- Issue [#805](http://localhost:3000/hyperhive/hyperhive/issues/805) — DNS resolver tracking
|
|
||||||
- Issue [#14](http://localhost:3000/hyperhive/hyperhive/issues/14) — netns isolation (downstream consumer)
|
|
||||||
- `docs/gateway.md` — vhost map + the gateway container's other duties
|
- `docs/gateway.md` — vhost map + the gateway container's other duties
|
||||||
|
|
|
||||||
|
|
@ -540,7 +540,9 @@ fetch entirely.
|
||||||
module sets when `services.hyperhive.gateway.enable = true`).
|
module sets when `services.hyperhive.gateway.enable = true`).
|
||||||
Gateway-on (default): `container` → `/agent/<name>/<url>` (same
|
Gateway-on (default): `container` → `/agent/<name>/<url>` (same
|
||||||
origin, gateway proxies to the per-agent harness — TCP or
|
origin, gateway proxies to the per-agent harness — TCP or
|
||||||
unix-domain per #784). Gateway-off (legacy / local dev):
|
unix-domain depending on the agent's `HIVE_WEB_SOCKET` opt-in,
|
||||||
|
see `docs/gateway.md::Per-agent unix-socket upstream`).
|
||||||
|
Gateway-off (legacy / local dev):
|
||||||
`container` → `http://<host>:<container.port>/<url>` (direct TCP
|
`container` → `http://<host>:<container.port>/<url>` (direct TCP
|
||||||
fallback). Forge links resolve against `http://<host>:3000`,
|
fallback). Forge links resolve against `http://<host>:3000`,
|
||||||
external links are already absolute. The same flag drives the
|
external links are already absolute. The same flag drives the
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue