nix: add hive-matrix module + hyperhive.domain option (#548 part 1)
This commit is contained in:
parent
b43438d1a6
commit
2b1c1b54ac
3 changed files with 259 additions and 4 deletions
10
CLAUDE.md
10
CLAUDE.md
|
|
@ -197,10 +197,18 @@ nix/
|
|||
modules/hive-c0re.nix systemd service + firewall + git wiring;
|
||||
`contextWindowTokens` attrset (per-model,
|
||||
injected as env vars into all containers);
|
||||
imports hive-forge.nix
|
||||
top-level `hyperhive.domain` option
|
||||
(nullable; required when matrix.enable);
|
||||
imports hive-forge.nix + hive-matrix.nix
|
||||
modules/hive-forge.nix optional in-container Forgejo
|
||||
(`hyperhive.forge.enable`, default on);
|
||||
Catppuccin Mocha theme via tmpfiles C+ copy
|
||||
modules/hive-matrix.nix optional in-container matrix-tuwunel
|
||||
homeserver (`hyperhive.matrix.enable`,
|
||||
default off); server_name derives from
|
||||
`hyperhive.domain` (subdomain by default);
|
||||
federation on (empty trusted_servers),
|
||||
registration off, e2ee deferred (#551)
|
||||
templates/harness-base.nix shared scaffolding for sub-agents + manager;
|
||||
`hyperhive.model` option (HIVE_DEFAULT_MODEL)
|
||||
templates/agent-base.nix sub-agent nixosConfiguration
|
||||
|
|
|
|||
|
|
@ -17,8 +17,32 @@ in
|
|||
{
|
||||
# The forge is part of the standard install — hive-c0re mirrors
|
||||
# every agent's applied config repo into it. On by default; opt out
|
||||
# with `hyperhive.forge.enable = false`.
|
||||
imports = [ ./hive-forge.nix ];
|
||||
# with `hyperhive.forge.enable = false`. hive-matrix is opt-in (off
|
||||
# by default) and asserts that `hyperhive.domain` is set before it
|
||||
# can be enabled.
|
||||
imports = [
|
||||
./hive-forge.nix
|
||||
./hive-matrix.nix
|
||||
];
|
||||
|
||||
# Top-level option shared by any hyperhive subsystem that needs a
|
||||
# stable hostname (matrix server_name today, forge ROOT_URL likely
|
||||
# next). Type is nullable + default null so existing operator
|
||||
# configs that don't set it still evaluate; subsystems that
|
||||
# actually need it (matrix) assert non-null in their own config
|
||||
# block with a helpful message.
|
||||
options.hyperhive.domain = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "darkest.space";
|
||||
description = ''
|
||||
Canonical host domain for hyperhive subsystems that need a
|
||||
stable name (currently: `hyperhive.matrix.serverName` derives
|
||||
from this when `useSubdomain = true`). No default — subsystems
|
||||
that opt to require it assert non-null in their own config and
|
||||
fail eval with a helpful message if it's missing.
|
||||
'';
|
||||
};
|
||||
|
||||
options.services.hive-c0re = {
|
||||
enable = lib.mkEnableOption "hive-c0re — hyperhive coordinator daemon";
|
||||
|
|
@ -171,7 +195,8 @@ in
|
|||
# serves this via `tower_http::ServeDir` for any path it doesn't
|
||||
# match against an API/action route.
|
||||
HIVE_STATIC_DIR = "${cfg.frontend}/dashboard";
|
||||
} // lib.optionalAttrs config.hyperhive.forge.enable {
|
||||
}
|
||||
// lib.optionalAttrs config.hyperhive.forge.enable {
|
||||
# Agents poll this URL for Forgejo notifications. Derived from
|
||||
# hyperhive.forge.{domain,httpPort} so it tracks forge config changes.
|
||||
HIVE_FORGE_URL = "http://${config.hyperhive.forge.domain}:${toString config.hyperhive.forge.httpPort}";
|
||||
|
|
|
|||
222
nix/modules/hive-matrix.nix
Normal file
222
nix/modules/hive-matrix.nix
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.hyperhive.matrix;
|
||||
hyperhiveDomain = config.hyperhive.domain;
|
||||
effectiveServerName =
|
||||
if cfg.serverName != null then
|
||||
cfg.serverName
|
||||
else if cfg.useSubdomain then
|
||||
"matrix.${hyperhiveDomain}"
|
||||
else
|
||||
hyperhiveDomain;
|
||||
in
|
||||
{
|
||||
# Private Matrix homeserver (matrix-tuwunel — the official conduwuit
|
||||
# successor) for hyperhive agents, wrapped in a nixos-container so it
|
||||
# doesn't fight any existing `services.matrix-*` the operator may
|
||||
# already run on the host. Same shape as `nix/modules/hive-forge.nix`:
|
||||
# shared host netns (`privateNetwork = false`) so agents reach it at
|
||||
# `http://localhost:<httpPort>` (or via the configured server_name
|
||||
# for federation), nixos-container only here for state + systemd-unit
|
||||
# isolation.
|
||||
#
|
||||
# Container name `hive-matrix` (not `h-*`) so the lifecycle scanner
|
||||
# ignores it; operator manages via the standard `nixos-container` CLI.
|
||||
#
|
||||
# Persistent state at `/var/lib/nixos-containers/hive-matrix/var/lib/
|
||||
# matrix-tuwunel/` (survives container restart / host reboot). To
|
||||
# wipe, destroy the container.
|
||||
#
|
||||
# Initial rollout (#548): federation enabled (needed for multi-hive
|
||||
# swarms; trusted_servers starts empty so no actual federation traffic
|
||||
# leaves until peers are explicitly listed), registration via admin
|
||||
# API only, e2ee disabled per operator call (tracked for follow-up at
|
||||
# #551).
|
||||
|
||||
options.hyperhive.matrix = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Run hive-matrix — a private matrix-tuwunel homeserver (in a
|
||||
nixos-container) for hyperhive agents. Off by default while
|
||||
the integration phases in; flip to `true` once the operator
|
||||
has set `hyperhive.domain` and is ready to onboard agents.
|
||||
'';
|
||||
};
|
||||
|
||||
package = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.matrix-tuwunel;
|
||||
defaultText = lib.literalExpression "pkgs.matrix-tuwunel";
|
||||
description = ''
|
||||
matrix-tuwunel package to run inside the container. Defaults
|
||||
to nixpkgs's `pkgs.matrix-tuwunel`. Override to pin a
|
||||
specific upstream if you need an unreleased feature.
|
||||
'';
|
||||
};
|
||||
|
||||
serverName = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.str;
|
||||
default = null;
|
||||
example = "matrix.darkest.space";
|
||||
description = ''
|
||||
Matrix `server_name` — the host part of every user ID
|
||||
(`@argus:<server_name>`) and room ID minted on this
|
||||
homeserver. CRITICAL: must be stable from day one because
|
||||
it's embedded irrevocably in the identifiers. Defaults to
|
||||
`matrix.''${hyperhive.domain}` when `useSubdomain = true`
|
||||
(the typical path — keeps the root domain free for the
|
||||
dashboard or forge), or to `''${hyperhive.domain}` itself
|
||||
when `useSubdomain = false`. Override here only if you need
|
||||
a name that doesn't follow either pattern.
|
||||
'';
|
||||
};
|
||||
|
||||
useSubdomain = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
When `serverName` is unset, use `matrix.''${hyperhive.domain}`
|
||||
rather than `''${hyperhive.domain}` itself. The default is
|
||||
true because the root domain is usually claimed by the
|
||||
dashboard or the forge; turn off only if the root domain is
|
||||
free AND you want matrix to serve from there.
|
||||
'';
|
||||
};
|
||||
|
||||
httpPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8008;
|
||||
description = ''
|
||||
TCP port tuwunel serves the matrix client-server API on.
|
||||
Default 8008 is the matrix-spec well-known port. Sits
|
||||
outside hyperhive's claimed ranges (dashboard 7000, manager
|
||||
8000, sub-agents 8100..8999). Federation listens on
|
||||
`federationPort` separately.
|
||||
'';
|
||||
};
|
||||
|
||||
federationPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8448;
|
||||
description = ''
|
||||
TCP port tuwunel serves the matrix server-server (federation)
|
||||
API on. Default 8448 is the matrix-spec well-known port.
|
||||
Open in the host firewall when `openFirewall = true`; needed
|
||||
for multi-hive swarms to talk to each other.
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Open `httpPort` + `federationPort` in the host firewall. Off
|
||||
when the homeserver should only be reachable from inside the
|
||||
host (e.g. while bringing the integration up before
|
||||
announcing it to other hives).
|
||||
'';
|
||||
};
|
||||
|
||||
trustedServers = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
example = [ "matrix.org" ];
|
||||
description = ''
|
||||
List of trusted matrix servers (homeservers whose signing
|
||||
keys this server will fetch identity-server-style). Empty
|
||||
by default — federation is enabled at the protocol level
|
||||
but no peer is trusted until listed here, so the homeserver
|
||||
is effectively closed until the operator declares hive
|
||||
peers explicitly.
|
||||
'';
|
||||
};
|
||||
|
||||
maxRequestSize = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "20000000";
|
||||
description = ''
|
||||
Maximum size in bytes of a single matrix client request body.
|
||||
Default 20 MB matches the matrix-spec recommendation for
|
||||
media uploads. String-typed to match tuwunel's TOML config
|
||||
shape (it accepts both integer + size-with-suffix strings).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# mara on #548: "there is no default, but it is required. add
|
||||
# assertion." — fail eval with a helpful message rather than
|
||||
# spawning a homeserver with a bogus server_name we can never
|
||||
# change later. `hyperhive.domain` is host-wide; matrix derives
|
||||
# the server_name from it (or from `cfg.serverName` if the
|
||||
# operator wants to override).
|
||||
assertions = [
|
||||
{
|
||||
assertion = hyperhiveDomain != null || cfg.serverName != null;
|
||||
message = ''
|
||||
hyperhive.matrix.enable = true requires either:
|
||||
- hyperhive.domain set to your host's canonical domain
|
||||
(recommended; shared with forge / dashboard), or
|
||||
- hyperhive.matrix.serverName set explicitly.
|
||||
|
||||
The matrix server_name is embedded into every user ID and
|
||||
room ID on this homeserver — it cannot be changed later
|
||||
without losing every account and chat history. Pick a
|
||||
stable hostname before enabling.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
containers.hive-matrix = {
|
||||
autoStart = true;
|
||||
ephemeral = false;
|
||||
# Share host netns — tuwunel's listeners look exactly like
|
||||
# host-side services, no port-forward plumbing, and agent
|
||||
# containers (also host netns) reach it via plain `localhost`.
|
||||
privateNetwork = false;
|
||||
config =
|
||||
{ ... }:
|
||||
{
|
||||
system.stateVersion = "25.11";
|
||||
services.matrix-tuwunel = {
|
||||
enable = true;
|
||||
package = cfg.package;
|
||||
settings.global = {
|
||||
server_name = effectiveServerName;
|
||||
address = "0.0.0.0";
|
||||
port = cfg.httpPort;
|
||||
max_request_size = cfg.maxRequestSize;
|
||||
# Federation enabled at the protocol level so swarms
|
||||
# can be wired up later by extending `trustedServers`
|
||||
# without a homeserver restart. Empty trusted_servers
|
||||
# keeps it effectively closed until peers are listed.
|
||||
allow_federation = true;
|
||||
trusted_servers = cfg.trustedServers;
|
||||
# Registration off — operator seeds agent accounts via
|
||||
# the tuwunel admin API (mirrors the forge pattern;
|
||||
# see `hive-c0re/src/matrix.rs` once #548 PR 2 lands).
|
||||
allow_registration = false;
|
||||
# E2EE disabled in initial rollout per operator call
|
||||
# (#548) — re-enabling tracked at #551.
|
||||
allow_encryption = false;
|
||||
};
|
||||
};
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = lib.mkIf cfg.openFirewall {
|
||||
allowedTCPPorts = [
|
||||
cfg.httpPort
|
||||
cfg.federationPort
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue