Watch
0
0
Fork
You've already forked hyperhive
0

drop option renames and removals older than two weeks

Refs #5379
This commit is contained in:
atlas 2026-10-16 20:28:14 +02:00
commit 8e09f8360a
47 changed files with 98 additions and 1026 deletions

View file

@ -19,23 +19,6 @@
...
}:
let
# What an `agent.nix` still setting the dropped per-agent matrix switch is
# told. Shared by the two `mkRemovedOptionModule` entries below, one per
# spelling of the path.
matrixEnableRemoved = ''
The per-agent matrix switch is gone. Matrix is enabled for an agent exactly
when it has at least one services.hyperhive.agent.matrixAccounts entry, and
the hive-internal `main` entry is declared for you whenever
services.hyperhive.agent.matrix.url is non-null.
- `enable = true` was the default: drop the line, nothing else to do.
- `enable = false`: set services.hyperhive.agent.matrix.url = null instead
(and declare no accounts of your own). That is what leaves the account set
empty, which is now what suppresses the daemon, its path watcher and the
injected extraMcpServers.matrix entry.
See docs/tools/matrix.md.
'';
hiveNameRemoved = ''
An agent reads its hive's name at runtime from /etc/hyperhive-hive-facts,
which the hive writes into every container, so no agent build sets it.
@ -71,36 +54,12 @@ in
./packages.nix
./queue.nix
./queue-identity.nix
./renamed-options.nix
./user.nix
./screen.nix
./weston-vnc.nix
(lib.mkRemovedOptionModule [ "hyperhive" "web" "useUnixSocket" ] ''
Unix socket mode is always enabled for all agents. Remove the
setting from your agent.nix.
'')
(lib.mkRemovedOptionModule [ "hyperhive" "allowedBashPatterns" ] ''
The built-in Bash tool is fully disabled; agents use
mcp__bash__run instead. Remove the setting from your agent.nix.
'')
# The dropped per-agent matrix switch. A removal, not a silent delete,
# because the definition that *changes meaning* is `false`: left undeclared
# it would be ignored and the agent would quietly gain the matrix tools its
# operator turned off. Failing the eval with the replacement spelling is the
# only outcome that cannot do that.
#
# Both spellings, for the same reason ../host-modules/deploy.nix lists both
# of `swarm|deploy.matrix.registrationTokenFile`: an existing agent.nix may
# carry either the `hyperhive.*` path or the `services.hyperhive.agent.*`
# one, and only the one it actually carries will fire. Neither is an entry
# in ./renamed-options.nix — a rename pointing at an undeclared option
# breaks evaluation for everyone, removed or not.
(lib.mkRemovedOptionModule [ "hyperhive" "matrix" "enable" ] matrixEnableRemoved)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "agent" "matrix" "enable" ] matrixEnableRemoved)
# Both spellings, for the reason given above.
(lib.mkRemovedOptionModule [ "hyperhive" "hiveName" ] hiveNameRemoved)
# The hive supplies both at runtime; a definition fails the eval, naming
# where the value comes from.
(lib.mkRemovedOptionModule [ "services" "hyperhive" "agent" "hiveName" ] hiveNameRemoved)
(lib.mkRemovedOptionModule [ "hyperhive" "claudeMemoryMaxBytes" ] memoryMaxBytesRemoved)
(lib.mkRemovedOptionModule [
"services"
"hyperhive"

View file

@ -1,207 +0,0 @@
# Deprecation shims for the agent tier's top-level `hyperhive.*` spelling.
#
# Per-agent options live at `services.hyperhive.agent.<name>`, beside the host
# tier's `services.hyperhive.*`. These entries keep an `agent.nix` that still
# uses the top-level `hyperhive.<name>` spelling evaluating, with a
# deprecation warning.
#
# ⚠️ One entry per LEAF, not per namespace. A single entry for a parent path
# only covers its children when the parent is a submodule-typed option; every
# nested option here (`user`, `mcp`, `otel`, …) is a plain attrset of
# declarations instead, so each leaf needs its own line — the same reason
# `../host-modules/deploy.nix` renames the CI runner's knobs one by one.
{ lib, ... }:
{
imports = [
(lib.mkRenamedOptionModule
[ "hyperhive" "_bashEnvFragments" ]
[ "services" "hyperhive" "agent" "_bashEnvFragments" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "allowedRecipients" ]
[ "services" "hyperhive" "agent" "allowedRecipients" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "autoCompact" ]
[ "services" "hyperhive" "agent" "autoCompact" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "availableModels" ]
[ "services" "hyperhive" "agent" "availableModels" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "backendEnvironmentFile" ]
[ "services" "hyperhive" "agent" "backendEnvironmentFile" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "cargo" "shortMessages" ]
[ "services" "hyperhive" "agent" "cargo" "shortMessages" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "claudeCodePath" ]
[ "services" "hyperhive" "agent" "claudeCodePath" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "claudeMarketplaces" ]
[ "services" "hyperhive" "agent" "claudeMarketplaces" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "claudePlugins" ]
[ "services" "hyperhive" "agent" "claudePlugins" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "claudePluginsAutoUpdate" ]
[ "services" "hyperhive" "agent" "claudePluginsAutoUpdate" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "dashboardLinks" ]
[ "services" "hyperhive" "agent" "dashboardLinks" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "docs" "enable" ]
[ "services" "hyperhive" "agent" "docs" "enable" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "docs" "source" ]
[ "services" "hyperhive" "agent" "docs" "source" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "effortLevel" ]
[ "services" "hyperhive" "agent" "effortLevel" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "extraMcpServers" ]
[ "services" "hyperhive" "agent" "extraMcpServers" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "extraWebProxies" ]
[ "services" "hyperhive" "agent" "extraWebProxies" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "forge" "url" ]
[ "services" "hyperhive" "agent" "forge" "url" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "frontend" "dist" ]
[ "services" "hyperhive" "agent" "frontend" "dist" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "frontend" "extraFiles" ]
[ "services" "hyperhive" "agent" "frontend" "extraFiles" ]
)
# `frontend.mergedDist` deliberately has NO entry:
# it is `readOnly` and ./frontend.nix already defines it, and a rename
# module contributes a *definition* to its target — so a shim for it makes
# every evaluation fail with "set multiple times". Nothing could have set
# it anyway; a reader of the old path gets "option does not exist",
# which says the same thing sooner. Same reasoning as the `hostUsersFile`
# exclusion in ../host-modules/deploy.nix.
(lib.mkRenamedOptionModule
[ "hyperhive" "github" "enable" ]
[ "services" "hyperhive" "agent" "github" "enable" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "gui" "enable" ]
[ "services" "hyperhive" "agent" "gui" "enable" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "gui" "vncPort" ]
[ "services" "hyperhive" "agent" "gui" "vncPort" ]
)
(lib.mkRenamedOptionModule [ "hyperhive" "icon" ] [ "services" "hyperhive" "agent" "icon" ])
(lib.mkRenamedOptionModule
[ "hyperhive" "logs" "queryUrl" ]
[ "services" "hyperhive" "agent" "logs" "queryUrl" ]
)
# `matrix.enable` is deliberately absent from this list: it has no
# replacement option, and a rename pointing at an undeclared option
# breaks evaluation for everyone. Both of its spellings are
# `mkRemovedOptionModule` in ./default.nix instead.
(lib.mkRenamedOptionModule
[ "hyperhive" "matrix" "url" ]
[ "services" "hyperhive" "agent" "matrix" "url" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "matrixAccounts" ]
[ "services" "hyperhive" "agent" "matrixAccounts" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "mcp" "bashHttpPort" ]
[ "services" "hyperhive" "agent" "mcp" "bashHttpPort" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "mcp" "httpPort" ]
[ "services" "hyperhive" "agent" "mcp" "httpPort" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "mcp" "matrixHttpPort" ]
[ "services" "hyperhive" "agent" "mcp" "matrixHttpPort" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "mcp" "subagentHttpPort" ]
[ "services" "hyperhive" "agent" "mcp" "subagentHttpPort" ]
)
(lib.mkRenamedOptionModule [ "hyperhive" "model" ] [ "services" "hyperhive" "agent" "model" ])
(lib.mkRenamedOptionModule
[ "hyperhive" "otel" "debug" ]
[ "services" "hyperhive" "agent" "otel" "debug" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "otel" "enable" ]
[ "services" "hyperhive" "agent" "otel" "enable" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "otel" "endpoint" ]
[ "services" "hyperhive" "agent" "otel" "endpoint" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "otel" "extraResourceAttributes" ]
[ "services" "hyperhive" "agent" "otel" "extraResourceAttributes" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "otel" "metricIntervalMs" ]
[ "services" "hyperhive" "agent" "otel" "metricIntervalMs" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "otel" "protocol" ]
[ "services" "hyperhive" "agent" "otel" "protocol" ]
)
(lib.mkRenamedOptionModule [ "hyperhive" "packages" ] [ "services" "hyperhive" "agent" "packages" ])
(lib.mkRenamedOptionModule
[ "hyperhive" "queue" "natsUrl" ]
[ "services" "hyperhive" "agent" "queue" "natsUrl" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "queue" "tokenEndpoint" ]
[ "services" "hyperhive" "agent" "queue" "tokenEndpoint" ]
)
# `queue.clientIdFile` / `queue.clientSecretFile` are excluded for the
# `mergedDist` reason above: both are `readOnly`, so a shim's definition
# collides with the option's own and breaks every evaluation. They are
# derived paths no agent config could have set.
(lib.mkRenamedOptionModule
[ "hyperhive" "swarmName" ]
[ "services" "hyperhive" "agent" "swarmName" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "useApiKey" ]
[ "services" "hyperhive" "agent" "useApiKey" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "user" "gid" ]
[ "services" "hyperhive" "agent" "user" "gid" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "user" "name" ]
[ "services" "hyperhive" "agent" "user" "name" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "user" "passwordlessSudo" ]
[ "services" "hyperhive" "agent" "user" "passwordlessSudo" ]
)
(lib.mkRenamedOptionModule
[ "hyperhive" "user" "uid" ]
[ "services" "hyperhive" "agent" "user" "uid" ]
)
];
}

View file

@ -212,9 +212,9 @@ let
grafana, the swarm controller, authelia, …).
- [per-agent options](agent.md) — options declared in
`nix/agent-modules/`, visible from every `agent.nix`
(`hyperhive.model`, `hyperhive.allowedRecipients`,
`hyperhive.extraMcpServers`, `hyperhive.frontend.*`,
`hyperhive.forge.*`, `hyperhive.matrix.*`, `hyperhive.gui.*`).
(`services.hyperhive.agent.model`, `services.hyperhive.agent.allowedRecipients`,
`services.hyperhive.agent.extraMcpServers`, `services.hyperhive.agent.frontend.*`,
`services.hyperhive.agent.forge.*`, `services.hyperhive.agent.matrix.*`, `services.hyperhive.agent.gui.*`).
Regenerate with `nix build .#docs` (bundle), `.#docs-host`,
`.#docs-swarm`, `.#docs-deploy`, or `.#docs-agent`. Consumed by the

View file

@ -87,7 +87,6 @@
./swarm.nix
./host-ca-removed.nix
./nats-callout-removed.nix
./swarm-peers-removed.nix
./swarm-required-services.nix
];
}

