docs(swarm): trim the respond module doc under the 30-line block lint

The discovery narrative belongs in the PR body, which carries it. What
stays at the line is what the code cannot say: that issuer_account must be
absent and why reaching for Token::new_user reintroduces the bug, the
BASE32HEX-vs-BASE32 distinction, and that nats-jwt is a test oracle rather
than a runtime dependency.
This commit is contained in:
atlas 2026-08-14 23:00:49 +02:00 committed by mara
commit b22f0ecaa1

View file

@ -1,44 +1,30 @@
//! Minting the reply the server expects on `msg.reply`.
//!
//! Two JWTs are involved and **neither** can be produced by `nats-jwt`, for
//! the same underlying reason: its `Claims` struct has no `aud`, and both of
//! these need one.
//! Both JWTs are hand-built because neither is expressible through
//! `nats-jwt`: its `Claims` has no `aud`, which the `authorization_response`
//! wrapper needs (the asking server's id, so a captured reply cannot be
//! replayed elsewhere in the cluster) and the user token needs (the account
//! name). Its `IntoNatsClaims` also returns a closed
//! `NatsClaims { User, Account }`.
//!
//! * the **`authorization_response`** wrapper — `aud` is the asking server's
//! id, so a captured reply cannot be replayed at a different server in the
//! cluster. Its `IntoNatsClaims` trait also returns the closed enum
//! `NatsClaims { User, Account }`, so the claim type is inexpressible
//! through it twice over.
//! * the **user JWT** it carries — in **server-config mode** (which is what
//! `nix/host-modules/swarm-nats.nix` renders: `accounts { AUTH, APP }`,
//! no operator), `aud` is the *account name* the admitted client lands in,
//! and `issuer_account` must be **absent**. `nats_jwt::Token::new_user`
//! always sets `issuer_account` and can express no `aud` at all.
//! ⛔ **`issuer_account` must be ABSENT from the user token.** It is an
//! operator-mode field, and `nix/host-modules/swarm-nats.nix` renders
//! *server-config* mode, where its mere presence makes the server refuse the
//! client — `Error non operator mode account "AUTH": attempted to use
//! issuer_account`, with the responder having answered `granted=true`.
//! `nats_jwt::Token::new_user` always sets it, which is why reaching for that
//! constructor again would reintroduce the bug.
//!
//! 🩸 That second one was found by running the thing, not by reading it. The
//! responder answered `granted=true` and the server still refused the client,
//! logging `Error non operator mode account "AUTH": attempted to use
//! issuer_account`. Every unit test passed throughout: they assert the shape
//! this module *intends*, and the field that broke it was one nobody had
//! reason to assert was missing. `issuer_account` is an operator-mode field —
//! it names the account when a *signing* key rather than the account identity
//! key signed the token — and in config mode its mere presence is fatal.
//! Signing follows `nats-jwt`'s own `sign()`: serialise the claims with `jti`
//! **empty**, sha256 that, `BASE32HEX_NOPAD` the digest into `jti`,
//! re-serialise, sign `"<b64url header>.<b64url claims>"` with the account
//! key. ⚠️ `BASE32HEX`, not `BASE32` — one word, and the only symptom is a
//! token the server rejects without saying why.
//!
//! So both are hand-built here, following the algorithm read out of
//! `nats-jwt`'s own `sign()`: serialise the claims with `jti` **empty**,
//! sha256 that, `BASE32HEX_NOPAD` the digest into `jti`, re-serialise, and
//! sign `"<b64url header>.<b64url claims>"` with the account key.
//!
//! ⚠️ `BASE32HEX`, not `BASE32`. That single word is the kind of thing that
//! produces a token the server rejects with no useful reason, and it is
//! copied from the reference implementation rather than from memory — which
//! is also why [`tests::hand_built_matches_the_reference`] exists: it builds
//! a *user* token both ways and asserts byte equality, so this encoder is
//! checked against `nats-jwt` on the shape that crate does model. Trusting it
//! on the shapes `nats-jwt` does **not** model has to rest on something
//! better than my reading of its source. `nats-jwt` is therefore a
//! **dev-dependency**: it is this module's test oracle, not part of the path
//! that runs in production.
//! `nats-jwt` is therefore a **dev-dependency, not a runtime one**: it is this
//! module's test oracle. [`tests::hand_built_matches_the_reference`] builds a
//! user token both ways and requires byte equality, so the encoder is checked
//! against the reference on the one shape that crate does model.
use data_encoding::{BASE32HEX_NOPAD, BASE64URL_NOPAD};
use nkeys::KeyPair;