hyperhive/nix/reserved-hive-fragments.nix
atlas cdaf5824ed swarm: refuse hive names that CONTAIN a word identifiers are composed from
Hive-scoped identifiers are built by joining a hive name with fixed words —
`hive-<name>`, `hive-<name>-agent` — so a hive called `foo-agent` produces
exactly the id hive `foo`'s agent containers produce. The queue's responder
resolves it as the agents, and that hive silently receives an agent grant
instead of its own; a NATS denial reaches a client as a timeout, so nothing
names the cause. Equality cannot see this: the two NAMES differ, only the
composed strings collide.

Deliberately a second file rather than three more entries in
`reserved-names.nix`. That list is matched by equality against a whole name and
every entry is a value some component emits; applying containment to it would
refuse `forgeworks` and `operator-hq` for failures that do not exist. The two
lists differ in subject (hive-only vs both), matcher (substring vs equality)
and admission rule, and each file's header says so.

No Rust change: `HIVE_RESERVED_NAMES` answers "may an AGENT be called this",
which stays an equality question — an agent name is never composed into these
identifiers.

Measured against the predicate rather than assumed:

  pr1ma=ok alpha=ok forgeworks=ok operator-hq=ok
  foo-agent=REFUSED beehive=REFUSED swarm=REFUSED myswarmx=REFUSED

The four `ok` cases are the control: `forgeworks` and `operator-hq` are exactly
what a merged list would have refused.
2026-08-31 18:50:15 +02:00

45 lines
2.4 KiB
Nix

# Words no HIVE name may CONTAIN — a substring blacklist, and the second half
# of `./reserved-names.nix`.
#
# Two files rather than one list, because the two differ in every dimension
# that matters and folding them would silently widen one of them:
#
# | | `reserved-names.nix` | this file |
# |---|---|---|
# | subject | agent AND hive names | hive names only |
# | match | equality | substring |
# | why | the name IS a value some component emits | the word is a fixed SEGMENT of a derived identifier |
#
# ⚠️ That last row is the whole argument. `hive-<name>` and `hive-<name>-agent`
# are built by composing a hive name with these words, so a hive called
# `foo-agent` produces an id another hive's AGENTS also produce — the composed
# strings collide even though the names do not. Equality cannot see that; only
# containment can.
#
# ⇒ And it is why the sender words (`operator`, `system`, `forge`, …) are NOT
# here: those are compared against a whole name, never embedded in one, so
# applying containment to them would refuse `forgeworks` for no failure that
# exists. `reserved-names.nix`'s own rule — an entry must name a real failure,
# not a word that looked risky — governs this file too.
#
# Read by `host-modules/swarm-otel.nix`, which owns the hive-roster assertion.
# Deliberately NOT exported to the Rust side: `HIVE_RESERVED_NAMES` answers
# "may an AGENT be called this", which stays an equality question.
[
# `hive-<name>` — every hive's own OIDC client id (`swarm-authelia.nix`'s
# `hiveClientPrefix`), 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
# (`agentClientSuffix`). A hive named `foo-agent` mints exactly the id hive
# `foo`'s agents do, and the responder resolves it as the agents: that hive
# silently receives an agent grant instead of its own, and a NATS denial
# arrives as a timeout, so nothing names the cause.
"agent"
# Already in `reserved-names.nix` as an exact match, listed again here for
# the wider rule: the swarm collector names components `<kind>/<owner>` with
# the hive name as owner, and a name merely CONTAINING it is not a collision
# today — but `swarm` is the one word this swarm composes identifiers from
# everywhere, so the operator's rule covers it and the two files agree.
"swarm"
]