swarm-nats: manual callout needs all four keys, not two

The two callout assertions guarded the halves the server verifies with.
The responder needs the other halves, and nothing related them: a config
supplying only the public keys satisfies both, renders a syntactically
valid auth_callout block, and defines no responder unit.

Callout with no responder is the fail-closed state, so that queue refuses
every client — and a NATS denial arrives as a timeout, so the symptom is
every consumer hanging with nothing logged anywhere.

The build-time config check does run in this case and passes, because the
config is valid; what is missing is a unit, and the absence of a unit is
not an event.
This commit is contained in:
atlas 2026-08-24 22:28:31 +02:00 committed by mara
commit aad5d3638f
2 changed files with 40 additions and 0 deletions

View file

@ -448,6 +448,39 @@ in
and no key authenticates anyone who sends that name.
'';
}
{
# The two above guard the halves the SERVER needs; the responder
# needs the other halves, and nothing related them. Satisfying
# only the public ones renders a valid `auth_callout` block and
# defines no responder — and callout with no responder is the
# fail-closed state, so the queue refuses everyone.
#
# Nothing downstream catches it. `nats-server -t` runs in exactly
# this case and passes, because the config IS valid; the missing
# piece is a unit, and the absence of a unit is not an event. The
# symptom is every client timing out, since a NATS denial reaches
# the client as a timeout rather than an error.
# The binding itself, not a copy of its formula: what is being
# asserted IS "the responder is configured", and two copies of one
# boolean is two places for a future edit to land in only one.
assertion = responderConfigured;
message = ''
services.hyperhive.swarm.nats has callout public keys but no
seed files:
nats.calloutUserSeedFile = "${cfg.calloutUserSeedFile}"
nats.calloutIssuerSeedFile = "${cfg.calloutIssuerSeedFile}"
Each seed is the private half of the public key already set
here the server verifies with the public half, the responder
signs with the private one. Configuring one side alone leaves
this queue with an auth-callout nobody answers, which refuses
every client rather than degrading.
Set both seed paths, or set nats.autoGenerateCallout = true to
have this host mint all four.
'';
}
{
assertion = autheliaUrl != null;
message = ''