A hive whose whole swarm is one box had to be handed two nkeys by hand before its queue could authenticate anyone, which is the one deployment shape where nobody else can supply them. `autoGenerateCallout` mints both keypairs on the host on first boot, keeps the seeds at 0600 host-side, and writes only the public halves into a fragment the server reads at start. The all-local mode turns it on; everywhere else the options stay operator-supplied and the fail-closed eval assertions keep their full force. The server config is not rewritten to do this. A wrapper includes upstream's rendered `settings` verbatim plus the runtime fragment, and the fragment wins — measured, along with the property that makes the whole shape safe: the empty strings the options render in auto mode are values `nats-server` refuses to start on, so any field the merge fails to reach fails closed loudly rather than leaving a walk-in-able server. The wrapper, the settings symlink and the fragment are siblings in one runtime directory, and that is forced rather than tidy: NATS resolves an include with filepath.Join against the config file's own directory, which strips a leading slash, so an absolute include silently becomes a relative one and the server never finds it. The includes are therefore bare filenames. That also means nothing in the closure would otherwise name the rendered settings, so the generator's symlink to it is what keeps it from being garbage-collected under a running server. `accounts` and `authorization` are defined once and rendered twice, into `settings` and into the fragment template. Written out separately they would diverge silently and backwards: the fragment is the later definition, so a future edit to `settings` alone would be ignored on exactly the hives that use auto mode. `validateConfig` goes off in auto mode because `nats-server -t` rejects the empty keys at build time; the parse check moves to server start, where the fragment exists. Upstream's own option description names this case.
92 lines
4.6 KiB
Nix
92 lines
4.6 KiB
Nix
# The all-local deployment mode.
|
|
#
|
|
# `enableAllLocalDefaults` is a *mode*, not a default other options read:
|
|
# it says "this box is the whole deployment" and then asserts the values
|
|
# that follow from that. mara, on the issue: it is "more of a deployment
|
|
# mode via settings set, less a default setting".
|
|
#
|
|
# That distinction is why the derivations live here as `mkDefault` in a
|
|
# `config` block rather than as `default =` inside each option. An option
|
|
# declares what IT is and what it is when nobody asks; a mode declares
|
|
# what a deployment shape implies. Written the other way round, every
|
|
# service option had to name a flag it has no relationship to, and the
|
|
# answer to "what does all-local turn on?" was spread across five files.
|
|
#
|
|
# Adding an autoconfigurable thing later means one line here — not a
|
|
# `default =` in the new module pointing back at this flag.
|
|
{
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.services.hyperhive;
|
|
in
|
|
{
|
|
options.services.hyperhive.enableAllLocalDefaults = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
example = true;
|
|
description = ''
|
|
Run the whole swarm on this host. Turning this on asserts the
|
|
toggles that an all-on-one-box deployment implies: the swarm's
|
|
shared services
|
|
(`services.hyperhive.swarm.enableRequiredServices`), the swarm
|
|
CA (`services.hyperhive.swarm.ca.autoConfigure`), the swarm
|
|
controller (`services.hyperhive.swarm.controller.enable`), and the
|
|
host's `/etc/hosts` entries for the names this hive serves
|
|
(`services.hyperhive.gateway.localHostsEntry`) — with no real DNS
|
|
for those names, the operator is browsing them from the same box
|
|
that answers for them.
|
|
|
|
**Off by default, and that is the load-bearing part.** A swarm's
|
|
services and its hives can live on different hosts, and a host has
|
|
no way to tell which ones it is meant to be — so this is an
|
|
operator saying "this is that box", never something inferred.
|
|
Turn it on for a dev box or a single-hive swarm and get a working
|
|
deployment with no further configuration; leave it off and every
|
|
swarm-level artifact is operator-provided.
|
|
|
|
Each toggle it asserts can still be set explicitly, which wins —
|
|
so "all local except X" needs no new option.
|
|
'';
|
|
};
|
|
|
|
# What the mode asserts. `mkDefault` (priority 1000) beats an option's
|
|
# own `default` (1500) and loses to any explicit definition, which is
|
|
# exactly the precedence a deployment mode wants: it fills in for an
|
|
# operator who hasn't spoken, and never argues with one who has.
|
|
# The gateway's own all-local bit. `localHostsEntry` maps every name
|
|
# this hive answers for to 127.0.0.1 in the HOST's /etc/hosts, which is
|
|
# exactly what "this box is the whole deployment" implies: there is no
|
|
# real DNS for these names, and the operator is browsing them from the
|
|
# same machine that serves them.
|
|
#
|
|
# ⚠️ It does NOT affect what containers resolve. dnsmasq sets
|
|
# `no-hosts = true` unconditionally (see hive-gateway/dnsmasq.nix), so
|
|
# agents keep getting the bridge IP from the authoritative `address=`
|
|
# rules rather than the host's 127.0.0.1 — an entry that would point
|
|
# every agent at its own netns. That guard already existing is what
|
|
# makes turning this on by default safe; without it this line would
|
|
# break every agent's access to the forge.
|
|
config.services.hyperhive.gateway.localHostsEntry = lib.mkDefault cfg.enableAllLocalDefaults;
|
|
|
|
config.services.hyperhive.swarm = {
|
|
enableRequiredServices = lib.mkDefault cfg.enableAllLocalDefaults;
|
|
ca.autoConfigure = lib.mkDefault cfg.enableAllLocalDefaults;
|
|
# The queue's auth-callout nkeys. Generating them is safe exactly
|
|
# when one operator owns both the queue and its responder, which is
|
|
# what this mode asserts. On any other topology the seeds have to
|
|
# reach whoever runs the responder, and minting them here would move
|
|
# that hand-off somewhere less visible rather than removing it.
|
|
nats.autoGenerateCallout = lib.mkDefault cfg.enableAllLocalDefaults;
|
|
# The controller is asserted by the MODE and by nothing else. Its own
|
|
# option stays `default = false` precisely because running it is a
|
|
# statement about swarm topology — but "this box is the whole
|
|
# deployment" IS that statement, and it is the one shape where the
|
|
# answer isn't ambiguous. Deriving it from `enableRequiredServices`
|
|
# instead would be wrong: a hive in a larger swarm can legitimately
|
|
# want the shared services without being the host that controls them.
|
|
controller.enable = lib.mkDefault cfg.enableAllLocalDefaults;
|
|
};
|
|
}
|