nats: say whether a user's grant is in force on the running server, not only written (hq issue 353)
mesh/merge-gate pass: builds nats, sent nowhere; merging this needs a person's bus upgrade; 1 wait(s) for a person; every machine composes with the change …
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered

nats_user_can read the user list on the machine. A list written is not a list the bus enforces until
the server reloads it: on 2026-10-09 the controller asked between the write and the reload's effect.
The answer now carries in_force — allowed in the file, and the server's config_load_time after the
file's write — with why when it is not, and anything not known is not in force.
This commit is contained in:
2026-10-12 12:13:03 +02:00
parent da296cacc6
commit 4fdb047cc8
4 changed files with 107 additions and 4 deletions
+36
View File
@@ -536,3 +536,39 @@ func UserCan(conf []byte, user, action, subject string) (map[string]any, error)
}
return nil, fmt.Errorf("the bus has no user %q; nats_users lists them", user)
}
// InForce adds to a UserCan answer whether the running server holds what the file says (novox/hq issue 353):
// `in_force` only when the grant is allowed in the file and the server's last configuration load
// (`config_load_time` of /varz) came after the file was last written. Anything not known is not in force, said
// in `why`: an asker waiting for its grant must not take a list sent for a list the bus enforces.
func InForce(answer map[string]any, written time.Time, writtenErr error, varz []byte, varzErr error) map[string]any {
answer["in_force"] = false
switch {
case writtenErr != nil:
answer["why"] = writtenErr.Error()
return answer
case varzErr != nil:
answer["why"] = "the server's last configuration load could not be read: " + varzErr.Error()
return answer
}
var v struct {
ConfigLoadTime time.Time `json:"config_load_time"`
}
if err := json.Unmarshal(varz, &v); err != nil || v.ConfigLoadTime.IsZero() {
answer["why"] = "the server does not say when it last loaded its configuration"
return answer
}
answer["file_written"] = written.UTC().Format(time.RFC3339Nano)
answer["server_loaded"] = v.ConfigLoadTime.UTC().Format(time.RFC3339Nano)
loaded := !v.ConfigLoadTime.Before(written)
allowed, _ := answer["allowed"].(bool)
switch {
case !allowed:
answer["why"] = "the user list on the machine does not allow it"
case !loaded:
answer["why"] = "the user list on the machine allows it, and the server has not reloaded since it was written"
default:
answer["in_force"] = true
}
return answer
}