swarm-bao: set the cluster address raft refuses to start without

openbao has never started on a hive that deploys it. With raft storage
and no `cluster_addr`, `bao server` exits 1 before serving anything:

    cluster address must be set when using raft storage

Reproduced against openbao 2.6.2 with the module's own config shape:
the same JSON minus `cluster_addr` exits 1 with that line, and with it
the server comes up and configures every listener.

Both addresses are built from `swarm.bao.domain` rather than a bind
address — that is the URL a reader already dials (`BAO_ADDR` in
glue-matrix-bao-token) and the name the server certificate carries.
Cluster traffic sits one port up, upstream's convention.

The `unknown or unsupported field loopback` warning in the same startup
log is unrelated and cosmetic: openbao's unknown-field check does not
know about named listener blocks, but the parser honours `type` and
configures each one (measured, two named listeners, both served).
Noted in the module so the next reader does not chase it; the JSON
array form that avoids the warning does not typecheck against nixpkgs'
`settings.listener` (`attrsOf`).

module-eval gains the regression gate plus a control that the settings
it reads vary per deployment.

Refs #3860
This commit is contained in:
atlas 2026-08-31 20:17:28 +02:00
commit 75a6101f66
2 changed files with 39 additions and 0 deletions

View file

@ -158,6 +158,10 @@ let
#
# Neither is a superset of the other, which is why this is not one address
# with a conditional value.
# ⚠️ openbao logs `unknown or unsupported field <name>` for each key here. Its
# unknown-field check does not know about named listener blocks; the parser
# does, honours `type`, and configures every one. Not the cause of a store
# that fails to start — look at `advertise` below.
listeners = {
loopback = {
type = "tcp";
@ -166,6 +170,18 @@ let
// listenerTls;
}
// extraListeners;
# Raft REFUSES TO START without `cluster_addr`, and the message names neither
# the setting nor the stanza: "cluster address must be set when using raft
# storage".
#
# By name and not by address: this is the URL a reader dials, and the name the
# server certificate has to carry anyway. Cluster traffic is one port up,
# upstream's own convention.
advertise = {
api_addr = "https://${cfg.domain}:${toString cfg.port}";
cluster_addr = "https://${cfg.domain}:${toString (cfg.port + 1)}";
};
in
{
# One service, two namespaces, and the split decides who may set what.
@ -615,6 +631,7 @@ in
listener = listeners;
storage.raft.path = stateDir;
}
// advertise
// sealSettings;
};