From de7aed5016015e9a057552897648fa5e31eb420a Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 1 Oct 2026 15:20:32 +0200 Subject: [PATCH] The controller may publish the memberships it issues The server refused every membership the controller published after a push (2026-10-01, Permissions Violation for Publish to mesh.assignment..): the controller's own grant named the control, node and JetStream subjects and not the assignments it alone issues (hq ADR 0160). Broker golden regenerated; one line differs. --- internal/broker/nats.go | 6 ++++-- internal/broker/testdata/composed.conf | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/broker/nats.go b/internal/broker/nats.go index 17bca40..65d5120 100644 --- a/internal/broker/nats.go +++ b/internal/broker/nats.go @@ -173,8 +173,10 @@ func PermissionsFor(p Principal) (Permissions, error) { switch p.Kind { case KindController: // The controller owns the mesh's own traffic and the streams. It is the only writer of - // stream definitions (design 25 §3), so it alone reaches the JetStream API. - pub = []string{"mesh.control.>", "mesh.node.>", "$JS.API.>"} + // stream definitions (design 25 §3), so it alone reaches the JetStream API — and it alone + // issues memberships (novox/hq ADR 0160), which it publishes into the assignments stream + // after each push; refused by the server on 2026-10-01 until this line named them. + pub = []string{"mesh.control.>", "mesh.node.>", "mesh.assignment.>", "$JS.API.>"} // **And where its consumers deliver.** A push consumer delivers on `_DELIVER.`, // and a client bound to it subscribes exactly that; the server refused it for every // principal the first time one bound a consumer (2026-09-28). Each kind below is granted diff --git a/internal/broker/testdata/composed.conf b/internal/broker/testdata/composed.conf index 10f86b9..623f8c9 100644 --- a/internal/broker/testdata/composed.conf +++ b/internal/broker/testdata/composed.conf @@ -24,7 +24,7 @@ accounts { jetstream: enabled users = [ { user: "controller", password: "$2a$11$cccccccccccccccccccccc", permissions: { - publish: { allow: ["$JS.ACK.CONTROL.controller.>", "$JS.ACK.EVENTS.controller.>", "$JS.API.>", "_INBOX.enrol.>", "mesh.control.>", "mesh.mod.*.tool.>", "mesh.node.>", "mesh.seat.mesh-build-machine.accept.>", "mesh.seat.mesh-controller.event.applied", "mesh.seat.mesh-controller.event.built-before", "mesh.seat.mesh-controller.event.refused"] } + publish: { allow: ["$JS.ACK.CONTROL.controller.>", "$JS.ACK.EVENTS.controller.>", "$JS.API.>", "_INBOX.enrol.>", "mesh.assignment.>", "mesh.control.>", "mesh.mod.*.tool.>", "mesh.node.>", "mesh.seat.mesh-build-machine.accept.>", "mesh.seat.mesh-controller.event.applied", "mesh.seat.mesh-controller.event.built-before", "mesh.seat.mesh-controller.event.refused"] } subscribe: { allow: ["$JS.API.>", "_DELIVER.controller", "_DELIVER.controller.>", "_INBOX.controller.>", "mesh.control.>", "mesh.mod.gitea.event.pull.merged", "mesh.mod.mesh-catalog.event.catching-up", "mesh.mod.mesh-catalog.event.upgraded", "mesh.seat.mesh-build-machine.event.built", "mesh.seat.mesh-controller.tool.>"] } allow_responses: { max: 1, ttl: "1m" } } } -- 2.54.0