View file

@ -26,8 +26,7 @@
#
# ⚠️ The renames below are deliberately in this one file rather than
# spread across the service modules, so all of them have a single home
# and a single file to delete when the deprecation window closes — the
# shape ./swarm-peers-removed.nix already uses.
# and a single file to delete when the deprecation window closes.
{ config, lib, ... }:
let
# The swarm UI's own switch, in both spellings. Accepted and ignored: a
@ -46,7 +45,7 @@ let
lib.optional (value != null)
"services.hyperhive.${path} no longer has any effect: swarm-ui now runs wherever swarm-controller runs. Remove this setting; services.hyperhive.deploy.swarm-controller.enable decides it.";
# Shared by the two removed spellings of the status-publishing secret below.
# The removed status-publishing secret below.
statusSecretRemoved = ''
hive-c0re reads this hive's status-publishing client secret from the
swarm's secret store, under the hive's own certificate
@ -57,101 +56,11 @@ let
in
{
imports = [
# Same type, same meaning, new path — so a rename carries it exactly
# and existing configs keep evaluating with one warning naming both
# paths. Precedent: ./hive-forge/service.nix, ./hive-matrix.nix.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "grafana" "enable" ]
[ "services" "hyperhive" "deploy" "grafana" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "grafana" "datasourceUrl" ]
[ "services" "hyperhive" "deploy" "grafana" "datasourceUrl" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "grafana" "logsDatasourceUrl" ]
[ "services" "hyperhive" "deploy" "grafana" "logsDatasourceUrl" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "grafana" "plugins" ]
[ "services" "hyperhive" "deploy" "grafana" "plugins" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "victoriametrics" "enable" ]
[ "services" "hyperhive" "deploy" "victoriametrics" "enable" ]
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "victoriametrics" "port" ] ''
VictoriaMetrics listens on a unix socket in a network namespace of its
own, and the gateway reaches it there, so it binds no host port. Remove
this definition.
'')
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "victorialogs" "enable" ]
[ "services" "hyperhive" "deploy" "victorialogs" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "controller" "enable" ]
[ "services" "hyperhive" "deploy" "swarm-controller" "enable" ]
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "controller" "forgeTokenFile" ] ''
The controller reads its forge token from the swarm's secret store, under
its own certificate, and forgejo-swarm-controller-account in the forge's
container puts it there. Remove this definition; the file it named is
read by nothing now and can be deleted.
'')
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "controller" "authBridgeUrl" ]
[ "services" "hyperhive" "deploy" "swarm-controller" "authBridgeUrl" ]
)
(lib.mkRemovedOptionModule
[ "services" "hyperhive" "swarm" "controller" "queue" "clientSecretFile" ]
''
The controller reads its queue client secret from the swarm's secret
store, under its own certificate (deploy.swarm-controller.baoClientCertFile),
and swarm-secret-publish on the host that runs authelia puts it there.
Remove this definition; the file it named is read by nothing now and can
be deleted.
''
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "authelia" "enable" ]
[ "services" "hyperhive" "deploy" "authelia" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "authelia" "usersFile" ]
[ "services" "hyperhive" "deploy" "authelia" "usersFile" ]
)
# `hostClientSecretDir`, `hostUsersFile` and `grafana.socketDir`
# deliberately have NO entry here. A rename module contributes a
# *definition* to its target, and all three are `readOnly`, which refuses a
# second one — so a shim for any of them makes every evaluation fail with
# "set multiple times", naming the option's own default as the rival
# definition.
#
# The first two are derived, so nothing could ever have set them and a
# shim's only customer is a reader of the old path. `grafana.socketDir` was
# a plain settable option; only a test fixture set it in
# this tree, but a deployment outside it can have. That setter now fails
# evaluation with "option does not exist" at the old path, rather than
# being carried to the new one.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "nats" "enable" ]
[ "services" "hyperhive" "deploy" "nats" "enable" ]
)
# `tokenEndpoint` stays under `swarm.*`: it is the swarm IdP's one
# address, identical on every hive, where this is "the queue as seen from
# here".
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "statusPublish" "natsUrl" ]
[ "services" "hyperhive" "deploy" "hive-controller" "statusPublish" "natsUrl" ]
)
(lib.mkRemovedOptionModule [
"services"
"hyperhive"
"swarm"
"statusPublish"
"clientSecretFile"
] statusSecretRemoved)
(lib.mkRemovedOptionModule [
"services"
"hyperhive"
@ -160,27 +69,11 @@ in
"statusPublish"
"clientSecretFile"
] statusSecretRemoved)
# Both old spellings of "run a hive here" forward to the one switch.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "enable" ]
[ "services" "hyperhive" "deploy" "hive-controller" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "enable" ]
[ "services" "hyperhive" "deploy" "hive-controller" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "otel" "enable" ]
[ "services" "hyperhive" "deploy" "swarm-otel" "enable" ]
)
# ⚠️ Not to be confused with `services.hyperhive.otel.clientSecretFile`,
# a different option on the hive tier that is not part of this table. A
# bare `clientSecretFile` matches five distinct options across the tree.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "otel" "clientSecretFile" ]
[ "services" "hyperhive" "deploy" "swarm-otel" "clientSecretFile" ]
)
# Same type, same meaning, new path — so a rename carries it exactly
# and existing configs keep evaluating with one warning naming both
# paths.
#
# The runner moved from `deploy.forgejo.ci` to `deploy.ci-runner` with
# every option keeping its type and meaning, so the old paths forward.
(lib.mkRenamedOptionModule
@ -219,69 +112,6 @@ in
[ "services" "hyperhive" "deploy" "bao" "servicesIssuerClientKeyFile" ]
)
# The runner toggle sat under `swarm.forge.ci` before `deploy.forgejo.ci`.
# Both were per-host, so each forwards straight to its final path.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "ci" "enable" ]
[ "services" "hyperhive" "deploy" "ci-runner" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "ci" "name" ]
[ "services" "hyperhive" "deploy" "ci-runner" "name" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "ci" "concurrency" ]
[ "services" "hyperhive" "deploy" "ci-runner" "concurrency" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "ci" "labels" ]
[ "services" "hyperhive" "deploy" "ci-runner" "labels" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "ci" "package" ]
[ "services" "hyperhive" "deploy" "ci-runner" "package" ]
)
# The rest of the forge split. What stays under `swarm.forge` is what the
# forge IS from any hive's point of view — the names and ports it answers
# on, the URLs it advertises, the client id it is registered under; the
# two renamed here are what the host running it decides. Its package is
# renamed further down with the other `*.package` entries. The two removed
# here named host files that the swarm secret store now replaces.
#
# `mirrors` renames in one entry rather than one per field: it is a single
# option of a list-of-submodule type, so the rename carries its whole
# value.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "openFirewall" ]
[ "services" "hyperhive" "deploy" "forgejo" "openFirewall" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "mirrors" ]
[ "services" "hyperhive" "deploy" "forgejo" "mirrors" ]
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "sso" "clientSecretFile" ] ''
The forge's OIDC client secret is fetched from the swarm secret store
(swarm-bao-forge-oidc) into a fixed runtime path. Remove this
definition.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "hostSwarmControllerTokenFile" ]
''
The forge publishes the swarm-controller's forge token to the swarm
secret store (forgejo-swarm-controller-account), and the controller
reads it there. Remove this definition; the file it named is read by
nothing now and can be deleted.
''
)
# The homeserver, split the same way. `swarm.matrix` keeps what it IS to
# every hive — the name it answers to, the URLs it is reached on, the
# client id it is registered under. These six are what the host
# running it decides; its two packages are renamed further down with
# the other `*.package` entries. ⚠️ `enable` is NOT here: its shim lives in
# ./hive-matrix.nix, so that file is the one
# to check before assuming this table is the whole matrix story.
#
# The homeserver's port and its firewall switch, removed rather than
# renamed: tuwunel listens on a unix socket only the gateway reaches, so
# there is no host port to choose or open.
@ -291,111 +121,20 @@ in
Remove this definition. Clients and federation peers reach it at
services.hyperhive.swarm.matrix.gatewayHost.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "openFirewall" ] ''
The homeserver no longer listens on a host port, so there is no port
to open. Remove this definition. Reach from outside the host goes
through the gateway: services.hyperhive.gateway.openFirewall.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "openFirewall" ] ''
The homeserver no longer listens on a host port, so there is no port
to open. Remove this definition. Reach from outside the host goes
through the gateway: services.hyperhive.gateway.openFirewall.
'')
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "trustedServers" ]
[ "services" "hyperhive" "deploy" "matrix" "trustedServers" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "maxRequestSize" ]
[ "services" "hyperhive" "deploy" "matrix" "maxRequestSize" ]
)
# Both spellings of the registration token, removed rather than renamed:
# the homeserver does not accept a shared registration secret at all,
# so there is no path to point a definition at. Accounts are created by
# the swarm's appservice.
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "registrationTokenFile" ] ''
The matrix registration token is gone: swarm-controller creates accounts
as the swarm's appservice instead, so there is no shared secret for
tuwunel to check. Remove this definition — nothing needs replacing.
The old token file at /var/lib/hyperhive/matrix-register-token is read by
nothing now and can be deleted. See docs/integrations/matrix.md.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "registrationTokenFile" ] ''
The matrix registration token is gone: swarm-controller creates accounts
as the swarm's appservice instead, so there is no shared secret for
tuwunel to check. Remove this definition — nothing needs replacing.
The old token file at /var/lib/hyperhive/matrix-register-token is read by
nothing now and can be deleted. See docs/integrations/matrix.md.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "gui" "enable" ] ''
The matrix web client has no on/off switch: every host that runs the
homeserver serves it at services.hyperhive.swarm.matrix.gatewayHost,
and the swarm UI links to it. Remove this definition.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "gui" "enable" ] ''
The matrix web client has no on/off switch: every host that runs the
homeserver serves it at services.hyperhive.swarm.matrix.gatewayHost,
and the swarm UI links to it. Remove this definition.
'')
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "sso" "clientSecretFile" ]
[ "services" "hyperhive" "deploy" "matrix" "sso" "clientSecretFile" ]
)
# Retention is read only where the container is defined, so it is a
# decision of the host running the store rather than something the swarm
# agrees on. The two stores keep their domain and port in `swarm.*`,
# because a client hive needs those to reach them; `package` followed
# `enable` and `retentionPeriod` across in a later slice.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "victoriametrics" "retentionPeriod" ]
[ "services" "hyperhive" "deploy" "victoriametrics" "retentionPeriod" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "victorialogs" "retentionPeriod" ]
[ "services" "hyperhive" "deploy" "victorialogs" "retentionPeriod" ]
)
# The switch over all of the above. `allSwarmServices` says whether THIS
# host runs every service the swarm requires; those services are
# required of the SWARM, so the name is about the swarm rather than this
# host's wiring.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "enableRequiredServices" ]
[ "services" "hyperhive" "deploy" "allSwarmServices" ]
)
# The mode above that one lives under `deploy`, not at the TOP of
# `services.hyperhive`: that namespace is everything about hyperhive, not
# the settings of a single hive. `singleHostSwarm` names what the mode
# asserts — the whole swarm runs on this host — instead of its mechanism.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "enableAllLocalDefaults" ]
[ "services" "hyperhive" "deploy" "singleHostSwarm" ]
)
# The hive TLS state dir lives under `deploy.hive-controller` (the
# hive-c0re daemon's namespace), not at the TOP of `services.hyperhive`,
# which is everything about hyperhive rather than the settings of one hive.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "tls" "stateDir" ]
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "stateDir" ]
)
# The hive CA's lifetime and its leaves', both spellings, removed rather
# than renamed: no hive CA is generated, and the store's per-hive role
# sets the gateway leaf's lifetime.
(lib.mkRemovedOptionModule [ "services" "hyperhive" "tls" "caValidityDays" ] ''
No hive CA is generated any more: the gateway leaf is issued by the
secret store's pki mount, whose per-hive role sets its lifetime
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
definition. See docs/swarm/ca.md.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "tls" "leafValidityDays" ] ''
No hive CA is generated any more: the gateway leaf is issued by the
secret store's pki mount, whose per-hive role sets its lifetime
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
definition. See docs/swarm/ca.md.
'')
# The hive CA's lifetime and its leaves', removed rather than renamed: no
# hive CA is generated, and the store's per-hive role sets the gateway
# leaf's lifetime.
(lib.mkRemovedOptionModule
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "caValidityDays" ]
''
@ -415,98 +154,12 @@ in
''
)
# The WireGuard mesh, whole. Unlike every rename above this one covers a
# namespace rather than a toggle: nothing under it is a fact another hive
# reads. A peer needs this host's `wireguardEndpoint` — the roster entry
# in ./swarm.nix, which stays — and nothing about the interface this host
# brings up. `listenPort` follows for the same reason and is easy to read
# the other way: it is what this host *binds*, while the port a peer
# *dials* is the one inside `wireguardEndpoint`.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "wireguard" "enable" ]
[ "services" "hyperhive" "deploy" "wireguard" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "wireguard" "privateKeyFile" ]
[ "services" "hyperhive" "deploy" "wireguard" "privateKeyFile" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "wireguard" "address" ]
[ "services" "hyperhive" "deploy" "wireguard" "address" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "wireguard" "listenPort" ]
[ "services" "hyperhive" "deploy" "wireguard" "listenPort" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "wireguard" "persistentKeepalive" ]
[ "services" "hyperhive" "deploy" "wireguard" "persistentKeepalive" ]
)
# Which build a service runs is a decision of the host that runs it, so
# every `*.package` follows its `enable` across. Left in separate
# namespaces they split one client's two halves, as `gui.enable` and
# `gui.package` would.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "package" ]
[ "services" "hyperhive" "deploy" "matrix" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "gui" "package" ]
[ "services" "hyperhive" "deploy" "matrix" "gui" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "grafana" "package" ]
[ "services" "hyperhive" "deploy" "grafana" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "victorialogs" "package" ]
[ "services" "hyperhive" "deploy" "victorialogs" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "victoriametrics" "package" ]
[ "services" "hyperhive" "deploy" "victoriametrics" "package" ]
)
# With these two `services.hyperhive.swarm.ui` holds nothing at all: its
# `enable` is an ignored switch (declared below) and its `package` is
# renamed here. `deploy.swarm-ui` keeps only its ignored `enable`.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "ui" "package" ]
[ "services" "hyperhive" "deploy" "swarm-controller" "ui" "package" ]
)
# `deploy.swarm-ui` keeps only its ignored `enable` (declared below); its
# `package` is the controller's UI package.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "deploy" "swarm-ui" "package" ]
[ "services" "hyperhive" "deploy" "swarm-controller" "ui" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "nats" "authPackage" ]
[ "services" "hyperhive" "deploy" "nats" "authPackage" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "authelia" "package" ]
[ "services" "hyperhive" "deploy" "authelia" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "authelia" "bridgePackage" ]
[ "services" "hyperhive" "deploy" "authelia" "bridgePackage" ]
)
# ⚠️ `swarm-controller`, not `hive-controller` — see the note in
# ./swarm-controller.nix. Both namespaces exist, so the wrong target here
# lands on a live option in hive-c0re's tree instead of failing.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "controller" "package" ]
[ "services" "hyperhive" "deploy" "swarm-controller" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "controller" "swarmctlPackage" ]
[ "services" "hyperhive" "deploy" "swarm-controller" "swarmctlPackage" ]
)
# ⚠️ `deploy.forgejo`, not `deploy.forge` — the deploy-side name differs
# from the swarm-side one here, as it does for the controller and the UI.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "forge" "package" ]
[ "services" "hyperhive" "deploy" "forgejo" "package" ]
)
];
# ⚠️ Only `deploy.forgejo.enable` is declared here. The rest of

