nix: comments state current behaviour, not history
Rewrite comments and option descriptions under nix/ that narrate how the
code got here ("used to", "new name", "sat at the TOP", "no longer",
renamed/moved narration, attributions) to say what the code does now and
why. Comments and description strings only; no logic changes.
Files in open PR #4970's diff are left alone. Refs #3902
This commit is contained in:
parent
ea63b0d150
commit
1c90619fbb
45 changed files with 331 additions and 411 deletions
|
|
@ -79,10 +79,10 @@ in
|
||||||
#
|
#
|
||||||
# Both spellings, for the same reason ../host-modules/deploy.nix lists both
|
# Both spellings, for the same reason ../host-modules/deploy.nix lists both
|
||||||
# of `swarm|deploy.matrix.registrationTokenFile`: an existing agent.nix may
|
# of `swarm|deploy.matrix.registrationTokenFile`: an existing agent.nix may
|
||||||
# carry either the pre-rename `hyperhive.*` path or the current one, and only
|
# carry either the `hyperhive.*` path or the `services.hyperhive.agent.*`
|
||||||
# the one it actually carries will fire. The rename shim that used to bridge
|
# one, and only the one it actually carries will fire. Neither is an entry
|
||||||
# them is gone from ./renamed-options.nix — a rename pointing at an
|
# in ./renamed-options.nix — a rename pointing at an undeclared option
|
||||||
# undeclared option breaks evaluation for everyone, removed or not.
|
# breaks evaluation for everyone, removed or not.
|
||||||
(lib.mkRemovedOptionModule [ "hyperhive" "matrix" "enable" ] matrixEnableRemoved)
|
(lib.mkRemovedOptionModule [ "hyperhive" "matrix" "enable" ] matrixEnableRemoved)
|
||||||
(lib.mkRemovedOptionModule [ "services" "hyperhive" "agent" "matrix" "enable" ] matrixEnableRemoved)
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "agent" "matrix" "enable" ] matrixEnableRemoved)
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
let
|
let
|
||||||
userName = config.services.hyperhive.agent.user.name;
|
userName = config.services.hyperhive.agent.user.name;
|
||||||
# The token the agent fetched from the swarm secret store
|
# The token the agent fetched from the swarm secret store
|
||||||
# (./forge-token.nix), then the state-dir file the hive used to write,
|
# (./forge-token.nix), then the state-dir file the hive writes, which is
|
||||||
# which is still the only copy for an agent without a store identity.
|
# the only copy for an agent without a store identity.
|
||||||
# Both are PATHS; each reader below takes the first that holds a token.
|
# Both are PATHS; each reader below takes the first that holds a token.
|
||||||
fetchedTokenFile = config.services.hyperhive.agent.forge.tokenFile;
|
fetchedTokenFile = config.services.hyperhive.agent.forge.tokenFile;
|
||||||
stateTokenFile = "/agents/${userName}/state/forge-token";
|
stateTokenFile = "/agents/${userName}/state/forge-token";
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ let
|
||||||
# per-agent "does this agent have a homeserver to reach" fact. So an agent the
|
# per-agent "does this agent have a homeserver to reach" fact. So an agent the
|
||||||
# hive handed no homeserver URL, whose operator declared no external account
|
# hive handed no homeserver URL, whose operator declared no external account
|
||||||
# either, has an empty set here and gets none of the units — the case the
|
# either, has an empty set here and gets none of the units — the case the
|
||||||
# deleted `matrix.enable = false` used to express.
|
# "matrix off" case.
|
||||||
#
|
#
|
||||||
# No cycle: `matrixAccounts`'s own definition reads `matrix.url`, never this
|
# No cycle: `matrixAccounts`'s own definition reads `matrix.url`, never this
|
||||||
# binding, so the `mkIf`s below may read the merged option value freely.
|
# binding, so the `mkIf`s below may read the merged option value freely.
|
||||||
|
|
@ -75,9 +75,9 @@ in
|
||||||
then talks to the wrong machine. An absent integration, never a
|
then talks to the wrong machine. An absent integration, never a
|
||||||
misdirected one.
|
misdirected one.
|
||||||
|
|
||||||
`null` is also this agent's **matrix off switch**, and the
|
`null` is also this agent's **matrix off switch** — there is no
|
||||||
replacement for the `services.hyperhive.agent.matrix.enable`
|
separate `services.hyperhive.agent.matrix.enable` boolean: the
|
||||||
boolean that used to exist: the hive-internal `main` account in
|
hive-internal `main` account in
|
||||||
`services.hyperhive.agent.matrixAccounts` is declared from this
|
`services.hyperhive.agent.matrixAccounts` is declared from this
|
||||||
URL, so `null` leaves that set empty and the whole integration —
|
URL, so `null` leaves that set empty and the whole integration —
|
||||||
daemon unit, path watcher, injected MCP entry — is not generated
|
daemon unit, path watcher, injected MCP entry — is not generated
|
||||||
|
|
@ -214,12 +214,11 @@ in
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
assertions = [
|
assertions = [
|
||||||
# The "extras require `matrix.enable`" assertion that used to head this
|
# There is no "extras require `matrix.enable`" assertion: a non-empty
|
||||||
# list is gone with the option: a non-empty account set is now what
|
# account set is what enables matrix, so that condition is the
|
||||||
# enables matrix, so the condition it checked has become the definition
|
# definition of the thing it would check. An operator declaring only an
|
||||||
# of the thing it was checking. An operator declaring only an external
|
# external account, with no hive homeserver, gets a rendered config —
|
||||||
# account, with no hive homeserver, is a config this module now renders
|
# matrix on, no `main`.
|
||||||
# rather than rejects — matrix on, no `main`.
|
|
||||||
#
|
#
|
||||||
# `main` is not a forbidden key --- this module declares it
|
# `main` is not a forbidden key --- this module declares it
|
||||||
# itself (see the `matrixAccounts.main` definition below), so the
|
# itself (see the `matrixAccounts.main` definition below), so the
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
# Deprecation shims for the agent tier's old option namespace.
|
# Deprecation shims for the agent tier's top-level `hyperhive.*` spelling.
|
||||||
#
|
#
|
||||||
# Every per-agent option used to live at the top level, `hyperhive.<name>`,
|
# Per-agent options live at `services.hyperhive.agent.<name>`, beside the host
|
||||||
# while the host tier has always been under `services.hyperhive.*`. The agent
|
# tier's `services.hyperhive.*`. These entries keep an `agent.nix` that still
|
||||||
# tier moved to `services.hyperhive.agent.<name>`; these entries keep an
|
# uses the top-level `hyperhive.<name>` spelling evaluating, with a
|
||||||
# existing `agent.nix` evaluating (with a deprecation warning) against the old
|
# deprecation warning.
|
||||||
# spelling.
|
|
||||||
#
|
#
|
||||||
# ⚠️ One entry per LEAF, not per namespace. A single entry for a parent path
|
# ⚠️ 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
|
# only covers its children when the parent is a submodule-typed option; every
|
||||||
|
|
@ -119,9 +118,9 @@
|
||||||
[ "hyperhive" "logs" "queryUrl" ]
|
[ "hyperhive" "logs" "queryUrl" ]
|
||||||
[ "services" "hyperhive" "agent" "logs" "queryUrl" ]
|
[ "services" "hyperhive" "agent" "logs" "queryUrl" ]
|
||||||
)
|
)
|
||||||
# `matrix.enable` is deliberately absent from this list: the option it
|
# `matrix.enable` is deliberately absent from this list: it has no
|
||||||
# renamed to no longer exists, and a rename pointing at an undeclared
|
# replacement option, and a rename pointing at an undeclared option
|
||||||
# option breaks evaluation for everyone. Both of its spellings are
|
# breaks evaluation for everyone. Both of its spellings are
|
||||||
# `mkRemovedOptionModule` in ./default.nix instead.
|
# `mkRemovedOptionModule` in ./default.nix instead.
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "hyperhive" "matrix" "url" ]
|
[ "hyperhive" "matrix" "url" ]
|
||||||
|
|
|
||||||
|
|
@ -239,8 +239,8 @@ in
|
||||||
# every hive — the name it answers to, the ports and URLs it is reached
|
# every hive — the name it answers to, the ports and URLs it is reached
|
||||||
# on, the client id it is registered under. These six are what the host
|
# on, the client id it is registered under. These six are what the host
|
||||||
# running it decides; its two packages moved as well, further down with
|
# running it decides; its two packages moved as well, further down with
|
||||||
# the other `*.package` moves. ⚠️ `enable` is NOT here: it was renamed
|
# the other `*.package` moves. ⚠️ `enable` is NOT here: its shim lives in
|
||||||
# earlier and its shim lives in ./hive-matrix.nix, so that file is the one
|
# ./hive-matrix.nix, so that file is the one
|
||||||
# to check before assuming this table is the whole matrix story.
|
# to check before assuming this table is the whole matrix story.
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "swarm" "matrix" "openFirewall" ]
|
[ "services" "hyperhive" "swarm" "matrix" "openFirewall" ]
|
||||||
|
|
@ -255,7 +255,7 @@ in
|
||||||
[ "services" "hyperhive" "deploy" "matrix" "maxRequestSize" ]
|
[ "services" "hyperhive" "deploy" "matrix" "maxRequestSize" ]
|
||||||
)
|
)
|
||||||
# Both spellings of the registration token, removed rather than renamed:
|
# Both spellings of the registration token, removed rather than renamed:
|
||||||
# the homeserver no longer accepts a shared registration secret at all,
|
# 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
|
# so there is no path to point a definition at. Accounts are created by
|
||||||
# the swarm's appservice.
|
# the swarm's appservice.
|
||||||
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "registrationTokenFile" ] ''
|
(lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "registrationTokenFile" ] ''
|
||||||
|
|
@ -301,28 +301,27 @@ in
|
||||||
[ "services" "hyperhive" "deploy" "victorialogs" "retentionPeriod" ]
|
[ "services" "hyperhive" "deploy" "victorialogs" "retentionPeriod" ]
|
||||||
)
|
)
|
||||||
|
|
||||||
# The switch over all of the above, and the name changes with the path
|
# The switch over all of the above. `allSwarmServices` says whether THIS
|
||||||
# because the old one described the wrong subject: those services are
|
# host runs every service the swarm requires; those services are
|
||||||
# required of the SWARM, while the option says whether THIS host runs
|
# required of the SWARM, so the name is about the swarm rather than this
|
||||||
# them. `allSwarmServices` is mara's own phrasing of what it means.
|
# host's wiring.
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "swarm" "enableRequiredServices" ]
|
[ "services" "hyperhive" "swarm" "enableRequiredServices" ]
|
||||||
[ "services" "hyperhive" "deploy" "allSwarmServices" ]
|
[ "services" "hyperhive" "deploy" "allSwarmServices" ]
|
||||||
)
|
)
|
||||||
|
|
||||||
# The mode above that one. It sat at the TOP of `services.hyperhive`,
|
# The mode above that one lives under `deploy`, not at the TOP of
|
||||||
# which is the same defect one tier up: that namespace is everything
|
# `services.hyperhive`: that namespace is everything about hyperhive, not
|
||||||
# about hyperhive, not the settings of a single hive. The new name says
|
# the settings of a single hive. `singleHostSwarm` names what the mode
|
||||||
# what the mode asserts — the whole swarm runs on this host — instead of
|
# asserts — the whole swarm runs on this host — instead of its mechanism.
|
||||||
# naming its mechanism.
|
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "enableAllLocalDefaults" ]
|
[ "services" "hyperhive" "enableAllLocalDefaults" ]
|
||||||
[ "services" "hyperhive" "deploy" "singleHostSwarm" ]
|
[ "services" "hyperhive" "deploy" "singleHostSwarm" ]
|
||||||
)
|
)
|
||||||
|
|
||||||
# The hive TLS state dir sat at the TOP of `services.hyperhive`, which is
|
# The hive TLS state dir lives under `deploy.hive-controller` (the
|
||||||
# meant to be everything about hyperhive rather than the settings of one
|
# hive-c0re daemon's namespace), not at the TOP of `services.hyperhive`,
|
||||||
# hive. `hive-controller` is hive-c0re's new name (mara, on the issue).
|
# which is everything about hyperhive rather than the settings of one hive.
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "tls" "stateDir" ]
|
[ "services" "hyperhive" "tls" "stateDir" ]
|
||||||
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "stateDir" ]
|
[ "services" "hyperhive" "deploy" "hive-controller" "tls" "stateDir" ]
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ in
|
||||||
clientKeyFile = lib.mkDefault "${pkiDir}/client-key.pem";
|
clientKeyFile = lib.mkDefault "${pkiDir}/client-key.pem";
|
||||||
serverCaFile = lib.mkDefault "${pkiDir}/ca.pem";
|
serverCaFile = lib.mkDefault "${pkiDir}/ca.pem";
|
||||||
|
|
||||||
# 🩸 Readers that used to present `client.pem` above, each now pointed at
|
# 🩸 The readers below each present a leaf of their own rather than
|
||||||
# a leaf of its own. The leaf is what bao sees, so this pairing is the
|
# `client.pem` above. The leaf is what bao sees, so this pairing is the
|
||||||
# whole of what turns "one principal with the union of their grants" into
|
# whole of what turns "one principal with the union of their grants" into
|
||||||
# one principal per grant — see ./swarm-bao.nix's `perHiveReaders` block
|
# one principal per grant — see ./swarm-bao.nix's `perHiveReaders` block
|
||||||
# for the grants themselves.
|
# for the grants themselves.
|
||||||
|
|
@ -193,11 +193,11 @@ in
|
||||||
[ -s ${pkiDir}/matrix-ctl.pem ] || ${signLeaf} ${pkiDir} matrix-ctl \
|
[ -s ${pkiDir}/matrix-ctl.pem ] || ${signLeaf} ${pkiDir} matrix-ctl \
|
||||||
${lib.escapeShellArg deployCfg.bao.matrixCtlCommonName} "" clientAuth
|
${lib.escapeShellArg deployCfg.bao.matrixCtlCommonName} "" clientAuth
|
||||||
|
|
||||||
# 🩸 The readers that used to present `client.pem` above. Each carries
|
# 🩸 The readers' leaves, separate from `client.pem` above. Each carries
|
||||||
# its own subject, which is the entire mechanism: bao matches a
|
# its own subject, which is the entire mechanism: bao matches a
|
||||||
# cert-auth role on the CN, so units sharing one leaf were one principal
|
# cert-auth role on the CN, so units sharing one leaf would be one
|
||||||
# holding the union of their grants, and the union included every agent
|
# principal holding the union of their grants, and the union would
|
||||||
# credential in the swarm.
|
# include every agent credential in the swarm.
|
||||||
#
|
#
|
||||||
# ⚠️ The first carries THIS hive's name in the subject, unlike the three
|
# ⚠️ The first carries THIS hive's name in the subject, unlike the three
|
||||||
# service leaves above. Its grant names one hive's path, because a queue
|
# service leaves above. Its grant names one hive's path, because a queue
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@ let
|
||||||
# a certificate the store accepts, and that is true on the store's own host
|
# a certificate the store accepts, and that is true on the store's own host
|
||||||
# and on a hive three networks away for exactly the same reason.
|
# and on a hive three networks away for exactly the same reason.
|
||||||
#
|
#
|
||||||
# 🩸 This principal's OWN leaf, not `clientCertFile` — the hive's, which four
|
# 🩸 This principal's OWN leaf, not `clientCertFile` — the hive's. Bao
|
||||||
# units used to share. Bao matches a cert-auth role on the CN, so one leaf for
|
# matches a cert-auth role on the CN, so one leaf shared by four readers
|
||||||
# four readers was ONE principal holding the union of four grants: read on
|
# would be ONE principal holding the union of four grants: read on
|
||||||
# `swarm/agents/*` AND `swarm/hives/<hive>/*` AND `swarm/services/*`, when
|
# `swarm/agents/*` AND `swarm/hives/<hive>/*` AND `swarm/services/*`, when
|
||||||
# this unit reads one queue credential and nothing else. Its own leaf carries
|
# this unit reads one queue credential and nothing else. Its own leaf carries
|
||||||
# `<queueAgentCommonNamePrefix>-<hive>` and its role grants the single path
|
# `<queueAgentCommonNamePrefix>-<hive>` and its role grants the single path
|
||||||
|
|
@ -53,11 +53,11 @@ let
|
||||||
baoDeploy.queueAgentClientCertFile != null && baoDeploy.queueAgentClientKeyFile != null;
|
baoDeploy.queueAgentClientCertFile != null && baoDeploy.queueAgentClientKeyFile != null;
|
||||||
|
|
||||||
# Does this host read the store at all — the hive's own leaf, which is the
|
# Does this host read the store at all — the hive's own leaf, which is the
|
||||||
# one thing a remote-store deployment has always had to place by hand. Only
|
# one thing a remote-store deployment has to place by hand. Only used to
|
||||||
# used to decide whether a missing per-principal leaf is a mistake or a
|
# decide whether a missing per-principal leaf is a mistake or a deployment
|
||||||
# deployment that has no store: a host holding neither is the supported
|
# that has no store: a host holding neither is the supported no-store
|
||||||
# no-store shape, and one holding this pair but not the pair above stopped
|
# shape, and one holding this pair but not the pair above reads the store
|
||||||
# short of naming this reader's own.
|
# without a reader identity of its own.
|
||||||
hiveReaderIdentity = baoDeploy.clientCertFile != null && baoDeploy.clientKeyFile != null;
|
hiveReaderIdentity = baoDeploy.clientCertFile != null && baoDeploy.clientKeyFile != null;
|
||||||
|
|
||||||
credentialDir = toString deployCfg.hive-controller.queue.agentCredentialDir;
|
credentialDir = toString deployCfg.hive-controller.queue.agentCredentialDir;
|
||||||
|
|
|
||||||
|
|
@ -115,17 +115,14 @@ in
|
||||||
HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES = otel.extraResourceAttributes;
|
HYPERHIVE_OTEL_EXTRA_RESOURCE_ATTRIBUTES = otel.extraResourceAttributes;
|
||||||
}
|
}
|
||||||
# HYPERHIVE_OTEL_HEADERS_CREDENTIAL is deliberately NOT emitted, and
|
# HYPERHIVE_OTEL_HEADERS_CREDENTIAL is deliberately NOT emitted, and
|
||||||
# its absence is the security half of this design: it was the variable
|
# its absence is the security half of this design: that variable would
|
||||||
# that put the upstream token into an agent's own settings.json. The
|
# put the upstream token into an agent's own settings.json. Nothing
|
||||||
# delivery path it drove — an nspawn credential forwarded by
|
# delivers the token into the container — no nspawn credential, no
|
||||||
# host_config.rs, then written to an agent-readable file by
|
# agent-readable header file.
|
||||||
# claude-settings.nix's `hive-otel-header` oneshot — no longer exists
|
|
||||||
# anywhere; it was removed along with this variable's last consumer.
|
|
||||||
#
|
#
|
||||||
# Kept as a comment rather than deleted because the useful part is the
|
# The rule: the collector holding the credential achieves nothing while
|
||||||
# RULE, not the history: the collector holding the credential achieves
|
# anything else hands out a copy, so there is exactly one holder and it
|
||||||
# nothing while anything else hands out a copy, so there is exactly one
|
# is on the host.
|
||||||
# holder and it is on the host.
|
|
||||||
// lib.optionalAttrs (otel.metricIntervalMs != null) {
|
// lib.optionalAttrs (otel.metricIntervalMs != null) {
|
||||||
HYPERHIVE_OTEL_METRIC_INTERVAL_MS = toString otel.metricIntervalMs;
|
HYPERHIVE_OTEL_METRIC_INTERVAL_MS = toString otel.metricIntervalMs;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,11 +207,8 @@ in
|
||||||
|
|
||||||
# `caTrust.containerOrdering` orders this unit after `hive-tls-ca.service`
|
# `caTrust.containerOrdering` orders this unit after `hive-tls-ca.service`
|
||||||
# in self-signed mode (see the hive-ca-trust helper). Runner registration
|
# in self-signed mode (see the hive-ca-trust helper). Runner registration
|
||||||
# is no longer on the boot-critical path — hive-c0re owns it out of band —
|
# is not on the boot-critical path — hive-c0re owns it out of band — so
|
||||||
# so the container boots immediately and needs no start-timeout bump. The
|
# the container boots immediately and needs no start-timeout bump.
|
||||||
# former `TimeoutStartSec = mkForce "180s"` band-aid (which papered over a
|
|
||||||
# boot-path forge + core-token round-trip that could trip the nspawn start
|
|
||||||
# timeout into a ~60s restart loop) is gone with that move.
|
|
||||||
systemd.services."container@hive-ci" = caTrust.containerOrdering;
|
systemd.services."container@hive-ci" = caTrust.containerOrdering;
|
||||||
|
|
||||||
containers.hive-ci = {
|
containers.hive-ci = {
|
||||||
|
|
@ -415,7 +412,7 @@ in
|
||||||
# runner stays down (Restart=on-failure retries) until c0re
|
# runner stays down (Restart=on-failure retries) until c0re
|
||||||
# registers it, rather than blocking the container's start.
|
# registers it, rather than blocking the container's start.
|
||||||
#
|
#
|
||||||
# No nix-daemon wait anymore: builds route through the HOST daemon
|
# No nix-daemon wait: builds route through the HOST daemon
|
||||||
# (bound socket above), which is up before the container — nix's
|
# (bound socket above), which is up before the container — nix's
|
||||||
# own connect retries cover any brief window. `mkBefore` runs this
|
# own connect retries cover any brief window. `mkBefore` runs this
|
||||||
# ahead of any upstream pre-steps.
|
# ahead of any upstream pre-steps.
|
||||||
|
|
|
||||||
|
|
@ -142,11 +142,6 @@ in
|
||||||
matter for access from outside the host. Flip to `true` when
|
matter for access from outside the host. Flip to `true` when
|
||||||
you want the operator's browser or external git clients to
|
you want the operator's browser or external git clients to
|
||||||
hit the forge directly.
|
hit the forge directly.
|
||||||
|
|
||||||
**Breaking change**: this used to default to `true`. If you
|
|
||||||
relied on the old default for external reach, add
|
|
||||||
`services.hyperhive.deploy.forgejo.openFirewall = true;` to your
|
|
||||||
host config before rebuilding.
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -783,7 +778,7 @@ in
|
||||||
Group = "forgejo";
|
Group = "forgejo";
|
||||||
SyslogIdentifier = "forgejo-sso-source";
|
SyslogIdentifier = "forgejo-sso-source";
|
||||||
};
|
};
|
||||||
# `FORGEJO_CUSTOM` (not `GITEA_CUSTOM` — forgejo renamed it)
|
# `FORGEJO_CUSTOM` (the forgejo-prefixed name, not `GITEA_CUSTOM`)
|
||||||
# is how the CLI finds the app.ini upstream's module wrote.
|
# is how the CLI finds the app.ini upstream's module wrote.
|
||||||
#
|
#
|
||||||
# This unit is also a trust-bundle consumer (declared in the
|
# This unit is also a trust-bundle consumer (declared in the
|
||||||
|
|
@ -930,29 +925,25 @@ in
|
||||||
# re-mint instead of being silently masked by a stamp that
|
# re-mint instead of being silently masked by a stamp that
|
||||||
# outlived what it claims exists.
|
# outlived what it claims exists.
|
||||||
if [ ! -s "$token_path" ]; then
|
if [ ! -s "$token_path" ]; then
|
||||||
# `write:admin` added for `swarm-controller::forge::Client::
|
# `write:admin` is required by `swarm-controller::forge::Client::
|
||||||
# ensure_agent_user` (the `CreateForgeUser` jobq node) —
|
# ensure_agent_user` (the `CreateForgeUser` jobq node):
|
||||||
# Forgejo's `admin_create_user` HTTP endpoint refused every
|
# Forgejo's `admin_create_user` HTTP endpoint refuses a
|
||||||
# call with "token does not have at least one of required
|
# token without it ("token does not have at least one of
|
||||||
# scope(s): [write:admin]" without it, an already-deployed
|
# required scope(s): [write:admin]"). A host whose token was
|
||||||
# swarm hitting this the moment that node shipped. A host
|
# minted without this scope keeps it — the
|
||||||
# whose token was minted before this scope was added won't
|
# `[ ! -s "$token_path" ]` guard above only mints when the
|
||||||
# pick it up automatically — the `[ ! -s "$token_path" ]`
|
# file is absent, by design (see its own comment) — so such a
|
||||||
# guard above only mints when the file is absent, by design
|
# deployment needs its token file deleted to force a re-mint
|
||||||
# (see its own comment) — so an existing deployment needs
|
# with the full scope.
|
||||||
# its token file deleted to force a re-mint with the new
|
|
||||||
# scope.
|
|
||||||
#
|
#
|
||||||
# Token name carries a timestamp suffix rather than the
|
# Token name carries a timestamp suffix rather than a fixed
|
||||||
# fixed `swarm-controller-boot` this used to be — a real
|
# name: deleting the token *file* (the remediation above)
|
||||||
# incident, not a hypothetical: deleting the token *file*
|
# doesn't delete the token Forgejo already has under that
|
||||||
# (the remediation above) doesn't delete the token Forgejo
|
# name, so re-running this unit would fail with "Command
|
||||||
# already has under that name, so re-running this unit hit
|
# error: access token name has been used already". Same
|
||||||
# "Command error: access token name has been used already"
|
# approach `hive-c0re::forge::users::mint_token` uses for its
|
||||||
# and failed outright. Same fix `hive-c0re::forge::users::
|
# own agent tokens — a monotonic suffix means a re-mint can
|
||||||
# mint_token` already uses for its own agent tokens, for
|
# never collide with whatever this account already has.
|
||||||
# the identical reason — a monotonic suffix means a re-mint
|
|
||||||
# can never collide with whatever this account already has.
|
|
||||||
out=$(forgejo admin user generate-access-token \
|
out=$(forgejo admin user generate-access-token \
|
||||||
--username ${lib.escapeShellArg swarmControllerForgeUser} \
|
--username ${lib.escapeShellArg swarmControllerForgeUser} \
|
||||||
--token-name "swarm-controller-boot-$(date +%s)" \
|
--token-name "swarm-controller-boot-$(date +%s)" \
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,10 @@ let
|
||||||
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
|
effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Forge moved under `swarm` when the swarm-global services were
|
# Forge lives under `swarm`, beside the other swarm-global services. One
|
||||||
# consolidated. One rename for the namespace: the subtree comes with it,
|
# rename for the top-level `services.hyperhive.forge` namespace: the
|
||||||
# so existing hives keep evaluating and get one warning naming both paths.
|
# subtree comes with it, so existing hives keep evaluating and get one
|
||||||
|
# warning naming both paths.
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "forge" ]
|
[ "services" "hyperhive" "forge" ]
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,9 @@ let
|
||||||
# `cfg.useSelfSigned` (options.nix) is the derived single source of truth.
|
# `cfg.useSelfSigned` (options.nix) is the derived single source of truth.
|
||||||
useSelfSigned = cfg.useSelfSigned;
|
useSelfSigned = cfg.useSelfSigned;
|
||||||
|
|
||||||
# nginx's own state dir. Kept at the historical `/var/lib/hive-gateway`
|
# nginx's own state dir, fixed at `/var/lib/hive-gateway` independent of
|
||||||
# path rather than renamed with the move: it holds the imported leaf
|
# this module's own path: it holds the imported leaf across reboots, and
|
||||||
# across reboots, and renaming it would strand every existing hive's
|
# a different path would strand every existing hive's certs.
|
||||||
# certs for no gain.
|
|
||||||
tlsDir = "/var/lib/hive-gateway/tls";
|
tlsDir = "/var/lib/hive-gateway/tls";
|
||||||
# TLS cert + key paths.
|
# TLS cert + key paths.
|
||||||
# - self-signed (default): the hive leaf the store issues (the
|
# - self-signed (default): the hive leaf the store issues (the
|
||||||
|
|
@ -207,14 +206,12 @@ in
|
||||||
# before c0re has run, and pin owner + mode rather than leaving it
|
# before c0re has run, and pin owner + mode rather than leaving it
|
||||||
# to whoever creates the path first.
|
# to whoever creates the path first.
|
||||||
#
|
#
|
||||||
# ⚠️ There is deliberately NO rule for /var/lib/hyperhive here. One
|
# ⚠️ There is deliberately NO rule for /var/lib/hyperhive here.
|
||||||
# used to declare it `0755 root root` and could never win:
|
|
||||||
# `hive-c0re.service` sets `StateDirectory = "hyperhive"` with
|
# `hive-c0re.service` sets `StateDirectory = "hyperhive"` with
|
||||||
# `StateDirectoryMode = "0750"`, which systemd re-applies on every
|
# `StateDirectoryMode = "0750"`, which systemd re-applies on every
|
||||||
# start. Two mechanisms owning one path, and the loser still read as
|
# start, so a tmpfiles rule for the same path could never win: two
|
||||||
# a guarantee — it is why the resulting outage was first misdiagnosed
|
# mechanisms owning one path, the loser still reading as a guarantee.
|
||||||
# as someone having changed the mode. c0re's own unit owns that dir;
|
# c0re's own unit owns that dir; this module has no opinion about it.
|
||||||
# this module no longer has an opinion about it.
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
# The gateway's own config dir — NOT under /var/lib/hyperhive. c0re
|
# The gateway's own config dir — NOT under /var/lib/hyperhive. c0re
|
||||||
# writes here, nginx reads here, and neither needs any access to the
|
# writes here, nginx reads here, and neither needs any access to the
|
||||||
|
|
@ -393,23 +390,15 @@ in
|
||||||
# ⚠️ NO `SupplementaryGroups = [ "hive-core" ]` on nginx, and its
|
# ⚠️ NO `SupplementaryGroups = [ "hive-core" ]` on nginx, and its
|
||||||
# absence is load-bearing rather than an omission.
|
# absence is load-bearing rather than an omission.
|
||||||
#
|
#
|
||||||
# It used to be here, to let nginx traverse `/var/lib/hyperhive` and
|
# `hive-c0re.service` declares `StateDirectory = "hyperhive"` with
|
||||||
# reach the config fragments that lived inside: `hive-c0re.service`
|
# `StateDirectoryMode = "0750"` owned by `hive-core`, and systemd
|
||||||
# declares `StateDirectory = "hyperhive"` with `StateDirectoryMode =
|
# re-applies that on every c0re start. The group would let nginx
|
||||||
# "0750"` owned by `hive-core`, and systemd re-applies that on every
|
# traverse that dir, but it also gives nginx read access to everything
|
||||||
# c0re start — beating this module's own tmpfiles rule for the same
|
# ELSE group-readable under it, including the broker sqlite — i.e.
|
||||||
# path. Without the group, nginx failed its config test and never
|
# every message between every agent, reachable by the process whose
|
||||||
# started (`nginx: [emerg] open() ".../agents.conf" failed (13:
|
# entire job is parsing untrusted network input. The config fragments
|
||||||
# Permission denied)`), taking the gateway and every hive domain
|
# nginx reads live in the gateway's own dir instead, so nginx needs no
|
||||||
# behind it down.
|
# access to c0re's tree. Adding this line would reopen that exposure.
|
||||||
#
|
|
||||||
# The group fixed the symptom and paid for it: it also gave nginx
|
|
||||||
# read access to everything ELSE group-readable under that dir,
|
|
||||||
# including the broker sqlite — i.e. every message between every
|
|
||||||
# agent, reachable by the process whose entire job is parsing
|
|
||||||
# untrusted network input. Moving the fragments to the gateway's own
|
|
||||||
# dir removes the need and the exposure together. Re-adding this line
|
|
||||||
# would restore both.
|
|
||||||
|
|
||||||
networking.firewall = lib.mkIf cfg.openFirewall {
|
networking.firewall = lib.mkIf cfg.openFirewall {
|
||||||
allowedTCPPorts = [
|
allowedTCPPorts = [
|
||||||
|
|
@ -424,12 +413,11 @@ in
|
||||||
# every name a service module contributed. See `docs/networking/gateway.md`
|
# every name a service module contributed. See `docs/networking/gateway.md`
|
||||||
# ("Local dev").
|
# ("Local dev").
|
||||||
#
|
#
|
||||||
# This used to restate the per-service list a THIRD time (after the
|
# This does not restate the per-service list (the vhosts and the
|
||||||
# vhosts and the dnsmasq records), with its own copy of each
|
# dnsmasq records already carry it). It is the same question —
|
||||||
# service's guard. It is the same question — "which names does this
|
# "which names does this host answer for" — so it reads the same
|
||||||
# host answer for" — so it reads the same answer; a service added
|
# answer; a service added later lands here with no edit, and cannot
|
||||||
# later lands here with no edit, and cannot land here with a
|
# land here with a different condition than it used for DNS.
|
||||||
# different condition than it used for DNS.
|
|
||||||
networking.hosts = lib.mkIf cfg.localHostsEntry {
|
networking.hosts = lib.mkIf cfg.localHostsEntry {
|
||||||
"127.0.0.1" = lib.unique ([ hyperhiveDomain ] ++ cfg.localNames);
|
"127.0.0.1" = lib.unique ([ hyperhiveDomain ] ++ cfg.localNames);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -82,10 +82,6 @@ in
|
||||||
nginx vhost. Leave off when running behind another reverse
|
nginx vhost. Leave off when running behind another reverse
|
||||||
proxy (e.g. caddy / traefik on the host) that handles TLS
|
proxy (e.g. caddy / traefik on the host) that handles TLS
|
||||||
termination + forwards to `port`.
|
termination + forwards to `port`.
|
||||||
|
|
||||||
**Note**: this used to default to `true`. Add
|
|
||||||
`services.hyperhive.gateway.openFirewall = true;` to your host
|
|
||||||
config if external reach stopped working after a recent upgrade.
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -187,22 +187,19 @@ let
|
||||||
# Swagger UI: nginx hosts the FULL themed dist (`swaggerUiTheme` —
|
# Swagger UI: nginx hosts the FULL themed dist (`swaggerUiTheme` —
|
||||||
# vendored Swagger UI + our overlay, see nix/packages/swagger-ui-
|
# vendored Swagger UI + our overlay, see nix/packages/swagger-ui-
|
||||||
# dist.nix + swagger-ui-theme.nix) straight from the store, with NO
|
# dist.nix + swagger-ui-theme.nix) straight from the store, with NO
|
||||||
# fallback to hive-c0re at all — per the operator's shape: "core
|
# fallback to hive-c0re at all: core hosts only the JSON. Only
|
||||||
# should not need the swagger ui at all if it is hosted in gateway"
|
# `/api/openapi.json` (the live-generated spec `index.html` fetches;
|
||||||
# / "core only hosts the json". Only `/api/openapi.json`
|
# not under this prefix) proxies to c0re via "/api/" below — that's
|
||||||
# (the live-generated spec `index.html` fetches; not under this
|
# the one thing that has to stay dynamic.
|
||||||
# prefix) still proxies to c0re via "/api/" below — that's the one
|
|
||||||
# thing that has to stay dynamic.
|
|
||||||
#
|
#
|
||||||
# Prefix location, not exact-match: wins over "/api/" on plain
|
# Prefix location, not exact-match: wins over "/api/" on plain
|
||||||
# prefix length (no ordering/`=` needed), and now needs to cover
|
# prefix length (no ordering/`=` needed), and covers every file in
|
||||||
# every file in the tree (bundle.js, maps, favicons, …), not just
|
# the tree (bundle.js, maps, favicons, …), not just our 2 override
|
||||||
# our 2 override files — hive-c0re no longer serves any of this as
|
# files — hive-c0re serves none of this as a fallback.
|
||||||
# a fallback once its own `utoipa-swagger-ui` mount is removed.
|
# `= /api/docs` (no trailing slash) redirects to `/api/docs/`: nginx
|
||||||
# `= /api/docs` (no trailing slash) issues the same redirect
|
# owns that redirect because hive-c0re has no route for it, and the
|
||||||
# `utoipa-swagger-ui`'s router used to: that mount is going away
|
# H0M3 hub's own `/api/docs` link (no trailing slash) would 404
|
||||||
# too, so nginx has to own it now, or the H0M3 hub's own `/api/docs`
|
# without it.
|
||||||
# link (no trailing slash) would 404 once hive-c0re drops the route.
|
|
||||||
swaggerUiLocations = {
|
swaggerUiLocations = {
|
||||||
"= /api/docs" = {
|
"= /api/docs" = {
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
|
|
|
||||||
|
|
@ -153,12 +153,11 @@ let
|
||||||
# to host it — otherwise moving the container between hives would look
|
# to host it — otherwise moving the container between hives would look
|
||||||
# like a different homeserver.
|
# like a different homeserver.
|
||||||
#
|
#
|
||||||
# ⚠️ Changing this default is a BREAKING change in a way that moving
|
# ⚠️ Changing this default is breaking: `serverName` is baked
|
||||||
# `gatewayHost` was not: `serverName` is baked irrevocably into every
|
# irrevocably into every user and room id, so a deployment that
|
||||||
# user and room id, so a deployment that rebuilds onto a new one is a
|
# rebuilds onto a new one is a *different homeserver*. Existing hives
|
||||||
# *different homeserver*, not a renamed one. Existing hives pin the old
|
# pin their value explicitly (see the option's description); the
|
||||||
# value explicitly (see the option's description); the default is what
|
# default is what a fresh swarm gets.
|
||||||
# a fresh swarm gets.
|
|
||||||
#
|
#
|
||||||
# Total on a null swarm domain, deliberately: the required-domain
|
# Total on a null swarm domain, deliberately: the required-domain
|
||||||
# assertion in hive-network.nix is what should fire, not a coercion
|
# assertion in hive-network.nix is what should fire, not a coercion
|
||||||
|
|
@ -251,9 +250,10 @@ in
|
||||||
# settings: docs/integrations/matrix.md. Vhost map + discovery flow + tuning
|
# settings: docs/integrations/matrix.md. Vhost map + discovery flow + tuning
|
||||||
# knobs: docs/networking/gateway.md.
|
# knobs: docs/networking/gateway.md.
|
||||||
|
|
||||||
# Matrix moved under `swarm` when the swarm-global services were
|
# Matrix lives under `swarm`, beside the other swarm-global services. One
|
||||||
# consolidated. One rename for the namespace: the subtree comes with it,
|
# rename for the top-level `services.hyperhive.matrix` namespace: the
|
||||||
# so existing hives keep evaluating and get one warning naming both paths.
|
# subtree comes with it, so existing hives keep evaluating and get one
|
||||||
|
# warning naming both paths.
|
||||||
imports = [
|
imports = [
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "matrix" ]
|
[ "services" "hyperhive" "matrix" ]
|
||||||
|
|
@ -311,11 +311,6 @@ in
|
||||||
the homeserver to other hives or when an external matrix
|
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**: this used to default to `true`. If you
|
|
||||||
relied on the old default for external reach, add
|
|
||||||
`services.hyperhive.deploy.matrix.openFirewall = true;` to your host
|
|
||||||
config before rebuilding.
|
|
||||||
|
|
||||||
Note: federation (the matrix-spec well-known port 8448) is
|
Note: federation (the matrix-spec well-known port 8448) is
|
||||||
intentionally not opened here. tuwunel serves the federation
|
intentionally not opened here. tuwunel serves the federation
|
||||||
API on the same `httpPort` as the client-server API by
|
API on the same `httpPort` as the client-server API by
|
||||||
|
|
|
||||||
|
|
@ -130,9 +130,9 @@ let
|
||||||
# leaf to hold, and its absence there is the correct state rather
|
# leaf to hold, and its absence there is the correct state rather
|
||||||
# than a stale one — its own vhosts are covered by the hive leaf
|
# than a stale one — its own vhosts are covered by the hive leaf
|
||||||
# above. Anywhere else it is expected: the leaf comes from the
|
# above. Anywhere else it is expected: the leaf comes from the
|
||||||
# secret store's `pki` mount now, not from a sub-CA that exists on
|
# secret store's `pki` mount, reachable from every host rather than
|
||||||
# one host in the swarm, so "this host cannot issue it" is no longer
|
# from a sub-CA on one host in the swarm, so "this host cannot issue
|
||||||
# one of the answers.
|
# it" is not one of the answers.
|
||||||
want_svc=${if localServiceDomains == [ ] then "0" else "1"}
|
want_svc=${if localServiceDomains == [ ] then "0" else "1"}
|
||||||
|
|
||||||
# Expiry is not the only way a leaf goes wrong. One signed when the
|
# Expiry is not the only way a leaf goes wrong. One signed when the
|
||||||
|
|
@ -346,17 +346,14 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf active {
|
config = lib.mkIf active {
|
||||||
# 🚫 The "these service names are outside this hive's domain and this
|
# 🚫 There is deliberately NO warning that these service names are
|
||||||
# host cannot sign for them" warning is GONE, and its absence is the
|
# outside this hive's domain and this host cannot sign for them. The
|
||||||
# deliverable rather than a tidy-up. It fired on
|
# services leaf comes from the secret store's `pki` mount, which every
|
||||||
# `!swarm.ca.autoConfigure` — i.e. "this host does not hold the swarm
|
# hive reaches over the network with its own identity, so holding the
|
||||||
# root key" — which was the whole reason a hive could end up serving
|
# swarm root key does not decide whether a hive can serve a
|
||||||
# its own leaf on a swarm-service name. The services leaf now comes
|
# swarm-service name. Such a warning would fire on every rebuild of
|
||||||
# from the secret store's `pki` mount, which every hive reaches over
|
# every hive that is not the CA host, about a fallback that does not
|
||||||
# the network with its own identity, so holding the root key stopped
|
# happen.
|
||||||
# being the thing that decides. Left in place it would warn, on every
|
|
||||||
# rebuild of every hive that is not the CA host, about a fallback
|
|
||||||
# that no longer happens.
|
|
||||||
|
|
||||||
# The state dir and the trust bundle. Never waits on the store: the
|
# The state dir and the trust bundle. Never waits on the store: the
|
||||||
# store's container is ordered after this unit, which writes the
|
# store's container is ordered after this unit, which writes the
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,8 @@ in
|
||||||
|
|
||||||
# Where the swarm lives. Declared beside the hive's own identity
|
# Where the swarm lives. Declared beside the hive's own identity
|
||||||
# because it is what that identity is derived FROM — every hive in a
|
# because it is what that identity is derived FROM — every hive in a
|
||||||
# swarm is a sub-domain of it. Unlike the renamed options nearby, this
|
# swarm is a sub-domain of it. No alias: this option has no earlier
|
||||||
# is genuinely new: nothing moved here, so there is no alias.
|
# spelling.
|
||||||
options.services.hyperhive.swarm.domain = lib.mkOption {
|
options.services.hyperhive.swarm.domain = lib.mkOption {
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = lib.types.nullOr lib.types.str;
|
||||||
default = null;
|
default = null;
|
||||||
|
|
@ -145,7 +145,7 @@ in
|
||||||
(`services.hyperhive.deploy.hive-controller.enable`,
|
(`services.hyperhive.deploy.hive-controller.enable`,
|
||||||
`deploy.bao.enable`, `deploy.matrix.enable`). Distinct from
|
`deploy.bao.enable`, `deploy.matrix.enable`). Distinct from
|
||||||
`services.hyperhive.domain` (the machine-addressable DNS name)
|
`services.hyperhive.domain` (the machine-addressable DNS name)
|
||||||
but no longer merely cosmetic: a hive occupies
|
and not merely cosmetic: a hive occupies
|
||||||
`<hiveName>.<swarm.domain>`, so this is the label the hive is
|
`<hiveName>.<swarm.domain>`, so this is the label the hive is
|
||||||
*addressed* by as well as the one it is called. Exposed to
|
*addressed* by as well as the one it is called. Exposed to
|
||||||
agents as `HYPERHIVE_HIVE_NAME`; surfaced in the dashboard
|
agents as `HYPERHIVE_HIVE_NAME`; surfaced in the dashboard
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,8 @@ in
|
||||||
# rather than the file.
|
# rather than the file.
|
||||||
#
|
#
|
||||||
# ⛔ **Do not hand this path to a consumer that reads only ONE
|
# ⛔ **Do not hand this path to a consumer that reads only ONE
|
||||||
# certificate from it.** This comment used to offer an OIDC verifier's
|
# certificate from it** — an OIDC verifier's `issuer_ca_path` is
|
||||||
# `issuer_ca_path` as the motivating example; that is exactly what took
|
# exactly such a reader, and takes the swarm collector down. The bundle is
|
||||||
# the swarm collector down for forty minutes once. The bundle is
|
|
||||||
# `system CAs ++ hive anchors`, so the anchor is ~123rd, and a
|
# `system CAs ++ hive anchors`, so the anchor is ~123rd, and a
|
||||||
# first-certificate-only reader gets whichever public CA sorts first and
|
# first-certificate-only reader gets whichever public CA sorts first and
|
||||||
# can verify nothing of ours — with a full, valid, 125-certificate file
|
# can verify nothing of ours — with a full, valid, 125-certificate file
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ in
|
||||||
⚠️ The collector is therefore in the path of all telemetry. It runs
|
⚠️ The collector is therefore in the path of all telemetry. It runs
|
||||||
on the same host as the agents and restarts on failure, and
|
on the same host as the agents and restarts on failure, and
|
||||||
telemetry is not the control plane, so degraded telemetry is not
|
telemetry is not the control plane, so degraded telemetry is not
|
||||||
degraded operation — but the export no longer survives independently
|
degraded operation — but the export does not survive independently
|
||||||
of anything host-side
|
of anything host-side
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
@ -239,7 +239,7 @@ in
|
||||||
onward, which is one hop for the whole hive rather than one per
|
onward, which is one hop for the whole hive rather than one per
|
||||||
target.
|
target.
|
||||||
|
|
||||||
No longer empty on a default hive: this collector's own endpoint
|
Not empty on a default hive: this collector's own endpoint
|
||||||
is always in here, so the scrape receiver is emitted everywhere.
|
is always in here, so the scrape receiver is emitted everywhere.
|
||||||
The `!= { }` guards around it stay meaningful — the set can still
|
The `!= { }` guards around it stay meaningful — the set can still
|
||||||
be forced empty, and a `prometheus` receiver with nothing to
|
be forced empty, and a `prometheus` receiver with nothing to
|
||||||
|
|
|
||||||
|
|
@ -132,10 +132,10 @@ in
|
||||||
tracked as a follow-up rather than papered over.
|
tracked as a follow-up rather than papered over.
|
||||||
|
|
||||||
On by default: a swarm's hives have identities, and that is a
|
On by default: a swarm's hives have identities, and that is a
|
||||||
fact about the swarm rather than about any one host. It used to
|
fact about the swarm rather than about any one host. It does not
|
||||||
default to whether the queue ran on *this* machine, which made
|
depend on whether the queue runs on *this* machine, so the answer
|
||||||
the answer differ between two hosts of one swarm — set it false
|
is the same on every host of one swarm — set it false for a swarm
|
||||||
for a swarm whose hives authenticate to nothing.
|
whose hives authenticate to nothing.
|
||||||
|
|
||||||
The clients are inert until something authenticates with them:
|
The clients are inert until something authenticates with them:
|
||||||
each is a client id and a secret sitting on this host. What
|
each is a client id and a secret sitting on this host. What
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ let
|
||||||
# 🏷️ The id EXTENDS the hive's own (`hive-<name>-agent`) rather than taking a
|
# 🏷️ The id EXTENDS the hive's own (`hive-<name>-agent`) rather than taking a
|
||||||
# prefix of its own (`agent-<name>`), because that reads as *the agent called
|
# prefix of its own (`agent-<name>`), because that reads as *the agent called
|
||||||
# `<name>`* — which is the one thing this identity does not carry. The cost is
|
# `<name>`* — which is the one thing this identity does not carry. The cost is
|
||||||
# that the two ids are no longer distinguishable by prefix, so the responder's
|
# that the two ids are not distinguishable by prefix, so the responder's
|
||||||
# agent rule must be tried BEFORE its hive rule; `policy.rs` says so at the
|
# agent rule must be tried BEFORE its hive rule; `policy.rs` says so at the
|
||||||
# match site, and the assertion below covers the case that ordering cannot.
|
# match site, and the assertion below covers the case that ordering cannot.
|
||||||
#
|
#
|
||||||
|
|
@ -905,11 +905,11 @@ in
|
||||||
};
|
};
|
||||||
environment = {
|
environment = {
|
||||||
SWARM_AUTHELIA_BRIDGE_BIND = "127.0.0.1:${toString cfg.bridgePort}";
|
SWARM_AUTHELIA_BRIDGE_BIND = "127.0.0.1:${toString cfg.bridgePort}";
|
||||||
# ONE file, and this is it. The bridge used to carry a
|
# ONE file, and this is it. The bridge keeps no second,
|
||||||
# second, private `…-users.json` it treated as canonical
|
# private users store: a store of its own treated as canonical,
|
||||||
# while writing `users.yml` as a rendering of it — two
|
# with `users.yml` written as a rendering of it, would be two
|
||||||
# canonical stores for one physical file, which is what
|
# canonical stores for one physical file, and would make
|
||||||
# made `swarm agent create` refuse to start on a hive whose
|
# `swarm agent create` refuse to start on a hive whose
|
||||||
# `users.yml` already held users.
|
# `users.yml` already held users.
|
||||||
SWARM_AUTHELIA_BRIDGE_USERS_FILE = deployCfg.authelia.usersFile;
|
SWARM_AUTHELIA_BRIDGE_USERS_FILE = deployCfg.authelia.usersFile;
|
||||||
# The CONFIGURED authelia, not whatever is on `PATH`: the
|
# The CONFIGURED authelia, not whatever is on `PATH`: the
|
||||||
|
|
|
||||||
|
|
@ -760,22 +760,22 @@ let
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# ── the principals that used to share the hive's own leaf ──────────────────
|
# ── the principals that each hold a leaf of their own ──────────────────────
|
||||||
#
|
#
|
||||||
# 🩸 Each of these reads exactly ONE path in the store, and until this split
|
# 🩸 Each of these reads exactly ONE path in the store, and does it holding
|
||||||
# each did it holding `deploy.bao.clientCertFile` — the hive's own leaf,
|
# its own leaf rather than `deploy.bao.clientCertFile` — the hive's own leaf,
|
||||||
# whose policy (`swarm-secret-client`'s `policy::render`) granted read on
|
# whose policy (`swarm-secret-client`'s `policy::render`) grants read on
|
||||||
# `swarm/agents/*`, `swarm/hives/<hive>/*` AND `swarm/services/*`. Principals
|
# `swarm/agents/*`, `swarm/hives/<hive>/*` AND `swarm/services/*`.
|
||||||
# presenting one certificate are ONE principal to bao, so the union of what
|
# Principals presenting one certificate are ONE principal to bao, so sharing
|
||||||
# they need was the only grant expressible: the unit that fetches Grafana's
|
# the hive's leaf would leave only the union of what they need expressible:
|
||||||
# OIDC client secret could fetch every agent credential in the swarm, and the
|
# the unit that fetches Grafana's OIDC client secret could fetch every agent
|
||||||
# unit that fetches this hive's queue credential could fetch Grafana's. Least
|
# credential in the swarm, and the unit that fetches this hive's queue
|
||||||
# privilege was not misconfigured, it was unrepresentable.
|
# credential could fetch Grafana's.
|
||||||
#
|
#
|
||||||
# Each now gets its own leaf, its own cert-auth role and a policy holding the
|
# Each gets its own leaf, its own cert-auth role and a policy holding the
|
||||||
# one `secret/data/…` path that principal's script actually names — spelled to
|
# one `secret/data/…` path that principal's script actually names — spelled
|
||||||
# the LEAF rather than to a prefix, for the reason `matrixCtlPolicyText` above
|
# to the LEAF rather than to a prefix, for the reason `matrixCtlPolicyText`
|
||||||
# gives for doing the same.
|
# above gives for doing the same.
|
||||||
#
|
#
|
||||||
# ⚠️ The queue-credential reader is PER HIVE and the OIDC readers are not,
|
# ⚠️ The queue-credential reader is PER HIVE and the OIDC readers are not,
|
||||||
# and that asymmetry is the shape of the paths rather than a preference. A
|
# and that asymmetry is the shape of the paths rather than a preference. A
|
||||||
|
|
@ -784,7 +784,7 @@ let
|
||||||
# `swarm/services/<client-id>/` and a swarm registers each of those exactly
|
# `swarm/services/<client-id>/` and a swarm registers each of those exactly
|
||||||
# once. A single role for a per-hive reader would have to name `hives/*` to
|
# once. A single role for a per-hive reader would have to name `hives/*` to
|
||||||
# serve every hive — which would let one hive read another's queue
|
# serve every hive — which would let one hive read another's queue
|
||||||
# credential.
|
# credential, a reach no hive needs.
|
||||||
readStanza = path: ''
|
readStanza = path: ''
|
||||||
path "${path}" {
|
path "${path}" {
|
||||||
capabilities = ["read"]
|
capabilities = ["read"]
|
||||||
|
|
@ -1024,10 +1024,9 @@ let
|
||||||
# `tls_require_and_verify_client_cert` on the API listener to scrape THAT
|
# `tls_require_and_verify_client_cert` on the API listener to scrape THAT
|
||||||
# instead, would each trade that property away for nothing.
|
# instead, would each trade that property away for nothing.
|
||||||
#
|
#
|
||||||
# Unconditional, and that is the change this carries: the reader is now the
|
# Unconditional: the reader is the collector in this very container (below),
|
||||||
# collector in this very container (below), which renders wherever the store
|
# which renders wherever the store does. There is no host where this
|
||||||
# does. There is no longer a host where this endpoint has no reader, so there
|
# endpoint has no reader, so it is not a function of anything.
|
||||||
# is nothing left for it to be a function of.
|
|
||||||
metricsListener = {
|
metricsListener = {
|
||||||
metrics = {
|
metrics = {
|
||||||
type = "tcp";
|
type = "tcp";
|
||||||
|
|
@ -1104,22 +1103,18 @@ let
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# ⚠️ Neither the listener above nor the forwarder below has a condition of
|
# ⚠️ Neither the listener above nor the forwarder below has a condition of
|
||||||
# its own, and they lost theirs for the same reason at different times.
|
# its own.
|
||||||
#
|
#
|
||||||
# The listener used to be gated on `scrapeHere = deployCfg.swarm-otel.enable`
|
# The listener's reader is the collector in this container, which sits
|
||||||
# — "does the swarm's collector happen to run on THIS host" — because the
|
# wherever the store does. "Does the swarm's collector run on THIS host" is
|
||||||
# thing reading it was that collector, over loopback, which only works when
|
# not a question it depends on: gating it on that would leave the metrics
|
||||||
# the two land together. mara's ruling (*"move the existing scraper
|
# path silently dead on every host that does not also run swarm-otel.
|
||||||
# to the local collector"*) removes the premise rather than the symptom: the
|
|
||||||
# scraper is now in this container, so the reader is wherever the store is,
|
|
||||||
# and the metrics path stops being silently dead on every host that does not
|
|
||||||
# also run swarm-otel.
|
|
||||||
#
|
#
|
||||||
# The forwarder never had a defensible one. "Does the swarm have a collector"
|
# "Does the swarm have a collector" is not a question that can be false —
|
||||||
# is not a question that can be false (mara: "the swarm always has at least
|
# the swarm always has at least one instance of all the swarm level
|
||||||
# one instance of all the swarm level services"), so both earlier gates —
|
# services — so gating the forwarder on `otel.enable` or
|
||||||
# `otel.enable`, then `deploy.swarm-otel.enable` — asked about the wrong host
|
# `deploy.swarm-otel.enable` would ask about the wrong host and render no
|
||||||
# and rendered no forwarder wherever the answer happened to be no.
|
# forwarder wherever the answer happened to be no.
|
||||||
#
|
#
|
||||||
# The only condition that belongs on either is the one already enclosing this
|
# The only condition that belongs on either is the one already enclosing this
|
||||||
# block: the store is deployed on this host.
|
# block: the store is deployed on this host.
|
||||||
|
|
@ -2133,14 +2128,13 @@ in
|
||||||
# granter step, on the host where that step runs.
|
# granter step, on the host where that step runs.
|
||||||
environment.etc."hyperhive/bao-bootstrap-policy.hcl".source = ./bao-bootstrap-policy.hcl;
|
environment.etc."hyperhive/bao-bootstrap-policy.hcl".source = ./bao-bootstrap-policy.hcl;
|
||||||
|
|
||||||
# 🚫 No `swarm.otel.scrapeTargets.bao` entry any more, and its absence is
|
# 🚫 No `swarm.otel.scrapeTargets.bao` entry, and its absence is
|
||||||
# the deliverable rather than a tidy-up. That option is read only by the
|
# deliberate. That option is read only by the SWARM collector, over
|
||||||
# SWARM collector, over loopback, so declaring the store there meant its
|
# loopback, so a store declared there would have metrics exactly on the
|
||||||
# metrics existed exactly on the hosts that also ran swarm-otel and
|
# hosts that also run swarm-otel and nowhere else — silently, since a
|
||||||
# nowhere else — silently, since a store with no entry looks identical to
|
# store with no entry looks identical to one nothing scrapes. The scrape
|
||||||
# one nothing scrapes. The scrape moved into this container's own
|
# runs in this container's own collector (see `receivers.prometheus`
|
||||||
# collector (see `receivers.prometheus` below), which travels with the
|
# below), which travels with the store.
|
||||||
# container's own collector is confirmed.
|
|
||||||
|
|
||||||
# ⚠️ The one nginx exception to this file's header, and it is one because
|
# ⚠️ The one nginx exception to this file's header, and it is one because
|
||||||
# it never terminates. `ssl_preread` reads the SNI off the ClientHello
|
# it never terminates. `ssl_preread` reads the SNI off the ClientHello
|
||||||
|
|
@ -2898,11 +2892,11 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# 🩸 The principals that used to log in as the hive. See the block around
|
# 🩸 The principals that each log in with a leaf of their own, not as the
|
||||||
# `perHiveReaders` for what each grant is and why the first is per hive;
|
# hive. See the block around `perHiveReaders` for what each grant is and
|
||||||
# the units are identical apart from the objects they
|
# why the first is per hive; the units are identical apart from the
|
||||||
# write, so the shape lives in `readerPolicyUnit` and the difference is
|
# objects they write, so the shape lives in `readerPolicyUnit` and the
|
||||||
# visible here in one line each.
|
# difference is visible here in one line each.
|
||||||
systemd.services.swarm-bao-queue-agent-policy = readerPolicyUnit "write the per-hive queue-credential bao policies and cert-auth roles" queueAgentReaders;
|
systemd.services.swarm-bao-queue-agent-policy = readerPolicyUnit "write the per-hive queue-credential bao policies and cert-auth roles" queueAgentReaders;
|
||||||
|
|
||||||
systemd.services.swarm-bao-grafana-oidc-policy = readerPolicyUnit "write Grafana's OIDC-secret-reader bao policy and cert-auth role" grafanaOidcReaders;
|
systemd.services.swarm-bao-grafana-oidc-policy = readerPolicyUnit "write Grafana's OIDC-secret-reader bao policy and cert-auth role" grafanaOidcReaders;
|
||||||
|
|
@ -3701,31 +3695,26 @@ in
|
||||||
# this line leaves a collector that validates, starts,
|
# this line leaves a collector that validates, starts,
|
||||||
# reports healthy and forwards nothing.
|
# reports healthy and forwards nothing.
|
||||||
directory = "/var/log/journal";
|
directory = "/var/log/journal";
|
||||||
# ⚠️ `--merge` stays from when this journal was host-linked
|
# ⚠️ `--merge` is a guard: an unqualified
|
||||||
# and `journalctl --follow` (unqualified) found no entry for
|
# `journalctl --follow` over a journal with no entry for the
|
||||||
# the current boot under that tree, crash-looping the
|
# current boot (a host-linked one) crash-loops the receiver
|
||||||
# receiver every ~2s with "No journal boot entry found for
|
# every ~2s with "No journal boot entry found for the
|
||||||
# the specified boot (+0)". The journal is local now, so the
|
# specified boot (+0)". This journal is container-local, so
|
||||||
# current boot has an entry here too — kept rather than
|
# the current boot has an entry and the flag changes nothing.
|
||||||
# dropped so the first deploy changes exactly one thing;
|
|
||||||
# revisit once delivery is confirmed.
|
|
||||||
merge = true;
|
merge = true;
|
||||||
storage = "file_storage";
|
storage = "file_storage";
|
||||||
};
|
};
|
||||||
|
|
||||||
# The scrape that used to belong to the swarm collector, now
|
# The store's scrape, run by this container's own collector.
|
||||||
# one hop shorter and one host less fussy — mara:
|
|
||||||
# *"move the existing scraper to the local collector"*.
|
|
||||||
#
|
#
|
||||||
# ⚠️ `metrics_path` and `params` are STATED, and both have to
|
# ⚠️ `metrics_path` and `params` are STATED, and both have to
|
||||||
# be: openbao serves no `/metrics` at all, and
|
# be: openbao serves no `/metrics` at all, and
|
||||||
# `/v1/sys/metrics` answers JSON unless the format is asked
|
# `/v1/sys/metrics` answers JSON unless the format is asked
|
||||||
# for. A scrape of the default path 404s and a scrape without
|
# for. A scrape of the default path 404s and a scrape without
|
||||||
# the query parses as nothing — each reads as a dead exporter
|
# the query parses as nothing — each reads as a dead exporter
|
||||||
# rather than as a wrong request, which is how the previous
|
# rather than as a wrong request. The spelling matches
|
||||||
# owner of this scrape learned to spell it
|
# ./swarm-otel.nix's `loopbackScrapeConfig`, the renderer for
|
||||||
# (./swarm-otel.nix's `loopbackScrapeConfig`, still the
|
# every other service's target.
|
||||||
# renderer for every other service's target).
|
|
||||||
#
|
#
|
||||||
# No credential, unlike the swarm collector's
|
# No credential, unlike the swarm collector's
|
||||||
# `publishedScrapeTargets` jobs: the target is the loopback
|
# `publishedScrapeTargets` jobs: the target is the loopback
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,11 @@
|
||||||
# something real — nothing whose trust store is built at build time can
|
# something real — nothing whose trust store is built at build time can
|
||||||
# name it: `docs/swarm/ca.md::Distributing the root`.
|
# name it: `docs/swarm/ca.md::Distributing the root`.
|
||||||
#
|
#
|
||||||
# ⚠️ This root no longer issues the swarm's SERVICE certificates, and the
|
# ⚠️ This root does not issue the swarm's SERVICE certificates; there is no
|
||||||
# `swarm-services-ca` sub-CA that used to sit under it is gone rather than
|
# `swarm-services-ca` sub-CA under it. Those come out of the secret store's
|
||||||
# moved. Those come out of the secret store's own `pki` mount now, anchored
|
# own `pki` mount, anchored on a root generated inside it (./swarm-bao.nix),
|
||||||
# on a root generated inside it (./swarm-bao.nix), and ./hive-tls.nix asks
|
# and ./hive-tls.nix asks for them over the network instead of signing them
|
||||||
# for them over the network instead of signing them here. What is left here
|
# here. This root has one job: the anchor each hive's own CA is issued under.
|
||||||
# is one job: the anchor each hive's own CA is issued under.
|
|
||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
config,
|
config,
|
||||||
|
|
|
||||||
|
|
@ -336,8 +336,8 @@ in
|
||||||
hostUnit = true;
|
hostUnit = true;
|
||||||
enable = deployCfg.swarm-controller.enable;
|
enable = deployCfg.swarm-controller.enable;
|
||||||
})
|
})
|
||||||
# The avatar moved with the forge-objects pass that uploads it: from
|
# The avatar belongs to the forge-objects pass that uploads it, which
|
||||||
# hive-c0re's boot sweep to this daemon.
|
# runs in this daemon.
|
||||||
(lib.mkRenamedOptionModule
|
(lib.mkRenamedOptionModule
|
||||||
[ "services" "hyperhive" "c0re" "orgAvatarPng" ]
|
[ "services" "hyperhive" "c0re" "orgAvatarPng" ]
|
||||||
[ "services" "hyperhive" "deploy" "swarm-controller" "configOrgAvatarPng" ]
|
[ "services" "hyperhive" "deploy" "swarm-controller" "configOrgAvatarPng" ]
|
||||||
|
|
|
||||||
|
|
@ -215,11 +215,11 @@ let
|
||||||
# its identity yet, which is a thing to say out loud rather than to route
|
# its identity yet, which is a thing to say out loud rather than to route
|
||||||
# around by reaching into authelia's tree whenever it happens to be local.
|
# around by reaching into authelia's tree whenever it happens to be local.
|
||||||
#
|
#
|
||||||
# 🩸 Grafana's OWN leaf, not `clientCertFile` — the hive's, which four units
|
# 🩸 Grafana's OWN leaf, not `clientCertFile` — the hive's. Bao matches a
|
||||||
# used to share. Bao matches a cert-auth role on the CN, so one leaf for four
|
# cert-auth role on the CN, so one leaf shared by four readers would be ONE
|
||||||
# readers was ONE principal holding the union of four grants: this unit could
|
# principal holding the union of four grants: this unit could read every
|
||||||
# read every agent credential in the swarm and every other service's OIDC
|
# agent credential in the swarm and every other service's OIDC client
|
||||||
# client secret, when what it needs is the one path `storeSecretPath` names.
|
# secret, when what it needs is the one path `storeSecretPath` names.
|
||||||
haveClientIdentity =
|
haveClientIdentity =
|
||||||
baoDeploy.grafanaOidcClientCertFile != null && baoDeploy.grafanaOidcClientKeyFile != null;
|
baoDeploy.grafanaOidcClientCertFile != null && baoDeploy.grafanaOidcClientKeyFile != null;
|
||||||
|
|
||||||
|
|
@ -377,7 +377,7 @@ in
|
||||||
and for two different reasons. Traces and Profiles front Tempo
|
and for two different reasons. Traces and Profiles front Tempo
|
||||||
and Pyroscope, which this swarm does not run — a UI for a backend
|
and Pyroscope, which this swarm does not run — a UI for a backend
|
||||||
that does not answer. Logs Drilldown is the subtler one: this
|
that does not answer. Logs Drilldown is the subtler one: this
|
||||||
swarm *does* run a log store, so that argument no longer applies
|
swarm *does* run a log store, so that argument does not apply
|
||||||
to it, but the app is Loki-only and no configuration here changes
|
to it, but the app is Loki-only and no configuration here changes
|
||||||
that. Its volume views call `/loki/api/v1/index/volume`, which
|
that. Its volume views call `/loki/api/v1/index/volume`, which
|
||||||
VictoriaLogs does not implement and answers "unsupported path
|
VictoriaLogs does not implement and answers "unsupported path
|
||||||
|
|
@ -398,12 +398,12 @@ in
|
||||||
services.hyperhive.gateway.enable = lib.mkDefault true;
|
services.hyperhive.gateway.enable = lib.mkDefault true;
|
||||||
services.hyperhive.gateway.dns.enable = lib.mkDefault true;
|
services.hyperhive.gateway.dns.enable = lib.mkDefault true;
|
||||||
|
|
||||||
# Registering the client is NOT here any more: it has to happen on the
|
# Registering the client is NOT here: it has to happen on the host that
|
||||||
# host that runs authelia, and this whole block is gated on the host that
|
# runs authelia, and this whole block is gated on the host that runs
|
||||||
# runs Grafana. ./glue-grafana-oidc-client.nix is where it moved to.
|
# Grafana. ./glue-grafana-oidc-client.nix does it.
|
||||||
|
|
||||||
# This arm fires only where Grafana runs — this whole block is under
|
# This arm fires only where Grafana runs — this whole block is under
|
||||||
# `deploy.grafana.enable`. SSO itself needs no assertion any more:
|
# `deploy.grafana.enable`. SSO itself needs no assertion:
|
||||||
# `swarm.authelia.url` is non-nullable, so every swarm has one and the
|
# `swarm.authelia.url` is non-nullable, so every swarm has one and the
|
||||||
# OIDC block below always names a real endpoint.
|
# OIDC block below always names a real endpoint.
|
||||||
assertions = [
|
assertions = [
|
||||||
|
|
@ -511,14 +511,13 @@ in
|
||||||
# authelia's host wrote it into, and that is true whether authelia is a
|
# authelia's host wrote it into, and that is true whether authelia is a
|
||||||
# network away or in the container next door.
|
# network away or in the container next door.
|
||||||
#
|
#
|
||||||
# 🩸 A second unit here used to copy the plaintext out of authelia's host
|
# 🩸 No second unit copies the plaintext out of authelia's host tree
|
||||||
# tree wherever the two were co-located, with the OIDC block gated on
|
# where the two are co-located. The store exists so a host holds ONE
|
||||||
# either route being available. The ruling that deleted it: the store
|
# out-of-band secret — its client certificate — and reads everything else
|
||||||
# exists so a host holds ONE out-of-band secret — its client certificate —
|
# with it, so skipping the store when the producer is local would buy a
|
||||||
# and reads everything else with it, so skipping the store when the
|
# round trip and cost a second delivery path, a second way for the file
|
||||||
# producer is local buys a round trip and costs a second delivery path, a
|
# to be wrong, and a gate whose false arm is a Grafana with no login.
|
||||||
# second way for the file to be wrong, and a gate whose false arm was a
|
# Recorded in docs/swarm/secrets.md.
|
||||||
# Grafana with no login. Recorded in docs/swarm/secrets.md.
|
|
||||||
#
|
#
|
||||||
# Shaped after ./glue-queue-agent-credential.nix, because it is a store
|
# Shaped after ./glue-queue-agent-credential.nix, because it is a store
|
||||||
# reader and this is what the store's readers do: a cert login that fails
|
# reader and this is what the store's readers do: a cert login that fails
|
||||||
|
|
@ -771,18 +770,17 @@ in
|
||||||
# property this option exists for — it should not arrive only
|
# property this option exists for — it should not arrive only
|
||||||
# once a plugin happens to be listed.
|
# once a plugin happens to be listed.
|
||||||
#
|
#
|
||||||
# - 🩸 and the one this comment used to miss: it also sets
|
# - 🩸 it also sets upstream's `plugins.preinstall_disabled`
|
||||||
# upstream's `plugins.preinstall_disabled` (nixpkgs'
|
# (nixpkgs'
|
||||||
# grafana.nix defaults it to `declarativePlugins != null`,
|
# grafana.nix defaults it to `declarativePlugins != null`,
|
||||||
# saying preinstall "causes issues with declarativePlugins").
|
# saying preinstall "causes issues with declarativePlugins").
|
||||||
# Preinstall is what fetches the Drilldown apps on first
|
# Preinstall is what fetches the Drilldown apps on first
|
||||||
# start, so claiming this directory silently removes them.
|
# start, so claiming this directory silently removes them.
|
||||||
# That is why the option's default is not empty.
|
# That is why the option's default is not empty.
|
||||||
#
|
#
|
||||||
# The claim that used to sit here — "nothing is taken over on a
|
# Taking this directory over is not harmless on a hive with no
|
||||||
# hive with no plugins, manual installation is impossible anyway"
|
# plugins: that reasoning covers only plugins a PERSON installs,
|
||||||
# — reasoned only about plugins a PERSON installs. Upstream
|
# and upstream installs some itself.
|
||||||
# installs some itself, and those are the ones that went missing.
|
|
||||||
declarativePlugins = deployCfg.grafana.plugins;
|
declarativePlugins = deployCfg.grafana.plugins;
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
|
|
@ -839,9 +837,7 @@ in
|
||||||
# Not conditional on anything, and the block below is not
|
# Not conditional on anything, and the block below is not
|
||||||
# either, which is what keeps the two from ever disagreeing.
|
# either, which is what keeps the two from ever disagreeing.
|
||||||
# Whether a password box sits on a public login page cannot
|
# Whether a password box sits on a public login page cannot
|
||||||
# depend on which host happens to run authelia — and it used to,
|
# depend on which host happens to run authelia.
|
||||||
# in a module where the OIDC block was gated and this line was
|
|
||||||
# not.
|
|
||||||
auth.disable_login_form = true;
|
auth.disable_login_form = true;
|
||||||
|
|
||||||
# Emitted in every deployment, because SSO is a requirement of
|
# Emitted in every deployment, because SSO is a requirement of
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,9 @@ in
|
||||||
today) listens on, at `127.0.0.1`. Reached through the gateway at
|
today) listens on, at `127.0.0.1`. Reached through the gateway at
|
||||||
`https://''${domain}/''${producerName}/`, same shape as a hive's
|
`https://''${domain}/''${producerName}/`, same shape as a hive's
|
||||||
own receiver — a swarm-level producer may not share a host with
|
own receiver — a swarm-level producer may not share a host with
|
||||||
this collector (mara, on the swarm-controller topology: "the
|
this collector (the swarm services need not run on the same host
|
||||||
swarm services dont have to run on the same host as the swarm
|
as the swarm controller), so loopback-only reachability is not a
|
||||||
controller"), so loopback-only reachability is not a supported
|
supported shape here, as it is not for a hive.
|
||||||
shape here any more than it is for a hive.
|
|
||||||
|
|
||||||
Authenticated by `oidc/''${producerName}`, checking for the
|
Authenticated by `oidc/''${producerName}`, checking for the
|
||||||
audience `services.hyperhive.swarm.controller.queueClientId`
|
audience `services.hyperhive.swarm.controller.queueClientId`
|
||||||
|
|
|
||||||
|
|
@ -250,10 +250,10 @@ let
|
||||||
# IS asserted is narrower and lives in `assertions` below — see
|
# IS asserted is narrower and lives in `assertions` below — see
|
||||||
# `hiveReaderIdentity`.
|
# `hiveReaderIdentity`.
|
||||||
#
|
#
|
||||||
# 🩸 The collector's OWN leaf, not `clientCertFile` — the hive's, which four
|
# 🩸 The collector's OWN leaf, not `clientCertFile` — the hive's. Bao
|
||||||
# units used to share. Bao matches a cert-auth role on the CN, so one leaf for
|
# matches a cert-auth role on the CN, so one leaf shared by four readers
|
||||||
# four readers was ONE principal holding the union of four grants: this unit
|
# would be ONE principal holding the union of four grants: this unit could
|
||||||
# could read every agent credential in the swarm and Grafana's OIDC client
|
# read every agent credential in the swarm and Grafana's OIDC client
|
||||||
# secret, when what it needs is the one path `storeSecretPath` names.
|
# secret, when what it needs is the one path `storeSecretPath` names.
|
||||||
haveClientIdentity =
|
haveClientIdentity =
|
||||||
baoDeploy.otelOidcClientCertFile != null && baoDeploy.otelOidcClientKeyFile != null;
|
baoDeploy.otelOidcClientCertFile != null && baoDeploy.otelOidcClientKeyFile != null;
|
||||||
|
|
@ -396,23 +396,21 @@ in
|
||||||
# this is how it authenticates *itself* to a service published behind
|
# this is how it authenticates *itself* to a service published behind
|
||||||
# the gateway.
|
# the gateway.
|
||||||
#
|
#
|
||||||
# Registering the client is NOT here any more: it has to happen on the
|
# Registering the client is NOT here: it has to happen on the host that
|
||||||
# host that runs authelia, and this whole block is gated on the host that
|
# runs authelia, and this whole block is gated on the host that runs the
|
||||||
# runs the collector. ./glue-swarm-otel-oidc-client.nix is where it moved
|
# collector. ./glue-swarm-otel-oidc-client.nix does it, the same split
|
||||||
# to, the same split ./swarm-grafana.nix made for its own client.
|
# ./swarm-grafana.nix has for its own client.
|
||||||
|
|
||||||
# THE delivery unit — one route, in every deployment. The secret authelia
|
# THE delivery unit — one route, in every deployment. The secret authelia
|
||||||
# minted arrives out of the swarm secret store, which the publisher on
|
# minted arrives out of the swarm secret store, which the publisher on
|
||||||
# authelia's host wrote it into, whether authelia is a network away or in
|
# authelia's host wrote it into, whether authelia is a network away or in
|
||||||
# the container next door.
|
# the container next door.
|
||||||
#
|
#
|
||||||
# 🩸 A unit here used to copy the plaintext directly out of authelia's
|
# 🩸 No unit here copies the plaintext directly out of authelia's host
|
||||||
# host tree, reachable only because they share this host's network
|
# tree: that is reachable only where authelia shares this host's network
|
||||||
# namespace — which stopped working the moment authelia moved to another
|
# namespace, and breaks the moment authelia is on another host. The store
|
||||||
# host, which is the defect this whole change exists to fix. The ruling
|
# exists so a host holds ONE out-of-band secret — its client certificate —
|
||||||
# that deleted it rather than gave it a remote sibling: the store exists
|
# and reads everything else with it. Recorded in docs/swarm/secrets.md.
|
||||||
# so a host holds ONE out-of-band secret — its client certificate — and
|
|
||||||
# reads everything else with it. Recorded in docs/swarm/secrets.md.
|
|
||||||
#
|
#
|
||||||
# Shaped after ./glue-queue-agent-credential.nix: a cert login that fails
|
# Shaped after ./glue-queue-agent-credential.nix: a cert login that fails
|
||||||
# LOUDLY, since every state it fails on is one a retry fixes, then a read
|
# LOUDLY, since every state it fails on is one a retry fixes, then a read
|
||||||
|
|
@ -638,8 +636,8 @@ in
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# The blacklist now lives in a shared file, so this module no longer
|
# The blacklist lives in a shared file, so this module does not
|
||||||
# controls its contents — and a guard whose subject can be edited
|
# control its contents — and a guard whose subject can be edited
|
||||||
# elsewhere has to assert that its own case is still in there. Without
|
# elsewhere has to assert that its own case is still in there. Without
|
||||||
# this, deleting one line from `reserved-names.nix` would silently
|
# this, deleting one line from `reserved-names.nix` would silently
|
||||||
# retire the check below rather than fail anything.
|
# retire the check below rather than fail anything.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
# Migration shim for `services.hyperhive.swarm.peers`, replaced by the
|
# Migration shim for `services.hyperhive.swarm.peers`, replaced by the
|
||||||
# `swarm.hives` directory in ./swarm.nix.
|
# `swarm.hives` directory in ./swarm.nix.
|
||||||
#
|
#
|
||||||
# ⚠️ This could not be a `mkRenamedOptionModule`. `hives` is not `peers`
|
# ⚠️ This is not a `mkRenamedOptionModule`. `hives` is not `peers`
|
||||||
# under a new name, it is **`peers` ∪ {self}**: the set gains a member no
|
# 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
|
# existing config has ever written down, because a host's own identity
|
||||||
# lived in *other* options entirely. A rename migrates a name and a
|
# lives in *other* options entirely. A rename migrates a name and a
|
||||||
# default can re-root a meaning; neither can conjure a new member.
|
# default can re-root a meaning; neither can conjure a new member.
|
||||||
#
|
#
|
||||||
# The whole module is self-contained and deletable — one file to remove
|
# The whole module is self-contained and deletable — one file to remove
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,9 @@ in
|
||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
|
|
||||||
# ⚠️ `services.hyperhive.swarm.ui` no longer exists as a namespace. `enable`
|
# ⚠️ `services.hyperhive.swarm.ui` is not a namespace. Its two options,
|
||||||
# moved in an earlier slice and `package` in this one, and those were its
|
# `enable` and `package`, live under `deploy.swarm-ui`, and the `swarm.ui`
|
||||||
# only two options — so both paths survive purely as renames in ./deploy.nix.
|
# paths survive purely as renames in ./deploy.nix.
|
||||||
options.services.hyperhive.deploy.swarm-ui.package = lib.mkOption {
|
options.services.hyperhive.deploy.swarm-ui.package = lib.mkOption {
|
||||||
type = lib.types.package;
|
type = lib.types.package;
|
||||||
defaultText = lib.literalExpression "hyperhive.packages.\${system}.swarm-ui";
|
defaultText = lib.literalExpression "hyperhive.packages.\${system}.swarm-ui";
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,5 @@ in
|
||||||
# ⚠️ Like the metrics store's, that endpoint is unauthenticated — which is
|
# ⚠️ Like the metrics store's, that endpoint is unauthenticated — which is
|
||||||
# why `listenAddress` above is loopback and why nothing reaches it except
|
# why `listenAddress` above is loopback and why nothing reaches it except
|
||||||
# through the gateway, where the ingest route is authenticated. (This module
|
# through the gateway, where the ingest route is authenticated. (This module
|
||||||
# does declare a vhost; the line that used to say otherwise was already
|
# declares a vhost.)
|
||||||
# wrong before the collector started pushing through it.)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ let
|
||||||
# explain their own admission rules; the second says why they are not one
|
# explain their own admission rules; the second says why they are not one
|
||||||
# list. They are applied HERE rather than in the module that happens to
|
# list. They are applied HERE rather than in the module that happens to
|
||||||
# consume them, because a hive name composes identifiers whatever else is
|
# consume them, because a hive name composes identifiers whatever else is
|
||||||
# enabled — ./swarm-otel.nix used to hold these guards and gated them on its
|
# enabled — gating them on another module's `enable` would leave a swarm
|
||||||
# own `enable`, so a swarm without the collector had no check at all.
|
# without that module with no check at all.
|
||||||
reservedNames = import ../reserved-names.nix;
|
reservedNames = import ../reserved-names.nix;
|
||||||
reservedFragments = import ../reserved-hive-fragments.nix;
|
reservedFragments = import ../reserved-hive-fragments.nix;
|
||||||
|
|
||||||
|
|
@ -213,7 +213,7 @@ in
|
||||||
# ⚠️ `lib.mkRemovedOptionModule` cannot do this job here, and
|
# ⚠️ `lib.mkRemovedOptionModule` cannot do this job here, and
|
||||||
# neither of its two halves survives the move into a submodule:
|
# neither of its two halves survives the move into a submodule:
|
||||||
# its `apply = throw` fires only when the value is READ, and
|
# its `apply = throw` fires only when the value is READ, and
|
||||||
# nothing reads this any more (that being the point of removing
|
# nothing reads this (that being the point of removing
|
||||||
# it); its `config.assertions` half would land on a submodule
|
# it); its `config.assertions` half would land on a submodule
|
||||||
# that declares no `assertions` option at all. It is a
|
# that declares no `assertions` option at all. It is a
|
||||||
# top-level tool. The assertion below is the working shape, and
|
# top-level tool. The assertion below is the working shape, and
|
||||||
|
|
@ -316,14 +316,14 @@ in
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
# An EMPTY `hives` fires this too, deliberately: since
|
# An EMPTY `hives` fires this too, deliberately: since
|
||||||
# `swarm.domain` became required, every hive is in a swarm — a
|
# `swarm.domain` is required, every hive is in a swarm — a
|
||||||
# swarm of one is still a swarm — so a directory with no entry
|
# swarm of one is still a swarm — so a directory with no entry
|
||||||
# for this host is missing one either way. It also has to fire
|
# for this host is missing one either way. It also has to fire
|
||||||
# here, because this host's own domain is now read out of the
|
# here, because this host's own domain is now read out of the
|
||||||
# directory: without the entry `services.hyperhive.domain` is
|
# directory: without the entry `services.hyperhive.domain` is
|
||||||
# null and the generic required-domain assertion in
|
# null and the generic required-domain assertion in
|
||||||
# ./hive-network.nix would fire instead, naming an option the
|
# ./hive-network.nix would fire instead, naming an option the
|
||||||
# operator should no longer be setting.
|
# operator should not be setting.
|
||||||
#
|
#
|
||||||
# Guarded on `hiveName != null` so the required-hiveName
|
# Guarded on `hiveName != null` so the required-hiveName
|
||||||
# assertion in ./hyperhive.nix is what fires for that case —
|
# assertion in ./hyperhive.nix is what fires for that case —
|
||||||
|
|
@ -349,11 +349,10 @@ in
|
||||||
{
|
{
|
||||||
# An error rather than a warning, deliberately. Re-declaring the
|
# An error rather than a warning, deliberately. Re-declaring the
|
||||||
# removed option above is what stops "option does not exist"; on
|
# removed option above is what stops "option does not exist"; on
|
||||||
# its own it would also turn a config that used to FAIL into one
|
# its own it would turn a config that sets the removed field into
|
||||||
# that quietly evaluates with the setting ignored, which is a
|
# one that quietly evaluates with the setting ignored, which is a
|
||||||
# worse answer than the unhelpful error it replaces. The operator
|
# worse answer than an error. So this stays fatal until the field
|
||||||
# asked for a removal error, so this stays fatal until the field
|
# is gone from the config.
|
||||||
# is gone from their config.
|
|
||||||
#
|
#
|
||||||
# Wording follows `lib.mkRemovedOptionModule`'s, so it reads like
|
# Wording follows `lib.mkRemovedOptionModule`'s, so it reads like
|
||||||
# every other removal the module system reports.
|
# every other removal the module system reports.
|
||||||
|
|
@ -536,8 +535,8 @@ in
|
||||||
# swarm's token endpoint. The other two coordinates are this machine's —
|
# swarm's token endpoint. The other two coordinates are this machine's —
|
||||||
# where the queue is *as seen from here*, and where its secret sits on this
|
# where the queue is *as seen from here*, and where its secret sits on this
|
||||||
# disk — so they hang off the daemon that uses them. `hive-controller` is
|
# disk — so they hang off the daemon that uses them. `hive-controller` is
|
||||||
# hive-c0re's new name, which is why the namespace is the daemon's rather
|
# the hive-c0re daemon's namespace, hence these live there rather than
|
||||||
# than a `deploy.statusPublish` of its own.
|
# under a `deploy.statusPublish` of their own.
|
||||||
#
|
#
|
||||||
# ⚠️ The all-or-nothing assertion above now spans both namespaces. That is
|
# ⚠️ The all-or-nothing assertion above now spans both namespaces. That is
|
||||||
# one service's own options either side of the split, not a service reaching
|
# one service's own options either side of the split, not a service reaching
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ let
|
||||||
{
|
{
|
||||||
# A homeserver URL is the whole input: from it the module derives the
|
# A homeserver URL is the whole input: from it the module derives the
|
||||||
# hive-internal `main` account, and from a non-empty account set the three
|
# hive-internal `main` account, and from a non-empty account set the three
|
||||||
# things that used to hang off `matrix.enable`.
|
# matrix pieces: daemon unit, path watcher and injected MCP entry.
|
||||||
name = "an agent with a homeserver gets a main account and the matrix units";
|
name = "an agent with a homeserver gets a main account and the matrix units";
|
||||||
ok =
|
ok =
|
||||||
let
|
let
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ let
|
||||||
ok = refusedWithoutIdentity controllerNoStore && !(refusedWithoutIdentity baoControllerHere);
|
ok = refusedWithoutIdentity controllerNoStore && !(refusedWithoutIdentity baoControllerHere);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# No hive mints an agent's matrix account any more, so a controller that
|
# No hive mints an agent's matrix account, so a controller that
|
||||||
# did not know the homeserver would create every agent without one. The
|
# did not know the homeserver would create every agent without one. The
|
||||||
# default is the swarm's own `chat.` vhost, with no operator setting.
|
# default is the swarm's own `chat.` vhost, with no operator setting.
|
||||||
name = "the controller is told the swarm's homeserver by default";
|
name = "the controller is told the swarm's homeserver by default";
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ let
|
||||||
deploy.bao.enable = true;
|
deploy.bao.enable = true;
|
||||||
deploy.bao.extraListenAddresses = [ "10.0.0.1" ];
|
deploy.bao.extraListenAddresses = [ "10.0.0.1" ];
|
||||||
# Pinned, not incidental: the case counting these listeners is about the
|
# Pinned, not incidental: the case counting these listeners is about the
|
||||||
# declared API addresses. ⚠️ Since the store got its own collector, this no longer keeps the METRICS
|
# declared API addresses. ⚠️ Leaving the swarm tier off does not keep the
|
||||||
# listener out of the count — that one is a function of the store, not of
|
# METRICS listener out of the count — that one is a function of the
|
||||||
# a collector's placement — so the case below subtracts it by name rather
|
# store, not of a collector's placement — so the case below subtracts it
|
||||||
# than by leaving the swarm tier off.
|
# by name.
|
||||||
deploy.swarm-otel.enable = false;
|
deploy.swarm-otel.enable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -156,11 +156,10 @@ let
|
||||||
&& (j.params.format or [ ]) == [ "prometheus" ];
|
&& (j.params.format or [ ]) == [ "prometheus" ];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# 🎯 This move's deliverable, and it needs BOTH halves or it is not a move:
|
# 🎯 Needs BOTH halves: the job is in the store's own container, and it
|
||||||
# the job is in the store's own container, and it is no longer declared
|
# is not declared to the swarm collector. Only the second half can catch
|
||||||
# to the swarm collector. Only the second half can catch a "move" that
|
# a scrape declared in both places — two scrapers of one endpoint,
|
||||||
# left the original behind — two scrapers of one endpoint, double-counted
|
# double-counted and neither obviously wrong.
|
||||||
# and neither obviously wrong.
|
|
||||||
name = "the store's metrics are scraped by its own collector, not the swarm's";
|
name = "the store's metrics are scraped by its own collector, not the swarm's";
|
||||||
ok = baoScrapeJob baoWithCollector "bao" != null && scrapeJob baoWithCollector "bao" == null;
|
ok = baoScrapeJob baoWithCollector "bao" != null && scrapeJob baoWithCollector "bao" == null;
|
||||||
}
|
}
|
||||||
|
|
@ -206,20 +205,13 @@ let
|
||||||
j != null && !(j ? metrics_path) && !(j ? params);
|
j != null && !(j ? metrics_path) && !(j ? params);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# 🩸 This case USED to be the absence arm — "a store with no collector
|
# 🩸 The store's metrics endpoint does not depend on where the swarm
|
||||||
# beside it serves no metrics" — and this inverts it deliberately
|
# collector runs. The endpoint is unauthenticated, so it should not exist
|
||||||
# rather than deleting it, because the condition it asserted is the bug.
|
# where nothing reads it; the reader ships inside the container, so every
|
||||||
#
|
# store has one. Gating the endpoint on a collector beside the store
|
||||||
# The old reasoning was sound for the old reader: the endpoint is
|
# would render a store whose metrics go nowhere, indistinguishable from
|
||||||
# unauthenticated, so it should not exist where nothing reads it, and
|
# a store nobody had asked to scrape. The endpoint is loopback in a
|
||||||
# the only reader was a swarm collector that had to be on this host.
|
# netns whose only other occupants are the store and its collector.
|
||||||
# What that rendered on every OTHER host was a store whose metrics went
|
|
||||||
# nowhere, indistinguishably from a store nobody had asked to scrape.
|
|
||||||
#
|
|
||||||
# The reader now ships inside the container, so "no collector beside it"
|
|
||||||
# describes no host that exists. The endpoint is still not exposed to
|
|
||||||
# anything new: it is loopback in a netns whose only other occupants are
|
|
||||||
# the store and its collector.
|
|
||||||
name = "a store whose swarm collector is elsewhere still serves and scrapes its metrics";
|
name = "a store whose swarm collector is elsewhere still serves and scrapes its metrics";
|
||||||
ok =
|
ok =
|
||||||
let
|
let
|
||||||
|
|
@ -272,12 +264,11 @@ let
|
||||||
(j.merge or false) == true && j.directory == "/var/log/journal";
|
(j.merge or false) == true && j.directory == "/var/log/journal";
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# The empty-directory trap this receiver used to fall into: `host` mode
|
# The empty-directory trap: `host` mode bind-mounts a host directory
|
||||||
# bind-mounts a host directory journald never actually writes into, so
|
# journald never actually writes into, so `merge`/`directory` above
|
||||||
# `merge`/`directory` above would keep validating and starting while
|
# would keep validating and starting while the receiver reads nothing —
|
||||||
# the receiver reads nothing — silently, same as every other failure
|
# silently, same as every other failure mode here. Guards against the
|
||||||
# mode here. Guards against re-adding the exact flag this was fixed by
|
# flag that links it.
|
||||||
# dropping.
|
|
||||||
name = "the store's container does not link its journal onto the host";
|
name = "the store's container does not link its journal onto the host";
|
||||||
ok = !(lib.elem "--link-journal=host" (baoWithCollector.containers.swarm-bao.extraFlags or [ ]));
|
ok = !(lib.elem "--link-journal=host" (baoWithCollector.containers.swarm-bao.extraFlags or [ ]));
|
||||||
}
|
}
|
||||||
|
|
@ -291,7 +282,7 @@ let
|
||||||
{
|
{
|
||||||
# Both ends of the hop, because a mismatch between them is silent in both
|
# Both ends of the hop, because a mismatch between them is silent in both
|
||||||
# directions: the exporter retries into a 404 and the receiver never
|
# directions: the exporter retries into a 404 and the receiver never
|
||||||
# hears from it. Not "on one host" any more — the far end is the gateway
|
# hears from it. Not "on one host" — the far end is the gateway
|
||||||
# vhost the swarm collector serves its name on, which is the whole point
|
# vhost the swarm collector serves its name on, which is the whole point
|
||||||
# of addressing it by name rather than by a bridge address.
|
# of addressing it by name rather than by a bridge address.
|
||||||
name = "the store's forwarder exports to the swarm collector's own route";
|
name = "the store's forwarder exports to the swarm collector's own route";
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@
|
||||||
#
|
#
|
||||||
# Where the forge runs. A swarm has one, on the host with
|
# Where the forge runs. A swarm has one, on the host with
|
||||||
# `deploy.forgejo.enable`; every other hive is a client of it, and the parts
|
# `deploy.forgejo.enable`; every other hive is a client of it, and the parts
|
||||||
# of a split deployment that used to lean on every host running a forge
|
# of a split deployment that must not assume every host runs a forge
|
||||||
# (the OIDC client, the controller's token, the CI runner) still have to
|
# (the OIDC client, the controller's token, the CI runner) have to hold.
|
||||||
# hold.
|
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
|
|
|
||||||
|
|
@ -70,13 +70,11 @@ let
|
||||||
deploy.bao.grafanaOidcClientKeyFile = "/etc/pki/bao-grafana-oidc-key.pem";
|
deploy.bao.grafanaOidcClientKeyFile = "/etc/pki/bao-grafana-oidc-key.pem";
|
||||||
};
|
};
|
||||||
|
|
||||||
# A Grafana host holding no store identity. This used to be the shape the
|
# A Grafana host holding no store identity. The module refuses it with an
|
||||||
# module went QUIET on — no OIDC block, a warning, and a container whose
|
# assertion naming the two options to set, rather than going QUIET: no OIDC
|
||||||
# login form is off regardless, so no way in and nothing failed. It is kept
|
# block can render and the container's login form is off regardless, so
|
||||||
# rather than deleted because the shape is still reachable by an operator;
|
# there would be no way in and nothing would fail. Only the identity is
|
||||||
# what changed is the deliverable, from a warning nothing reads back to a
|
# missing, so an arm below can name which refusal fired.
|
||||||
# refusal naming the two options to set. Only the identity is missing, so an
|
|
||||||
# arm below can name which refusal fired.
|
|
||||||
grafanaNoIdentity = hive {
|
grafanaNoIdentity = hive {
|
||||||
deploy.grafana.enable = true;
|
deploy.grafana.enable = true;
|
||||||
deploy.grafana.plugins = [ ];
|
deploy.grafana.plugins = [ ];
|
||||||
|
|
@ -104,9 +102,9 @@ let
|
||||||
) m.assertions;
|
) m.assertions;
|
||||||
cases = [
|
cases = [
|
||||||
{
|
{
|
||||||
# This fixture enables grafana and NOT authelia, which is the shape the
|
# This fixture enables grafana and NOT authelia, so the login-form toggle
|
||||||
# login form used to stay enabled in: the toggle read "both services are
|
# has to follow "grafana requires SSO" rather than "both services are on
|
||||||
# on this host" rather than "grafana requires SSO". Grafana ships an
|
# this host". Grafana ships an
|
||||||
# `admin`/`admin` account and its vhost is on the public gateway, so a
|
# `admin`/`admin` account and its vhost is on the public gateway, so a
|
||||||
# password box there is a way in whatever the topology.
|
# 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";
|
name = "grafana disables its local login form even where authelia is not on this host";
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ let
|
||||||
|
|
||||||
# The hive-name guards, with the collector explicitly OFF. That is the whole
|
# The hive-name guards, with the collector explicitly OFF. That is the whole
|
||||||
# property: the guards live where `swarm.hives` is declared, so they run in a
|
# property: the guards live where `swarm.hives` is declared, so they run in a
|
||||||
# deployment that has a secret store and no collector — which used to skip
|
# deployment that has a secret store and no collector, rather than being
|
||||||
# them entirely, because they were assertions inside swarm-otel's own `mkIf`.
|
# assertions inside swarm-otel's own `mkIf`.
|
||||||
#
|
#
|
||||||
# ⚠️ `controllerCommonName` is overridden to a name containing NO reserved
|
# ⚠️ `controllerCommonName` is overridden to a name containing NO reserved
|
||||||
# fragment. Its default (`swarm-controller`) contains `swarm` and is caught
|
# fragment. Its default (`swarm-controller`) contains `swarm` and is caught
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,9 @@ let
|
||||||
# refuses; see ./host-modules/deploy.nix. `package` and `bridgePackage` are
|
# refuses; see ./host-modules/deploy.nix. `package` and `bridgePackage` are
|
||||||
# ordinary options, so they do carry one. The two arms
|
# ordinary options, so they do carry one. The two arms
|
||||||
# below have different jobs. `usersFile` tests the rename; the nats one tests
|
# below have different jobs. `usersFile` tests the rename; the nats one tests
|
||||||
# that a reader repointed to the new namespace still renders the derived
|
# that a reader pointed at the `deploy` namespace still renders the derived
|
||||||
# path, which is the failure this slice could actually have shipped — seven
|
# path — seven of those reads go through an alias a path-shaped grep
|
||||||
# of those reads went through an alias a path-shaped grep cannot see.
|
# cannot see.
|
||||||
autheliaOldPath = hive {
|
autheliaOldPath = hive {
|
||||||
deploy.authelia.enable = true;
|
deploy.authelia.enable = true;
|
||||||
deploy.nats.enable = true;
|
deploy.nats.enable = true;
|
||||||
|
|
@ -84,8 +84,8 @@ let
|
||||||
|
|
||||||
# A hive running NOTHING of the swarm's own services — no IdP here, no
|
# A hive running NOTHING of the swarm's own services — no IdP here, no
|
||||||
# `swarm.authelia.url` set by hand. The whole point of the fixture is what it
|
# `swarm.authelia.url` set by hand. The whole point of the fixture is what it
|
||||||
# does *not* say: it is the shape whose IdP address used to be null, and
|
# does *not* say: no IdP address is given and none is derived from
|
||||||
# before that a co-location-derived guess.
|
# co-location.
|
||||||
autheliaNotColocated = hive { };
|
autheliaNotColocated = hive { };
|
||||||
# The same swarm's IdP host, for the arm that the two agree.
|
# The same swarm's IdP host, for the arm that the two agree.
|
||||||
autheliaColocated = hive { deploy.authelia.enable = true; };
|
autheliaColocated = hive { deploy.authelia.enable = true; };
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,7 @@ let
|
||||||
# deployment this unit exists for: authelia is the one host the store is
|
# deployment this unit exists for: authelia is the one host the store is
|
||||||
# guaranteed not to share once either has a machine of its own.
|
# guaranteed not to share once either has a machine of its own.
|
||||||
#
|
#
|
||||||
# ⚠️ `enable` is deliberately NOT set here. It used to be, with a comment
|
# ⚠️ `enable` is deliberately NOT set here: leaving it unset is what makes
|
||||||
# saying the default asked whether both ran on this host — which documented
|
|
||||||
# the co-location bug instead of catching it. Leaving it unset is what makes
|
|
||||||
# this fixture exercise the default rather than mask it.
|
# this fixture exercise the default rather than mask it.
|
||||||
secretPublisherRemote = hive {
|
secretPublisherRemote = hive {
|
||||||
deploy.authelia.enable = true;
|
deploy.authelia.enable = true;
|
||||||
|
|
@ -125,9 +123,9 @@ let
|
||||||
&& !(lib.elem "swarm-grafana" (clients grafanaRemoteAuthelia));
|
&& !(lib.elem "swarm-grafana" (clients grafanaRemoteAuthelia));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# The collector's half of the same defect and the same fix: this used to
|
# The collector's half of the same property: the client is not gated on
|
||||||
# be gated on `deploy.swarm-otel.enable`, so a split deployment
|
# `deploy.swarm-otel.enable`, which would let a split deployment register
|
||||||
# registered the client nowhere and authelia minted nothing to publish.
|
# it nowhere, so authelia would mint nothing to publish.
|
||||||
name = "the swarm's collector client is registered wherever authelia runs";
|
name = "the swarm's collector client is registered wherever authelia runs";
|
||||||
ok =
|
ok =
|
||||||
let
|
let
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,10 @@ let
|
||||||
&& !(lib.hasInfix "secret/swarm/hives/" s);
|
&& !(lib.hasInfix "secret/swarm/hives/" s);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
# The defect itself. These exporters used to be gated on the stores'
|
# These exporters must not be gated on the stores' PER-HOST enables: a
|
||||||
# PER-HOST enables, so a collector that did not share a host with them
|
# collector that did not share a host with them would render none at all
|
||||||
# rendered none at all and dropped everything it received, from every
|
# and drop everything it received, from every hive — silently, because
|
||||||
# hive — silently, because an absent exporter is not an error.
|
# an absent exporter is not an error.
|
||||||
name = "a collector that hosts neither store still exports to both";
|
name = "a collector that hosts neither store still exports to both";
|
||||||
ok =
|
ok =
|
||||||
let
|
let
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,10 @@ let
|
||||||
# derivation means a prompt edit doesn't bust the cargo cache.
|
# derivation means a prompt edit doesn't bust the cargo cache.
|
||||||
#
|
#
|
||||||
# …and the dep cache underneath is the one WITHOUT test targets. This
|
# …and the dep cache underneath is the one WITHOUT test targets. This
|
||||||
# derivation has never run a test — `doCheck = false` below says so —
|
# derivation never runs a test — `doCheck = false` below says so — so it
|
||||||
# but it used to sit on a cache that compiled every dev-dependency and
|
# sits on a cache that skips every dev-dependency and test harness, work
|
||||||
# test harness anyway, work whose only consumers are the checks. A
|
# whose only consumers are the checks. Compiling them here would make a
|
||||||
# deploy paid for it on every toolchain or dependency change.
|
# deploy pay for it on every toolchain or dependency change.
|
||||||
workspaceBuild = craneLib.buildPackage {
|
workspaceBuild = craneLib.buildPackage {
|
||||||
src = cleanSrc;
|
src = cleanSrc;
|
||||||
cargoArtifacts = cargoArtifactsBinOnly;
|
cargoArtifacts = cargoArtifactsBinOnly;
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,9 @@ buildNpmPackage {
|
||||||
# Read from `../../frontend/npm-deps-hash`, the single source of
|
# Read from `../../frontend/npm-deps-hash`, the single source of
|
||||||
# truth `./swarm-ui.nix` and `../checks.nix`'s `swarm-ui-typecheck`
|
# truth `./swarm-ui.nix` and `../checks.nix`'s `swarm-ui-typecheck`
|
||||||
# also read — all three build from the one `frontend/package-lock.json`,
|
# also read — all three build from the one `frontend/package-lock.json`,
|
||||||
# so one file keeps them from drifting independently (this used to be
|
# so one file keeps them from drifting independently (a hardcoded copy
|
||||||
# a hardcoded copy per derivation, and only some of the three got
|
# per derivation would let some of the three miss a lockfile change).
|
||||||
# updated the one time the lockfile changed). Regenerate with
|
# Regenerate with
|
||||||
# `prefetch-npm-deps frontend/package-lock.json` and overwrite the
|
# `prefetch-npm-deps frontend/package-lock.json` and overwrite the
|
||||||
# file whenever the lockfile changes.
|
# file whenever the lockfile changes.
|
||||||
npmDepsHash = builtins.readFile ../../frontend/npm-deps-hash;
|
npmDepsHash = builtins.readFile ../../frontend/npm-deps-hash;
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
|
|
||||||
# Pure vendored Swagger UI 5.17.14 static dist — no hyperhive theming
|
# Pure vendored Swagger UI 5.17.14 static dist — no hyperhive theming
|
||||||
# (see ./swagger-ui-theme.nix for that layer). Deliberately NOT sourced
|
# (see ./swagger-ui-theme.nix for that layer). Deliberately NOT sourced
|
||||||
# via the Rust `utoipa-swagger-ui-vendored` crate / Cargo.lock (per the
|
# via the Rust `utoipa-swagger-ui-vendored` crate / Cargo.lock: this is a
|
||||||
# operator's ask: a nix package that just contains the plain static
|
# nix package that just contains the plain static dist, not fetched via
|
||||||
# dist, not fetched via cargo) — hive-c0re no longer needs to know this
|
# cargo — hive-c0re does not need to know this exists, because the
|
||||||
# exists at all once the gateway hosts it directly.
|
# gateway hosts it directly.
|
||||||
#
|
#
|
||||||
# Sourced straight from the `swagger-ui-dist` npm package, which ships
|
# Sourced straight from the `swagger-ui-dist` npm package, which ships
|
||||||
# exactly the built `dist/` files (no source, no build step needed) at
|
# exactly the built `dist/` files (no source, no build step needed) at
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
# `{{config}}` placeholder its OWN build.rs
|
# `{{config}}` placeholder its OWN build.rs
|
||||||
# injects (`replace_default_url_with_config`
|
# injects (`replace_default_url_with_config`
|
||||||
# + `format_config`); hive-c0re doesn't serve
|
# + `format_config`); hive-c0re doesn't serve
|
||||||
# this file at all anymore, so that rewrite
|
# this file at all, so that rewrite
|
||||||
# is baked in statically here instead —
|
# is baked in statically here instead —
|
||||||
# hand-verified byte-for-byte against what
|
# hand-verified byte-for-byte against what
|
||||||
# `format_config` would produce, by applying
|
# `format_config` would produce, by applying
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@
|
||||||
# The swarm collector names its components `<kind>/<owner>` and uses the hive
|
# The swarm collector names its components `<kind>/<owner>` and uses the hive
|
||||||
# name as the owner, so a hive called `swarm` would silently replace the
|
# name as the owner, so a hive called `swarm` would silently replace the
|
||||||
# swarm tier's own pipelines and lose its own — it would keep accepting
|
# swarm tier's own pipelines and lose its own — it would keep accepting
|
||||||
# pushes into a pipeline that routes nowhere. Previously enforced only
|
# pushes into a pipeline that routes nowhere. `swarm-otel.nix` asserts
|
||||||
# against hive names, in `swarm-otel.nix`'s own `reservedOwners`.
|
# this name stays in the list, since its own `reservedOwners` reads it.
|
||||||
"swarm"
|
"swarm"
|
||||||
|
|
||||||
# ---- usernames the forge refuses -------------------------------------
|
# ---- usernames the forge refuses -------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue