nats declares its own server settings, and where the mesh's users go

The split the controller now makes, from this side. The module's own
configuration — ports, TLS, JetStream — is a declared file resource, because those
are properties of this container and change when its image does. `bus-users` names
where the mesh writes every account and permission, in the same directory, and the
module's configuration includes it.

**Both files in one directory because they have to be.** An absolute include path
is resolved relative to the including file's directory: nats-server given
`include /etc/nats/accounts.conf` from /etc/nats-server/nats.conf looks for
/etc/nats-server/etc/nats/accounts.conf and refuses to start. Verified against the
server, and recorded in the configuration itself where somebody moving a file will
read it.

**`verify: true` is gone, and it was refusing every connection in the mesh.** It
makes the server demand a client certificate; a host pins this server's exact
certificate and authenticates with the password the mesh minted, and presents none.
Found by building this image and connecting to it as a host would.

The entrypoint now waits for both files and watches the mesh's half: the module's
own does not change without a new declaration, and that recreates the container
anyway. Verified end to end against this image — the mesh's user list rewritten,
the module noticing and reloading the server itself with no signal from outside,
and the connection the mesh already had still working afterwards.
This commit is contained in:
2026-09-27 02:50:35 +02:00
parent ae99204a8c
commit a093c88c32
2 changed files with 30 additions and 14 deletions
+22 -14
View File
@@ -3,9 +3,9 @@
# configuration.
#
# **Why this exists inside the module** (novox/hq design 25 §5). The controller composes every
# account and permission into one configuration file, and that file changes whenever a module is
# added, reassigned, or a person's access is granted or revoked — which is often, and on the one
# server everything else depends on. The host has no way to say "reload this container": a
# account and permission into one file, and that file changes whenever a module is added,
# reassigned, or a person's access is granted or revoked — which is often, and on the one server
# everything else depends on. The host has no way to say "reload this container": a
# container resource has `restart-on` and nothing else, and a container's `restart-on` means
# *recreate* — every connection dropped and every in-flight JetStream ack lost, mid-flight, for a
# permission change. `reload-on` is real but it is a *service* field, not a container's.
@@ -19,21 +19,29 @@
# here is declared `restart-on` or `reload-on`.
set -eu
# **Two files, and only one of them is the mesh's** (novox/hq design 25 §4, task 1.7). CONF is this
# module's own — ports, TLS, JetStream — declared in its manifest, because those are properties of
# the container this module raises. USERS is every account and permission, composed by the
# controller, and CONF includes it. So what is watched here is the mesh's half: the module's own
# does not change without a new declaration, and that recreates the container anyway.
CONF="${MESH_NATS_CONF:-/etc/nats/nats.conf}"
USERS="${MESH_NATS_USERS:-/etc/nats/accounts.conf}"
POLL="${MESH_NATS_CONF_POLL_SECONDS:-5}"
# The controller writes the configuration as part of the same declaration that creates this
# container, but the two are not ordered against each other. Waiting is correct and starting
# without one is not: nats-server would come up with its compiled-in defaults — no TLS, no
# accounts, every subject open to anyone who can reach the port — and then be reloaded into
# correctness a moment later. A bus that is briefly open to everything is not a bus that is
# briefly wrong; it is an open bus.
while [ ! -s "$CONF" ]; do
echo "[nats] waiting for the mesh to compose $CONF"
sleep 1
# Both are written as part of the same declaration that creates this container, but none of the
# three are ordered against each other. Waiting is correct and starting without them is not:
# nats-server given a configuration whose include is missing refuses to start, and one given no
# configuration at all comes up with its compiled-in defaults — no TLS, no accounts, every subject
# open to anyone who can reach the port. A bus that is briefly open to everything is not a bus that
# is briefly wrong; it is an open bus.
for needed in "$CONF" "$USERS"; do
while [ ! -s "$needed" ]; do
echo "[nats] waiting for the mesh to write $needed"
sleep 1
done
done
digest() { sha256sum "$CONF" 2>/dev/null | cut -d' ' -f1; }
digest() { sha256sum "$USERS" 2>/dev/null | cut -d' ' -f1; }
nats-server --config "$CONF" "$@" &
server=$!
@@ -52,7 +60,7 @@ while kill -0 "$server" 2>/dev/null; do
[ -n "$now" ] || continue
if [ "$now" != "$last" ]; then
last=$now
echo "[nats] configuration changed; reloading in place"
echo "[nats] the mesh's user list changed; reloading in place"
kill -HUP "$server" || true
fi
done