View file

@ -67,13 +67,13 @@ in
}
// lib.optionalAttrs (!config.services.hyperhive.github.enable) {
# GitHub integration is on by default; only signal the OFF override to
# meta.rs, which then injects `hyperhive.github.enable = false` into
# meta.rs, which then injects `services.hyperhive.agent.github.enable = false` into
# every agent. See services.hyperhive.github.enable.
HYPERHIVE_GITHUB_DISABLED = "1";
}
// lib.optionalAttrs config.services.hyperhive.otel.enable (
# Hive-wide OTEL config -> read by meta.rs::otel_config and
# injected as build-time `hyperhive.otel.*` into every agent.
# injected as build-time `services.hyperhive.agent.otel.*` into every agent.
# Endpoint presence is the enable signal on the meta side; the
# optional fields are only emitted when set so absent values
# don't render no-op env lines.
@ -172,7 +172,7 @@ in
# fallback reads as harmless because hive-c0re shares the host netns —
# but the value is handed to *agents*, which do not, so `127.0.0.1`
# there names the agent itself. An absent forward leaves
# `hyperhive.matrix.url` null and the daemon no-ops; that is the honest
# `services.hyperhive.agent.matrix.url` null and the daemon no-ops; that is the honest
# answer when there is no matrix vhost to point at.
HIVE_MATRIX_URL = "http://${config.services.hyperhive.swarm.matrix.gatewayHost}";
}

View file

@ -76,7 +76,7 @@
description = ''
URL of the narrow `docs/` source (no fragment). Inlined into the
generated meta `flake.nix` at `inputs.hyperhive-docs.url` and
threaded to each agent as `hyperhive.docs.source`, from which the
threaded to each agent as `services.hyperhive.agent.docs.source`, from which the
harness resolves `$HIVE_DOCS_DIR`. Its own store path — separate
from `hyperhiveFlake` — so a doc edit only re-locks this input
instead of rebuilding every agent container.

View file

@ -30,27 +30,6 @@ let
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
in
{
# Forge lives under `swarm`, beside the other swarm-global services. One
# rename for the top-level `services.hyperhive.forge` namespace: the
# subtree comes with it, so existing hives keep evaluating and get one
# warning naming both paths.
imports = [
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "forge" ]
[ "services" "hyperhive" "swarm" "forge" ]
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "sso" "enable" ] ''
SSO is no longer optional: the forge always registers the swarm's
authelia as a login source.
Removed rather than defaulted to true so a config that turned it
OFF fails here, where the line is, instead of silently gaining a
login provider on the next rebuild. Drop the line; if it was
false, set services.hyperhive.swarm.authelia.url as the assertion
describes.
'')
];
# Every swarm needs this forge — it's the canonical store for the meta
# flake + every agent's config repo (and the `internal/*` repos) — but
# only one host runs it: `deploy.forgejo.enable`, in ../deploy.nix. What

View file

@ -12,12 +12,6 @@ let
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "hyperhive" "gateway" "selfSignedTls" ] ''
Self-signed TLS is the implicit default whenever neither
tls.certDir nor tls.acme is configured, and there is no http-only
mode. Remove the setting; configure `tls.certDir` or `tls.acme`
to override the self-signed default.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "gateway" "auth" ] ''
The dashboard is served on services.hyperhive.swarm.dashboardDomain
behind authelia, and only accounts in authelia's `admins` group can

View file

@ -61,7 +61,7 @@ in
Client-server API base URL **hive-c0re itself** uses to
provision matrix (register agent users, create the hive space
and chat room, invite members). Distinct from the agent-facing
`hyperhive.matrix.url`, which is the gateway vhost handed to
`services.hyperhive.agent.matrix.url`, which is the gateway vhost handed to
each agent's `hive-matrix-daemon`.
Defaults to `https://` on the homeserver's gateway vhost

View file

@ -281,42 +281,12 @@ in
# settings: docs/integrations/matrix.md. Vhost map + discovery flow + tuning
# knobs: docs/networking/gateway.md.
# Matrix lives under `swarm`, beside the other swarm-global services. One
# rename for the top-level `services.hyperhive.matrix` namespace: the
# subtree comes with it, so existing hives keep evaluating and get one
# warning naming both paths.
imports = [
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "matrix" ]
[ "services" "hyperhive" "swarm" "matrix" ]
)
# "does THIS host run it" is a per-host decision, and `swarm.*` is the
# namespace every host in the swarm agrees on. What stays under
# `swarm.matrix` is the homeserver's identity as every hive sees it —
# its server name, its addresses, its federation policy.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarm" "matrix" "enable" ]
[ "services" "hyperhive" "deploy" "matrix" "enable" ]
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "sso" "enable" ] ''
SSO is no longer optional: a homeserver that runs at all delegates
login to the swarm's authelia.
Removed rather than defaulted to true so a config that turned it
OFF fails here, where the line is, instead of silently gaining a
login flow on the next rebuild. Drop the line; if it was false,
set services.hyperhive.deploy.matrix.sso.clientSecretFile and
services.hyperhive.swarm.authelia.url as the assertions describe.
'')
];
# `swarm.matrix`, in ./hive-matrix-service.nix, is what the homeserver IS from
# any hive's point of view: the name it answers to, the URLs it is
# reached on, and the client id it is registered under. What lives here is what the host running it
# decides — which build it runs, whether it is exposed, which peers it trusts,
# how large a request it accepts, and where its host-local secrets sit. Same rule as
# ./swarm-victorialogs.nix; `enable` already lives in ./deploy.nix, which also
# carries the renames.
# ./swarm-victorialogs.nix; `enable` lives in ./deploy.nix.
options.services.hyperhive.deploy.matrix = {
package = lib.mkOption {
type = lib.types.package;

View file

@ -63,19 +63,6 @@ in
# Hive-internal network — the host-side bridge every container in a
# private netns hangs off. Full design: docs/networking/network.md.
imports = [
(lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "isolateContainers" ] ''
Network isolation is the only mode and is always on whenever
the hive runs; the shared-netns path was removed. Remove
the setting.
'')
(lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "upstreamDns" ] ''
The hive resolver always follows the host's resolvers now
(dnsmasq runs on the host and reads its /etc/resolv.conf
directly). Configure upstream DNS on the host itself instead.
'')
];
options.services.hyperhive.network = {
enable = lib.mkOption {
type = lib.types.bool;

View file

@ -9,7 +9,7 @@
# a removed option fails evaluation with a message, a CA-file option naming
# one of the deleted paths fails it too, and every host deletes the files.
#
# Self-contained and deletable, like ./swarm-peers-removed.nix: one file to
# Self-contained and deletable: one file to
# remove when the deprecation window closes.
{ lib, config, ... }:
let

View file

@ -11,14 +11,7 @@ let
hiveCfg = config.services.hyperhive;
in
{
# The swarm's display name lives under `swarm`, with the other
# swarm-global settings; the hive's own name and domain are top-level,
# because they describe this hive rather than the swarm it joins.
imports = [
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "swarmName" ]
[ "services" "hyperhive" "swarm" "name" ]
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "ruthless" ] ''
hive-c0re no longer creates ruth at boot. Remove this definition, and create
ruth from the swarm dashboard like any other agent; a ruth container that
@ -205,11 +198,11 @@ in
example = false;
description = ''
Hive-wide switch for the per-agent GitHub integration (the `gh` CLI
wrapper + git credential helper, per `hyperhive.github.enable`). On by
wrapper + git credential helper, per `services.hyperhive.agent.github.enable`). On by
default: every agent gets the integration, inert until an operator links
a PAT for it in the swarm UI. Set `false` to turn it off for the whole hive --- the
meta-flake renderer (`hive-c0re/src/meta.rs`) then injects
`hyperhive.github.enable = false` into every agent. Exposed to hive-c0re
`services.hyperhive.agent.github.enable = false` into every agent. Exposed to hive-c0re
as `HYPERHIVE_GITHUB_DISABLED` (set only when the integration is off).
'';
};

View file

@ -2,7 +2,7 @@
# Set ONCE here at host level; the meta-flake renderer
# (`hive-c0re/src/meta.rs::otel_config`) reads the HYPERHIVE_OTEL_* env
# exported off hive-c0re's unit (see ./hive-c0re) and injects the matching
# `hyperhive.otel.*` build-time config into EVERY agent (mirroring the
# `services.hyperhive.agent.otel.*` build-time config into EVERY agent (mirroring the
# CA-cert injection), so each agent's harness exports its own Claude Code
# stats directly to the collector. There is no per-agent opt-in — this is
# the single switch for the whole hive.

View file

@ -1530,14 +1530,7 @@ let
storeRefreshTimer = import ./lib/store-refresh-timer.nix { };
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "bao" "matrixCtlHiveName" ] ''
swarm-controller is the only minter of the per-hive matrix sender token,
and this option named a write grant matrix-ctl no longer holds. Delete
this setting.
'')
]
++
imports =
map
(
option:

View file

@ -285,22 +285,6 @@ in
hostUnit = true;
enable = deployCfg.swarm-controller.enable;
})
# The avatar belongs to the forge-objects pass that uploads it, which
# runs in this daemon.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "orgAvatarPng" ]
[ "services" "hyperhive" "deploy" "swarm-controller" "configOrgAvatarPng" ]
)
(lib.mkRemovedOptionModule
[ "services" "hyperhive" "deploy" "swarm-controller" "queue" "clientSecretFile" ]
''
The controller reads its queue client secret from the swarm's secret
store, under its own certificate (deploy.swarm-controller.baoClientCertFile),
and swarm-secret-publish on the host that runs authelia puts it there.
Remove this definition; the file it named is read by nothing now and can
be deleted.
''
)
(lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "swarm-controller" "forgeTokenFile" ]
''
The controller reads its forge token from the swarm's secret store, under
@ -421,7 +405,7 @@ in
description = ''
Quick links to swarm-wide services, surfaced by the swarm UI's
links menu (`GET /api/links`). Same shape and same
zero-code-change-to-extend idea as `hyperhive.dashboardLinks`
zero-code-change-to-extend idea as `services.hyperhive.agent.dashboardLinks`
(`nix/agent-modules/dashboard-links.nix`), one level up: rather
than one fixed list, definitions merge with the list-merge idiom
`services.hyperhive.gateway.localNames` uses, so an operator can

View file

@ -33,15 +33,6 @@ let
};
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "otel" "journaldUnits" ] ''
The swarm collector no longer takes a unit allowlist: it ships the
journal of every system unit on the host and drops only user
sessions (records under a `user-<uid>.slice`). Delete the setting;
nothing replaces it.
'')
];
# `enable` is `services.hyperhive.deploy.swarm-otel.enable` — see ./deploy.nix.
# ⚠️ That is the SWARM collector. The per-hive one keeps its own
# `services.hyperhive.otel.enable` (./otel.nix) and is a different

View file

@ -1,67 +0,0 @@
# Migration shim for `services.hyperhive.swarm.peers`, replaced by the
# `swarm.hives` directory in ./swarm.nix.
#
# ⚠️ This is not a `mkRenamedOptionModule`. `hives` is not `peers`
# under another name, it is **`peers` ∪ {self}**: the set gains a member no
# existing config has ever written down, because a host's own identity
# lives in *other* options entirely. A rename migrates a name and a
# default can re-root a meaning; neither can conjure a new member.
#
# The whole module is self-contained and deletable — one file to remove
# when the deprecation window closes, with nothing else referring to it.
#
# Deliberately a warning rather than a hard failure, with the loudness
# coming from elsewhere: a config that set only `peers` leaves `hives`
# empty, so the self-entry assertion in ./swarm.nix fails the build
# anyway. The warning is what explains it; the assertion is what stops
# it.
{
lib,
config,
...
}:
let
peers = config.services.hyperhive.swarm.peers;
withCaCert = lib.attrNames (lib.filterAttrs (_: p: p ? caCert && p.caCert != null) peers);
in
{
options.services.hyperhive.swarm.peers = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default = { };
visible = false;
internal = true;
description = ''
Removed — use `services.hyperhive.swarm.hives` instead, which
describes every hive in the swarm including this one. Kept only so
an existing definition produces a warning that says where to move
it, rather than an "option does not exist" error that says nothing.
'';
};
config.warnings =
lib.optional (peers != { }) ''
services.hyperhive.swarm.peers is removed and ignored. Move these
entries to services.hyperhive.swarm.hives, keyed by hive NAME
rather than domain, and add an entry for this hive itself
(services.hyperhive.hiveName) — `hives` is the swarm's full
directory, identical on every host.
services.hyperhive.swarm.hives.<name> = {
domain = "<the old attrset key>";
# wireguard* carries over unchanged
};
Still set: ${lib.concatStringsSep ", " (lib.attrNames peers)}
''
++ lib.optional (withCaCert != [ ]) ''
services.hyperhive.swarm.peers.<hive>.caCert is removed and
ignored, and has no replacement in services.hyperhive.swarm.hives.
Trust inside a swarm derives from the secret store's pki root (see
docs/swarm/ca.md): every hive chains to it, so a per-hive CA is dead
weight. What this genuinely drops is trusting a hive whose root this
swarm does NOT own — another swarm's, or one keeping its own CA.
Still set on: ${lib.concatStringsSep ", " withCaCert}
'';
}

View file

@ -70,18 +70,6 @@ in
# `services.hyperhive.deploy.swarm-controller.enable` is, because it reaches
# that daemon over its unix socket. There is no `domain` option: the UI
# answers on the swarm apex, which the swarm already declares once.
imports = [
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "ui" "domain" ] ''
The swarm UI answers on services.hyperhive.swarm.domain and nothing
else. It shares that name with the swarm-controller it fronts — they
are one service to a reader and to a certificate — so a second option
only ever created two spellings of one address.
Removed rather than aliased: a config still setting it was pinning a
name the controller's own public URL never used, and silently keeping
the alias would preserve exactly that mismatch.
'')
];
# ⚠️ Neither `services.hyperhive.swarm.ui` nor `deploy.swarm-ui` is a
# namespace for anything but the old spellings: the UI's `package` lives

View file

@ -118,13 +118,6 @@ let
# never gets a gateway certificate.
swarmCfg.bao.ui.domain
];
# Hives whose entry still carries the removed `certFingerprint`. Scanned
# here, at top level, because that is the only place an assertion about a
# submodule field can actually live — see the option's own comment.
pinnedHives = lib.attrNames (
lib.filterAttrs (_: hive: hive.certFingerprint != null) swarmCfg.hives
);
in
{
options.services.hyperhive.swarm.hives = lib.mkOption {
@ -207,33 +200,6 @@ in
silently excluded from `wg-hive`).
'';
};
# Removed, and re-declared only so an existing definition
# produces an error that says *what happened*. Without it the
# module system says `The option ... does not exist`, which
# tells an operator nothing about why it went or what replaced
# it — and this field was set on real deployments.
#
# ⚠️ `lib.mkRemovedOptionModule` cannot do this job here, and
# neither of its two halves survives inside a submodule:
# its `apply = throw` fires only when the value is READ, and
# nothing reads this (that being the point of removing
# it); its `config.assertions` half would land on a submodule
# that declares no `assertions` option at all. It is a
# top-level tool. The assertion below is the working shape, and
# it is the same one ./swarm-peers-removed.nix uses for the
# analogous `peers.<hive>.caCert`.
certFingerprint = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
visible = false;
internal = true;
description = ''
Removed. Trust inside a swarm comes from the secret store's
pki root (docs/swarm/ca.md), which replaces per-hive leaf
pinning entirely.
'';
};
};
}
)
@ -388,37 +354,6 @@ in
Declared hives: ${lib.concatStringsSep ", " (lib.attrNames swarmCfg.hives)}
'';
}
{
# An error rather than a warning, deliberately. Re-declaring the
# removed option above is what stops "option does not exist"; on
# its own it would turn a config that sets the removed field into
# one that quietly evaluates with the setting ignored, which is a
# worse answer than an error. So this stays fatal until the field
# is gone from the config.
#
# Wording follows `lib.mkRemovedOptionModule`'s, so it reads like
# every other removal the module system reports.
assertion = pinnedHives == [ ];
message = ''
The option definition `services.hyperhive.swarm.hives.<hive>.certFingerprint'
no longer has any effect; please remove it.
Still set on: ${lib.concatStringsSep ", " pinnedHives}
It pinned a peer hive's TLS leaf for hive-c0re's own peer HTTPS
checks, and was removed along with the dashboard feature it
existed to serve — nothing else ever consumed it.
There is no replacement, and none is needed for a hive inside
this swarm: trust comes from the secret store's pki root (see
docs/swarm/ca.md), which every hive chains to, so one anchor
replaces per-hive pinning.
What that genuinely drops is trusting a hive whose root this
swarm does NOT own — another swarm's, or one keeping its own
CA. That is a cross-swarm problem and wants a mechanism
designed for it.
'';
}
(nameGuards.mustBeIdent {
option = "services.hyperhive.swarm.hives";
names = hiveNames;

View file

@ -37,7 +37,7 @@ let
m:
lib.any (a: !a.assertion && lib.hasInfix "deploy.builder.enable requires" a.message) m.assertions;
natsHere = shared.natsOldPath;
natsHere = shared.natsQueueOnly;
autheliaHere = hive {
deploy.bao.enable = true;

View file

@ -995,36 +995,6 @@ let
&& bare.services.dnsmasq.enable
&& bare.networking.bridges ? hive-br0;
}
{
# A host config written against either old spelling still runs its hive.
# The builder's own `true` is lowered to `mkDefault false`, so only the
# forwarded definition can turn it on.
name = "the old hive toggles forward to deploy.hive-controller.enable and warn";
ok =
let
renamedFrom =
old: m:
lib.any (
w: lib.hasInfix old w && lib.hasInfix "services.hyperhive.deploy.hive-controller.enable" w
) m.warnings;
viaOld =
extra:
hive (
extra
// {
deploy.hive-controller.enable = lib.mkDefault false;
}
);
oldEnable = viaOld { enable = true; };
oldC0re = viaOld { c0re.enable = true; };
in
oldEnable.services.hyperhive.deploy.hive-controller.enable
&& oldEnable.systemd.services ? hive-c0re
&& renamedFrom "services.hyperhive.enable" oldEnable
&& oldC0re.services.hyperhive.deploy.hive-controller.enable
&& oldC0re.systemd.services ? hive-c0re
&& renamedFrom "services.hyperhive.c0re.enable" oldC0re;
}
{
# Refused wherever something turns the name into an identifier, and only
# there. The store host is the control that the refusal is not the hive's

View file

@ -82,16 +82,6 @@ let
deploy.hive-controller.enable = false;
deploy.forgejo.ci.enable = true;
};
# The older spelling, under the swarm-level forge options.
ciUnderSwarmForge = hive {
swarm.forge.ci.enable = true;
swarm.forge.ci.name = "older-runner";
swarm.forge.ci.concurrency = 2;
swarm.forge.ci.labels = [ "nix:host" ];
swarm.forge.ci.package = pkgs.hello;
deploy.bao.ciRunnerClientCertFile = "/etc/pki/bao-ci-runner.pem";
deploy.bao.ciRunnerClientKeyFile = "/etc/pki/bao-ci-runner-key.pem";
};
runsForge = m: m.services.hyperhive.deploy.forgejo.enable && m.containers ? hive-forge;
@ -299,22 +289,6 @@ let
&& lib.any (lib.hasInfix "services.hyperhive.deploy.forgejo.ci.enable") ciUnderForge.warnings
&& !(lib.any (a: !a.assertion) ciUnderForge.assertions);
}
{
# The control is the container: it only exists if `enable` arrived.
name = "a CI option under swarm.forge sets the same option under deploy.ci-runner";
ok =
let
r = ciUnderSwarmForge.services.hyperhive.deploy.ci-runner;
in
r.enable
&& r.name == "older-runner"
&& r.concurrency == 2
&& r.labels == [ "nix:host" ]
&& r.package == pkgs.hello
&& ciUnderSwarmForge.containers ? swarm-ci-runner
&& lib.any (lib.hasInfix "services.hyperhive.swarm.forge.ci.enable") ciUnderSwarmForge.warnings
&& !(lib.any (a: !a.assertion) ciUnderSwarmForge.assertions);
}
{
# The rename carries `enable` but not the identity: an old config with
# only the old name is refused, and the message says what to set. The

View file

@ -22,12 +22,12 @@ let
runGroup
;
grafanaOldPath = hive {
grafanaHere = hive {
deploy.grafana.enable = true;
swarm.grafana.datasourceUrl = "http://127.0.0.1:19999";
swarm.grafana.logsDatasourceUrl = "http://127.0.0.1:19998";
swarm.grafana.plugins = [ ];
swarm.grafana.package = pkgs.emptyDirectory;
deploy.grafana.datasourceUrl = "http://127.0.0.1:19999";
deploy.grafana.logsDatasourceUrl = "http://127.0.0.1:19998";
deploy.grafana.plugins = [ ];
deploy.grafana.package = pkgs.emptyDirectory;
};
# A host that tries to move the socket directory; reading the option throws.
@ -120,7 +120,7 @@ let
ok =
!(builtins.tryEval grafanaSocketDirSet.services.hyperhive.deploy.grafana.socketDir).success
&&
grafanaOldPath.services.nginx.virtualHosts.${grafanaOldPath.services.hyperhive.swarm.grafana.domain}.locations."= /metrics".proxyPass
grafanaHere.services.nginx.virtualHosts.${grafanaHere.services.hyperhive.swarm.grafana.domain}.locations."= /metrics".proxyPass
== "http://unix:/run/swarm-grafana/grafana.sock:/metrics";
}
{
@ -132,8 +132,8 @@ let
name = "grafana's published vhost serves /metrics only behind the collector's auth subrequest";
ok =
let
g = grafanaOldPath.services.hyperhive.swarm.grafana;
locs = grafanaOldPath.services.nginx.virtualHosts.${g.domain}.locations;
g = grafanaHere.services.hyperhive.swarm.grafana;
locs = grafanaHere.services.nginx.virtualHosts.${g.domain}.locations;
checksOf = l: lib.filter (lib.hasPrefix "auth_request") (lib.splitString "\n" l.extraConfig);
authz = locs."= /__grafana_metrics_authz";
in
@ -143,7 +143,7 @@ let
&& !(lib.hasInfix "error_page" locs."= /metrics".extraConfig)
&& authz.proxyPass == "https://auth.t.local/api/authz/auth-request"
&& lib.hasInfix "internal;" authz.extraConfig
&& !(grafanaOldPath.services.nginx.virtualHosts ? "grafana-metrics")
&& !(grafanaHere.services.nginx.virtualHosts ? "grafana-metrics")
&& lib.hasPrefix "http://unix:" locs."/".proxyPass;
}
{
@ -152,8 +152,7 @@ let
# this host". Its vhost is on the public gateway, so a password box there
# is a way in whatever the topology.
name = "grafana disables its local login form even where authelia is not on this host";
ok =
grafanaOldPath.containers.swarm-grafana.config.services.grafana.settings.auth.disable_login_form;
ok = grafanaHere.containers.swarm-grafana.config.services.grafana.settings.auth.disable_login_form;
}
{
# The absence class this whole file is for, and the reported defect in one
@ -445,7 +444,7 @@ let
ok =
let
sources =
grafanaOldPath.containers.swarm-grafana.config.services.grafana.provision.datasources.settings.datasources;
grafanaHere.containers.swarm-grafana.config.services.grafana.provision.datasources.settings.datasources;
logs = lib.head (lib.filter (d: d.uid == "swarm-victorialogs") sources);
rules = logs.jsonData.logLevelRules or [ ];
# `overwrite_text` makes the stored text the OpenTelemetry short name
@ -473,7 +472,7 @@ let
ok =
let
groups =
grafanaOldPath.containers.swarm-grafana.config.services.grafana.provision.alerting.rules.settings.groups;
grafanaHere.containers.swarm-grafana.config.services.grafana.provision.alerting.rules.settings.groups;
rules = lib.concatMap (g: g.rules) groups;
rule = lib.findFirst (r: r.uid == "hh-agent-turn-failed") null rules;
expr = (lib.head rule.data).model.expr;
@ -517,9 +516,8 @@ let
name = "grafana's vhost is rendered with the gateway's listen set, TLS and security headers";
ok =
let
gw = grafanaOldPath.services.hyperhive.gateway;
v =
grafanaOldPath.services.nginx.virtualHosts.${grafanaOldPath.services.hyperhive.swarm.grafana.domain};
gw = grafanaHere.services.hyperhive.gateway;
v = grafanaHere.services.nginx.virtualHosts.${grafanaHere.services.hyperhive.swarm.grafana.domain};
# nginx's `listen` entries come back with defaults filled in
# (`extraParameters`, `proxyProtocol`), so compare the fields set.
listenOf = map (l: {

View file

@ -103,13 +103,7 @@ let
];
}).config;
# Note `hyperhive`, not `services.hyperhive`: the agent tier's options moved
# under `services.hyperhive.agent`, and ../agent-modules/renamed-options.nix
# keeps the top-level spelling reaching them. ⚠️ That shim covers the
# options that existed when the tier moved and nothing since, so a fixture
# for an option added afterwards has to go through [`agentWith`] and name
# the real path.
agent = extra: agentWith { hyperhive = extra; };
agent = extra: agentWith { services.hyperhive.agent = extra; };
baoNames = machine: machine.services.hyperhive.gateway.localNames;

View file

@ -22,25 +22,18 @@ let
runGroup
;
# The queue's two old `swarm.nats` paths that still rename; dropping either
# shim fails the eval. No store identity, which the first case below relies on.
natsOldPath = shared.natsOldPath;
# No store identity, which the first case below relies on.
natsQueueOnly = shared.natsQueueOnly;
# Seventh split slice, plus slice 10's two authelia packages. Of slice 7's
# movers only `usersFile` has a rename entry — the other two are `readOnly`,
# and a rename module contributes a definition, which a read-only option
# refuses; see ./host-modules/deploy.nix. `package` and `bridgePackage` are
# ordinary options, so they do carry one. The two arms
# below have different jobs. `usersFile` tests the rename; the nats one tests
# that a reader pointed at the `deploy` namespace still renders the derived
# path — seven of those reads go through an alias a path-shaped grep
# cannot see.
autheliaOldPath = hive {
# The IdP and the queue on one host. A reader of authelia's host paths has to
# follow the `deploy` namespace; seven of those reads go through an alias a
# path-shaped grep cannot see.
autheliaAndNats = hive {
deploy.authelia.enable = true;
deploy.nats.enable = true;
swarm.authelia.usersFile = "/var/lib/test-authelia/users.yml";
swarm.authelia.package = pkgs.emptyDirectory;
swarm.authelia.bridgePackage = pkgs.emptyDirectory;
deploy.authelia.usersFile = "/var/lib/test-authelia/users.yml";
deploy.authelia.package = pkgs.emptyDirectory;
deploy.authelia.bridgePackage = pkgs.emptyDirectory;
# Renders the publisher, which reads the host client-secret dir.
deploy.swarm-secret-publisher.baoClientCertFile = "/etc/pki/publisher.pem";
deploy.swarm-secret-publisher.baoClientKeyFile = "/etc/pki/publisher-key.pem";
@ -297,9 +290,9 @@ let
{
name = "authelia and nats declare no scrape target on a host with no collector";
ok =
!(autheliaOldPath.services.hyperhive.deploy.swarm-otel.enable)
&& !(autheliaOldPath.services.hyperhive.swarm.otel.scrapeTargets ? authelia)
&& !(autheliaOldPath.services.hyperhive.swarm.otel.scrapeTargets ? nats);
!(autheliaAndNats.services.hyperhive.deploy.swarm-otel.enable)
&& !(autheliaAndNats.services.hyperhive.swarm.otel.scrapeTargets ? authelia)
&& !(autheliaAndNats.services.hyperhive.swarm.otel.scrapeTargets ? nats);
}
{
# The xkey and the responder's OIDC client secret are read only from
@ -308,7 +301,7 @@ let
# minted one.
name = "a queue with no responder store identity is refused at eval, one with a leaf is not";
ok =
refusedWithoutIdentity natsOldPath
refusedWithoutIdentity natsQueueOnly
&& !(refusedWithoutIdentity natsWithStoreIdentity)
&& !(refusedWithoutIdentity natsOnStoreHost);
}
@ -327,10 +320,10 @@ let
# this key would still evaluate cleanly — the only reader that would
# notice is a publisher whose row exceeds upstream's much smaller
# default, and by then it is a dropped row, not an eval failure.
# Piggybacks on the pre-rename nats fixture above, which already
# Piggybacks on the queue-only fixture above, which already
# renders this container's full config.
name = "the queue's payload ceiling is set, not inherited from the server's default";
ok = natsOldPath.containers.swarm-nats.config.services.nats.settings.max_payload == 8388608;
ok = natsQueueOnly.containers.swarm-nats.config.services.nats.settings.max_payload == 8388608;
}
{
# What keeps a callout request inside one principal: the responder is
@ -351,7 +344,7 @@ let
&& !(settings ? system_account)
)
[
natsOldPath
natsQueueOnly
natsOnStoreHost
];
}
@ -375,7 +368,7 @@ let
&& !(c.services.nats.settings.authorization.auth_callout ? xkey)
)
[
natsOldPath
natsQueueOnly
natsOnStoreHost
];
}
@ -384,7 +377,7 @@ let
# responder with it: the responder requires the server, so a server that
# failed to start left the responder inactive rather than failed.
name = "starting the queue's server starts its responder";
ok = lib.elem "nats.service" (responderOf natsOldPath).wantedBy;
ok = lib.elem "nats.service" (responderOf natsQueueOnly).wantedBy;
}
{
# The keys are created before anything needs them, by the responder's
@ -480,7 +473,7 @@ let
ok =
let
exec =
natsOldPath.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart;
natsQueueOnly.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart;
in
!(lib.hasInfix "--agent-publish-subject " exec);
}
@ -491,7 +484,7 @@ let
name = "the responder grants a verified agent its own hive-free subjects";
ok =
let
exec = (responderOf natsOldPath).serviceConfig.ExecStart;
exec = (responderOf natsQueueOnly).serviceConfig.ExecStart;
in
lib.hasInfix "--agent-token-publish-subject '$$SWARM.term.{agent}'" exec
&& lib.hasInfix "--agent-token-publish-subject '$$SWARM.agent-state.{agent}'" exec
@ -503,7 +496,7 @@ let
name = "the responder grants a verified agent its own icon key";
ok =
let
exec = (responderOf natsOldPath).serviceConfig.ExecStart;
exec = (responderOf natsQueueOnly).serviceConfig.ExecStart;
in
lib.hasInfix "--agent-token-publish-subject '$$KV.agent-icons.{agent}'" exec;
}
@ -514,7 +507,7 @@ let
name = "a verified agent's grant is exactly its own subjects, and its JetStream API subjects read its own schedule, inference and permissions keys";
ok =
let
args = lib.splitString " " (responderOf natsOldPath).serviceConfig.ExecStart;
args = lib.splitString " " (responderOf natsQueueOnly).serviceConfig.ExecStart;
granted = lib.concatLists (
lib.imap0 (
i: a:
@ -685,7 +678,7 @@ let
# through an `autheliaCfg` alias, where a path-shaped grep does not see it.
name = "a consumer of authelia's host client-secret dir renders it from the deploy namespace";
ok =
autheliaOldPath.systemd.paths.swarm-secret-publish.pathConfig.PathChanged
autheliaAndNats.systemd.paths.swarm-secret-publish.pathConfig.PathChanged
== "/var/lib/nixos-containers/swarm-authelia/var/lib/authelia-swarm/oidc-clients";
}
{

View file

@ -55,11 +55,10 @@ in
# shape that hives elsewhere join.
swarmServicesWithController = hiveWithController { deploy.allSwarmServices = true; };
# The queue's two old `swarm.nats` paths that still rename, `enable` and
# `authPackage`, with no store identity.
natsOldPath = hive {
swarm.nats.enable = true;
swarm.nats.authPackage = pkgs.emptyDirectory;
# The queue with no store identity.
natsQueueOnly = hive {
deploy.nats.enable = true;
deploy.nats.authPackage = pkgs.emptyDirectory;
};
controllerAlone = hive { deploy.swarm-controller.enable = true; };

View file

@ -61,11 +61,6 @@ let
# authelia somewhere else, the credential delivered by hand. Whether this
# collector authenticates must follow the credential, never another
# service's placement.
#
# The `swarm.otel.clientSecretFile` below is the PRE-RENAME path. It predates
# the split and is deliberately left spelled that way: it makes this fixture
# the old-path case for that option too, so dropping its rename entry fails
# the eval here rather than only in a real operator's config.
otelRemoteAuthelia = hive {
deploy.swarm-otel.enable = true;
deploy.authelia.enable = false;
@ -74,7 +69,7 @@ let
# `token_url`, so a hive with a secret and no URL has a credential it can
# present nowhere.
swarm.authelia.url = "https://auth.example.invalid";
swarm.otel.clientSecretFile = "/var/lib/swarm-otel-oidc/by-hand.secret";
deploy.swarm-otel.clientSecretFile = "/var/lib/swarm-otel-oidc/by-hand.secret";
};
# A collector whose ONLY scrape work is published: loopback targets forced
@ -148,10 +143,8 @@ let
{
# The collector's half of the same split, and a different arm from the
# authenticator case below: this one reads the PATH the unit loads, so a
# reader left on a source that is non-null but wrong still fails. The
# fixture spells the option its pre-rename way, so it covers the rename
# entry at the same time.
name = "a config written against the pre-rename otel secret path still loads it as a credential";
# reader left on a source that is non-null but wrong still fails.
name = "a by-hand otel secret path is the one the collector loads as a credential";
ok =
lib.any (c: lib.hasInfix "/var/lib/swarm-otel-oidc/by-hand.secret" c)
otelRemoteAuthelia.containers.swarm-otel.config.systemd.services.opentelemetry-collector.serviceConfig.LoadCredential;

View file

@ -124,7 +124,7 @@ let
deploy.forgejo.enable = true;
deploy.forgejo.mirrors = [ aMirror ];
deploy.forgejo.actionsFromSelf = false;
c0re.orgAvatarPng = "/etc/marker-avatar.png";
deploy.swarm-controller.configOrgAvatarPng = "/etc/marker-avatar.png";
};
mirrorsNoController = hive {
deploy.forgejo.enable = true;
@ -796,7 +796,7 @@ let
&& lib.elem "actions/checkout" (map (m: m.dest) (mirrorsOf forgeWithController));
}
{
name = "the old c0re org-avatar option lands on the controller's unit";
name = "the org avatar lands on the controller's unit, not hive-c0re's";
ok =
controllerWithMirrors.systemd.services.swarm-controller.environment.SWARM_CONTROLLER_CONFIG_ORG_AVATAR_PNG
== "/etc/marker-avatar.png"

View file

@ -4,7 +4,7 @@
# hyperhive-authored Claude Code plugin marketplace (source tree at
# ../../claude-plugins), shipped as a standalone derivation and spliced
# into the *default* `hyperhive.claudeMarketplaces`/`claudePlugins` lists
# into the *default* `services.hyperhive.agent.claudeMarketplaces`/`claudePlugins` lists
# (nix/agent-modules/claude-settings.nix) as a local marketplace source.
# `claude plugin marketplace add` accepts a plain filesystem path (verified:
# `claude plugin marketplace add --help` → "Add a marketplace from a URL,

View file

@ -244,7 +244,7 @@ in
assets = pkgs.callPackage ./assets.nix { };
# hyperhive-authored Claude Code plugin marketplace (source tree at
# ../../claude-plugins) — a local-path marketplace spliced into the
# default `hyperhive.claudeMarketplaces`/`claudePlugins` lists. See
# default `services.hyperhive.agent.claudeMarketplaces`/`claudePlugins` lists. See
# ./claude-plugins.nix for the "why a store path, not a repo" rationale.
claude-plugins = pkgs.callPackage ./claude-plugins.nix { };
# The repo docs/ markdown tree as a standalone derivation —

View file

@ -20,7 +20,7 @@
# static/{tabs,flow,stats,stream-worker}.js{,.map}
# static/{colors,theme,common,dashboard,flow,stats}.css
# $out/agent/ the per-agent default UI (layered with
# hyperhive.frontend.extraFiles at activation time)
# services.hyperhive.agent.frontend.extraFiles at activation time)
# index.html stats.html screen.html
# static/{app,stats}.js{,.map}
# static/{colors,theme,agent}.css

View file

@ -15,7 +15,7 @@
# frontend/, prompts/, flake.lock. docs/ is deliberately dropped:
# it is shipped to agent containers as its OWN narrow meta-flake
# input (`hyperhiveDocsSource`, below), threaded through hive-c0re
# → the meta flake → `hyperhive.docs.source`. Keeping docs/ out of
# → the meta flake → `services.hyperhive.agent.docs.source`. Keeping docs/ out of
# THIS source means a doc edit only re-hashes the docs input (a
# cheap re-link), not the whole flake source (which would rebuild
# every agent container). The `reference-docs` build from `../docs`
@ -39,7 +39,7 @@
# path moves ONLY on doc edits, decoupled from `hyperhiveFlakeSource`.
# hive-c0re threads this to the meta flake as the `hyperhive-docs`
# input (same pattern as the `hyperhive` input); the harness resolves
# `$HIVE_DOCS_DIR` from it via `hyperhive.docs.source`. Evaluated
# `$HIVE_DOCS_DIR` from it via `services.hyperhive.agent.docs.source`. Evaluated
# here on the host where docs/ exists — it cannot be derived from
# inside the docs-stripped `hyperhiveFlakeSource`.
hyperhiveDocsSource = lib.cleanSourceWith {