feat(swarm-nats): JetStream, with the per-account grant the server needs

Retention so a reader can ask what a hive last said without anyone
keeping a second copy. The container is ephemeral = false, so the store
survives a restart with no bind mount, and losing it degrades to the
true answer (nothing known) rather than a stale one.

The per-account grant is the part that is easy to miss: the server
option gives the SERVER JetStream, and an account gets it only from its
own. Measured against a running 2.14.1 with this exact two-account
shape - global-only makes kv operations fail 10039 while the server
starts cleanly and logs Starting JetStream, so no rendering check can
see it. Deliberately not granted to the callout account: the responder
mints credentials and has no business holding stream state.
This commit is contained in:
atlas 2026-08-15 11:35:18 +02:00 committed by mara
commit 4c6b1ff701

View file

@ -276,6 +276,18 @@ in
services.nats = {
enable = true;
# Retention, so a reader can ask "what did this hive last
# say?" without anyone keeping a second copy. The upstream
# option also wires `settings.jetstream.store_dir = dataDir`;
# the container is `ephemeral = false`, so that survives a
# restart with no bind mount.
#
# ⚠️ Losing the store is not a correctness problem here: a
# reader then sees nothing for every hive, which is the true
# answer until each one publishes again. It degrades to
# honesty rather than to a stale "healthy".
jetstream = true;
serverName = "swarm-nats";
port = cfg.port;
settings = {
@ -307,7 +319,25 @@ in
# with a hole. So the only way to get a live server here is
# a real key whose seed nobody but the responder holds.
${calloutAccount}.users = [ { nkey = cfg.calloutUserPublicKey; } ];
${clientAccount} = { };
# ⚠️ `services.nats.jetstream = true` gives the SERVER
# JetStream; an account gets it only from its own grant.
# Measured against a running 2.14.1 with this exact
# two-account shape, because the failure is invisible to
# any config-rendering check:
#
# global jetstream only → `nats kv add` from this
# account fails `code=503 err_code=10039 jetstream
# not enabled for account`, while the server starts
# cleanly and logs "Starting JetStream"
# + this line → the same command succeeds
#
# The grant is per-account by design, and that is worth
# keeping: the callout account above deliberately does
# NOT get it. The responder mints credentials; it has no
# business holding stream state.
${clientAccount} = {
jetstream = "enabled";
};
};
authorization = {