Stop hearing mesh.assignment: a process follows its membership from the grants part's state alone
ADR 0571 clause 10: the grants part publishes memberships on mesh.assignment until the lease write and stops then. FollowMembership now reads and watches its own key of the state memberships and nothing else, and refuses to follow, naming the state, when it cannot read it (there is nothing else to follow, and a silent follow of nothing is the fault). A module's principal and the runtime lose their subscribe on mesh.assignment; the grants part's publish grant stays until the write (S21 stops the publishing). Tests: TestFollowMembershipHearsWhatIsIssuedToTheAccount (integration) edited: no state is a refusal, a membership put in the state is applied, one published on the subject is not; run locally against its embedded bus. Rows added to TestTheCopiedComposerComposesAsTheControllers: nobody subscribes mesh.assignment, and the controller's golden grant of it is the one row the copy now lacks. No new test function. (issue 774, 774/S5) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -161,8 +161,11 @@ func grantsAsTheControllers(t *testing.T, got, want string) {
|
||||
intended := beyondTheControllers(user)
|
||||
for g := range was {
|
||||
// A runtime's direct get of its membership from the assignments stream went with the stream (research 072
|
||||
// row 33): it reads the grants part's state memberships instead (ADR 0571 clause 7).
|
||||
if !now[g] && !strings.HasPrefix(g, "$JS.API.DIRECT.GET."+AssignmentsStream+".mesh.assignment."+strings.SplitN(user, ".", 2)[0]+".") {
|
||||
// row 33): it reads the grants part's state memberships instead (ADR 0571 clause 7). And it no longer hears
|
||||
// the subject, where the grants part stops publishing at the lease write (ADR 0571 clause 10, issue 774).
|
||||
node := strings.SplitN(user, ".", 2)[0]
|
||||
if !now[g] && !strings.HasPrefix(g, "$JS.API.DIRECT.GET."+AssignmentsStream+".mesh.assignment."+node+".") &&
|
||||
!strings.HasPrefix(g, "mesh.assignment."+node+".") {
|
||||
t.Errorf("%s: the copy does not grant %s, which the controller's does", user, g)
|
||||
}
|
||||
}
|
||||
@@ -880,6 +883,17 @@ func healthChecks(t *testing.T) {
|
||||
if u.Kind == KindModule && MayPublish(perms, "$KV.mesh-delivery-assigner_policies.x") {
|
||||
t.Errorf("%s writes the assigner's policies", u.Username())
|
||||
}
|
||||
// ADR 0571 clause 10 (issue 774, 774/S5/AC2): nobody hears memberships on mesh.assignment; they are read from
|
||||
// the grants part's state.
|
||||
if MaySubscribe(perms, "mesh.assignment.a.restic") {
|
||||
t.Errorf("%s hears mesh.assignment", u.Username())
|
||||
}
|
||||
}
|
||||
// Nor the runtime carrying the node's modules, which reads each one's membership from the state.
|
||||
if perms, err := PermissionsFor(Principal{Kind: KindNodeTools, Node: "a", Module: RuntimeModule,
|
||||
Carries: []Declared{{Module: "restic"}}}); err != nil || MaySubscribe(perms, "mesh.assignment.a.restic") ||
|
||||
!MayPublish(perms, "$JS.API.DIRECT.GET.KV_mesh-grants_memberships.$KV.mesh-grants_memberships.a.restic") {
|
||||
t.Errorf("the runtime hears mesh.assignment or reads no membership: %v", err)
|
||||
}
|
||||
// 1953 S-age (ADR 0565 §10): a log that says an age keeps its entries that long and no longer; one that says none, as
|
||||
// long as the log (ADR 0297).
|
||||
|
||||
@@ -1075,10 +1075,10 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
||||
}
|
||||
}
|
||||
sub = append(sub, announcing(announced...)...)
|
||||
// Its own membership (ADR 0160): the one subject a runtime derives for itself, heard live. Nothing else's.
|
||||
sub = append(sub, MembershipSubject(p.Node, p.Module))
|
||||
// And from the grants part's state memberships, its own key (novox/hq ADR 0571 clause 7): its one read of it,
|
||||
// since the assignments stream's direct get went with the stream (research 072 row 33).
|
||||
// Its own membership (ADR 0160), from the grants part's state memberships, its own key (novox/hq ADR 0571 clause
|
||||
// 7): its one read of it, since the assignments stream's direct get went with the stream (research 072 row 33),
|
||||
// and no longer heard on its subject, where the grants part stops publishing at the lease write (ADR 0571 clause
|
||||
// 10, issue 774).
|
||||
pub = append(pub, readGrants(GrantsMembershipsBucket, MembershipKey(p.Node, p.Module))...)
|
||||
|
||||
// 1b. The tools it calls, if its manifest says it calls any (novox/hq ADR 0152). The same
|
||||
@@ -1240,10 +1240,10 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Every assigned module's membership on this node (ADR 0160): one per module, heard live and read from the
|
||||
// grants part's state memberships (ADR 0571 clause 7). This node's and no other's — the one token
|
||||
// that varies is the module, so the pattern is the machine's own assignments.
|
||||
sub = append(sub, "mesh.assignment."+p.Node+".*")
|
||||
// Every assigned module's membership on this node (ADR 0160): one per module, read and watched from the
|
||||
// grants part's state memberships (ADR 0571 clause 7), and no longer heard on `mesh.assignment`, where the grants
|
||||
// part stops publishing at the lease write (ADR 0571 clause 10, issue 774). This node's and no other's — the
|
||||
// one token that varies is the module, so the pattern is the machine's own assignments.
|
||||
pub = append(pub, readGrants(GrantsMembershipsBucket, p.Node+".*")...)
|
||||
// And that it is there (novox/hq to-be 45 §3, S11): its own heartbeat, under its machine's
|
||||
// name and no other's, on core NATS like the host's.
|
||||
|
||||
Reference in New Issue
Block a user