Watch
0
0
Fork
You've already forked hyperhive
0

Compare commits

..
4 changed files with 26 additions and 118 deletions

View file

@ -1,66 +0,0 @@
# Assertions about names an operator chooses, so a module states WHAT is
# forbidden and not how to phrase the refusal.
{ lib }:
let
# One shape for both guards. Reports EVERY offender, not the first: fixing
# one per rebuild is the slowest possible way to learn a rule.
#
# Offenders come after their label rather than before it, so one name and
# five read the same — no English verb agrees with both.
guard =
{
option,
names,
forbidden,
hit,
problem,
forbiddenLabel,
why,
}:
let
offenders = lib.filter (n: lib.any (f: hit f n) forbidden) names;
quote = xs: lib.concatMapStringsSep ", " (x: "'${x}'") xs;
in
{
assertion = offenders == [ ];
message = ''
${option} ${problem}: ${quote offenders}
${forbiddenLabel}: ${quote forbidden}
${why}
'';
};
in
{
# No name may BE one of `reserved`.
mustNotEqual =
{
option,
names,
reserved,
why,
}:
guard {
inherit option names why;
forbidden = reserved;
hit = f: n: f == n;
problem = "has reserved name(s)";
forbiddenLabel = "Reserved";
};
# No name may CONTAIN one of `fragments`.
mustNotContain =
{
option,
names,
fragments,
why,
}:
guard {
inherit option names why;
forbidden = fragments;
hit = lib.hasInfix;
problem = "has name(s) containing a reserved word";
forbiddenLabel = "Forbidden as substrings";
};
}

View file

@ -72,13 +72,6 @@ let
# would be an eval error rather than a silently missing guard. # would be an eval error rather than a silently missing guard.
reservedOwners = import ../reserved-names.nix; reservedOwners = import ../reserved-names.nix;
# Words no hive name may CONTAIN, because identifiers are composed from a
# hive name. That file explains why it is not merged into the list above.
reservedFragments = import ../reserved-hive-fragments.nix;
nameGuards = import ./lib/name-guards.nix { inherit lib; };
hiveNames = lib.attrNames hyperhiveCfg.swarm.hives;
# A published target is declared as ONE url, because that url is also the # A published target is declared as ONE url, because that url is also the
# audience its token is minted for — but prometheus wants the same fact in # audience its token is minted for — but prometheus wants the same fact in
# three fields. Split it here rather than asking a service to state it # three fields. Split it here rather than asking a service to state it
@ -665,30 +658,33 @@ in
Put it back, or give this module a different swarmTierName. Put it back, or give this module a different swarmTierName.
''; '';
} }
(nameGuards.mustNotEqual { {
option = "services.hyperhive.swarm.hives"; # A hive whose name is one this module claims for itself collides in
names = hiveNames; # the collector's component namespace, and `//` resolves it silently:
reserved = reservedOwners; # the swarm tier's parts win, that hive's `metrics/<name>` pipeline
why = '' # disappears, and its `resource/<name>` stamp goes with it — so the
The collector names components `<kind>/<owner>` with the hive name # hive still connects and pushes into nothing, unlabelled.
as owner, and `//` resolves the clash silently: the swarm tier's #
parts win, that hive's pipeline and `hive=` stamp disappear, and it # Checked rather than documented for the same reason the port range
keeps pushing into a route that goes nowhere. Rename the hive. # is: the names are all known at evaluation time, and the runtime
# symptom is a hive that looks healthy and reports no metrics, with
# nothing in any log naming the cause.
assertion = !lib.any (h: lib.elem h reservedOwners) (lib.attrNames hyperhiveCfg.swarm.hives);
message = ''
services.hyperhive.swarm.hives contains ${
lib.concatMapStringsSep ", " (h: "'${h}'") (
lib.filter (h: lib.elem h reservedOwners) (lib.attrNames hyperhiveCfg.swarm.hives)
)
}, which the swarm collector reserves for its own pipelines.
The collector names components `<kind>/<owner>` and uses the hive
name as the owner, so such a hive would silently replace the
swarm tier's parts and lose its own — it would keep accepting
pushes into a pipeline that routes nowhere.
Rename the hive. Reserved: ${lib.concatMapStringsSep ", " (n: "'${n}'") reservedOwners}.
''; '';
}) }
(nameGuards.mustNotContain {
option = "services.hyperhive.swarm.hives";
names = hiveNames;
fragments = reservedFragments;
why = ''
Hive-scoped identifiers are composed from a hive name —
`hive-<name>`, `hive-<name>-agent` — so a name containing one of
these produces an identifier that is also somebody else's. The
failure is a wrong grant rather than an error: the client
authenticates and receives another principal's permissions, and a
NATS denial arrives as a timeout. Rename the hive.
'';
})
{ {
# A published target that is not an `https://host/path` url. Without # A published target that is not an `https://host/path` url. Without
# this the split returns null and the failure surfaces as # this the split returns null and the failure surfaces as

View file

@ -1,19 +0,0 @@
# Words no HIVE name may CONTAIN. The substring half of `./reserved-names.nix`.
#
# Separate because the two differ in every dimension: subject (hive names only
# vs hive AND agent), matcher (substring vs equality), and admission rule (a
# fixed segment of a COMPOSED identifier vs a value some component emits).
# Folding them would widen the other one — containment over `forge` refuses
# `forgeworks`, which names no failure.
[
# `hive-<name>` — every hive's OIDC client id, and the string the queue's
# auth-callout responder strips to decide which hive a connection is.
"hive"
# `hive-<name>-agent` — the client its agent containers present. A hive named
# `foo-agent` mints exactly the id hive `foo`'s agents do.
"agent"
# Also an exact-match entry next door, for the collector's `<kind>/<owner>`
# components. Here because it is the word this swarm composes identifiers
# from everywhere.
"swarm"
]

View file

@ -25,9 +25,6 @@
# `from`/`to`, or a component name the collector builds pipelines from — not a # `from`/`to`, or a component name the collector builds pipelines from — not a
# word that merely looked risky. A name in here that nothing emits is a refusal # word that merely looked risky. A name in here that nothing emits is a refusal
# with no failure behind it. # with no failure behind it.
#
# ⚠️ Matched by EQUALITY. Words forbidden *inside* a hive name live in
# `./reserved-hive-fragments.nix` — read its header before merging the two.
[ [
# ---- message-layer senders ------------------------------------------- # ---- message-layer senders -------------------------------------------
# The human at the dashboard: a broker recipient (the T4LK box sends # The human at the dashboard: a broker recipient (the T4LK box sends