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.
178 lines
8.1 KiB
Markdown
178 lines
8.1 KiB
Markdown
# hive-matrix
|
|
|
|
Private Matrix homeserver (matrix-tuwunel — the conduwuit
|
|
successor) wrapped in a nixos-container, plus optional fluffychat-web
|
|
client at `matrix.<hive>/`. Configured via
|
|
`services.hyperhive.matrix.*`; vhost routing lives in
|
|
[`gateway.md`](gateway.md).
|
|
|
|
## Container shape
|
|
|
|
Same shape as [`gateway.md::hive-forge container shape`](gateway.md):
|
|
|
|
- Container name `hive-matrix` (not `h-*`) so c0re's lifecycle
|
|
scanner ignores it; operator manages via the standard
|
|
`nixos-container` CLI.
|
|
- Keeps hive-matrix from fighting any `services.matrix-*` the
|
|
operator already runs on the host — separate systemd namespace,
|
|
separate state dir.
|
|
- Container shares the host network namespace
|
|
(`privateNetwork = false`) so agents reach tuwunel at
|
|
`http://localhost:<httpPort>` without extra plumbing — the
|
|
nixos-container is here for state + systemd-unit isolation, not
|
|
network isolation.
|
|
- Persistent state at
|
|
`/var/lib/nixos-containers/hive-matrix/var/lib/matrix-tuwunel/`
|
|
survives container restart / host reboot. To wipe, destroy the
|
|
container.
|
|
|
|
## Identity vs API listener: `serverName` vs `gatewayHost`
|
|
|
|
Two distinct hostnames:
|
|
|
|
- **`serverName`** — matrix-spec `server_name`, embedded
|
|
*irrevocably* in every `@user:<server_name>` and `!room:<server_name>`
|
|
identifier minted on this homeserver. Cannot be changed later
|
|
without abandoning every account and chat history. Defaults to the
|
|
bare `services.hyperhive.domain`; clients auto-discover the actual
|
|
API endpoint via the `.well-known/matrix/{client,server}` routes
|
|
the hive-gateway serves at that domain.
|
|
- **`gatewayHost`** — the API listener hostname, where the gateway's
|
|
matrix vhost proxies `/_matrix/*` to tuwunel. Defaults to
|
|
`matrix.<services.hyperhive.domain>` (sub-domain shape). Set to
|
|
`null` to skip the gateway vhost (tuwunel stays direct on
|
|
`httpPort`).
|
|
|
|
**Breaking change**: `serverName` used to default to
|
|
`matrix.${services.hyperhive.domain}`. Existing homeservers must set
|
|
the option explicitly to preserve their existing user / room IDs
|
|
before rebuilding. The default flipped because the bare hive-domain
|
|
makes for cleaner matrix IDs and `.well-known` delegation hides the
|
|
sub-domain from the user-facing identifier.
|
|
|
|
## Default-closed firewall
|
|
|
|
`openFirewall` defaults to `false` (secure-by-default): the
|
|
homeserver is reachable from the host + every agent container via
|
|
loopback either way (shared netns), so the firewall hole only
|
|
matters for access from *outside* the host. Flip to `true` when
|
|
announcing the homeserver to other hives or when an external matrix
|
|
client needs to reach the client-server API directly.
|
|
|
|
**Breaking change**: used to default to `true`. Operators relying on
|
|
external reach must add
|
|
`services.hyperhive.matrix.openFirewall = true;` before rebuilding.
|
|
|
|
Federation port 8448 is intentionally not opened here — tuwunel
|
|
serves the federation API on the same `httpPort` as client-server
|
|
by default. Reaching it on 8448 needs either an explicit tuwunel
|
|
bind to that port OR a reverse-proxy + `.well-known/matrix/server`
|
|
delegation (the latter lives in `gateway.md::Discovery flow`).
|
|
|
|
## Provisioning flow (registration token)
|
|
|
|
Token-gated registration: hive-c0re holds the token, agents never
|
|
see it. The agent only receives the resulting `access_token`.
|
|
|
|
1. **System activation** writes a 32-byte random hex token (64
|
|
chars) to `cfg.registrationTokenFile`
|
|
(`/var/lib/hyperhive/matrix-register-token` by default), mode
|
|
`0600 root:root`, before any container start. Idempotent — only
|
|
writes when the file is missing or empty; always re-applies 0600
|
|
(normalises any 0640 / world-readable carry-over from
|
|
pre-LoadCredential deployments). This runs at activation time
|
|
(not first container start) to dodge a race where nspawn creates
|
|
an empty file when the bind-mount target is missing and tuwunel
|
|
reads `registration_token_file=""`, rejecting every registration
|
|
until next restart.
|
|
2. **Read-only bind-mount** maps the host file into the tuwunel
|
|
container at the same path.
|
|
3. **systemd `LoadCredential=`** inside the container copies the
|
|
bind-mounted file into
|
|
`/run/credentials/tuwunel.service/registration_token`, owned by
|
|
tuwunel's dynamic user with mode `0400`, at service start. The
|
|
host file stays `root:root 0600` — no `chown :tuwunel` /
|
|
`chmod 0640` / GID-pin gymnastics required. Keeps
|
|
`DynamicUser = true` + `PrivateUsers = true` intact.
|
|
4. tuwunel's `registration_token_file` points at the credentials
|
|
path, not the original bind-mount path.
|
|
5. **hive-c0re** uses the token to register each agent account via
|
|
the matrix-spec UIAA registration flow, persists the returned
|
|
`access_token` to `<agent-state>/matrix-token`. The agent's
|
|
matrix MCP client authenticates with that access_token and
|
|
never touches the shared registration token.
|
|
|
|
Initial rollout settings:
|
|
|
|
- `allow_federation = true` at the protocol level so swarms can be
|
|
wired up later by extending `trustedServers` without a homeserver
|
|
restart. `trusted_servers = []` keeps it effectively closed
|
|
until peers are listed.
|
|
- `allow_registration = true` (required for the token flow to
|
|
engage). The absent
|
|
`yes_i_am_very_very_sure_…_open_registration_…` flag keeps the
|
|
server closed to anyone without the token.
|
|
- `allow_encryption = false` per operator call; E2EE re-enabling is
|
|
deferred to a follow-up.
|
|
|
|
## Assertion rationale
|
|
|
|
Two `config.assertions` entries fail eval early rather than ship
|
|
surprising behaviour:
|
|
|
|
- **`hyperhiveDomain != null || cfg.serverName != null`** —
|
|
`server_name` is embedded into every user / room ID irrevocably;
|
|
we refuse to spawn the homeserver with a bogus `server_name` we
|
|
can never change later.
|
|
- **`cfg.gatewayHost != ""`** — same footgun as `forge.domain`:
|
|
empty string renders `.<hive>`-shaped garbage in both nginx
|
|
`server_name` (treated as wildcard catch-all, surprising) and
|
|
`/etc/hosts` (invalid entry). `null` is the right opt-out shape;
|
|
empty string is rejected explicitly.
|
|
|
|
## fluffychat-web build fixes
|
|
|
|
`pkgs.fluffychat-web` ships from `flutter341.buildFlutterApplication`,
|
|
which has two upstream gaps for fluffychat's web target:
|
|
|
|
- The dart web-worker entry point (`web/native_executor.dart`) isn't
|
|
compiled — `buildFlutterApplication` only runs `flutter build web`
|
|
on the main entry.
|
|
- `native_imaging`'s C source isn't built — emscripten isn't a
|
|
flutter-builder native build input.
|
|
|
|
Both fixed in `nix/modules/hive-matrix.nix` via two derivations:
|
|
|
|
- **`fluffychat-web-imaging`** builds `Imaging.{js,wasm}` from the
|
|
`native_imaging` C source via `pkgs.emscripten`. Source comes
|
|
from `pkgs.fluffychat-web.passthru.pubspecLock.dependencySources.native_imaging`
|
|
— already in the build closure of the flutter app, so no parallel
|
|
hash pin and version auto-syncs with nixpkgs bumps. Build closure
|
|
is ~3.6 GiB (emscripten LLVM); runtime closure is just the two
|
|
output files. `dontConfigure = true` because cmake runs inside
|
|
`js/Makefile` via `emcmake cmake`, not at the package root. The
|
|
build script needs `HOME` + `EM_CACHE` writable for emscripten's
|
|
on-demand sysroot build (libc, libc++ → wasm).
|
|
- **`fluffychat-web-fixed`** is `pkgs.fluffychat-web` plus a
|
|
`postInstall` patch that (a) compiles `web/native_executor.dart`
|
|
via `dart compile js` (dart from the flutter341 closure, no
|
|
incremental cost) and (b) installs `fluffychat-web-imaging`'s
|
|
outputs into `$out`.
|
|
|
|
Two non-obvious fixes from review history:
|
|
|
|
- **`make -C js`** instead of `cd js; make` — keeps the build-phase
|
|
pwd at the source root so `installPhase` doesn't have to know
|
|
about the cd. Robust against future reorders / `dontBuild`.
|
|
- **`web/native_executor.dart`** as a build-CWD-relative path,
|
|
*not* `$src/web/...` — `dart`'s `package_config.json` walk-up
|
|
needs to hit `buildFlutterApplication`'s pub-get output
|
|
(`.dart_tool/` in the build CWD). Walking up from a read-only
|
|
`$src/` store path finds no `.dart_tool/` and errors with
|
|
"Couldn't resolve the package 'matrix'".
|
|
|
|
Drop both derivations when nixpkgs's flutter builder grows worker
|
|
+ emcc support upstream.
|
|
|
|
Mount point is `matrix.<hive>/`; upstream `--base-href "/"` is
|
|
correct at sub-domain root, no override.
|