AM-D5: a per-machine state reaches its machine's keys below it
A per-machine state's account reaches its machine's key and the keys below it (mesh-sdk PerMachineKeys), no other machine's, so the tool runner's approvals keep <node>.ask.<id> and <node>.act.<digest>; the membership names the machine's key in the same form. Builds: design 46 7-approval-mark §13 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -62,6 +62,7 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
|
||||
t.Run("the bus module's process alone keeps every module's bus state and serves the broker's verbs", theBusModulesProcessKeepsBusState)
|
||||
t.Run("a process serving a verb is granted its calls and chunk subjects, and the reverse goes to an account naming it", chunkSubjectsFollowTheAnswerer)
|
||||
t.Run("the tool runner's runtime hears every event subject and nothing wider", theToolRunnerHearsEveryEvent)
|
||||
t.Run("a per-machine state reaches its machine's keys below it and no other machine's", aPerMachineStateReachesItsMachinesKeys)
|
||||
// ADR 0567 §3, §9: a node reads every key of the seat's assignments and its candidates (it composes with the whole
|
||||
// proposal laid over), and none of its open proposals; it writes neither.
|
||||
reads := strings.Join(NodeComposeReads("laptop"), " ")
|
||||
@@ -1516,3 +1517,44 @@ func theToolRunnerHearsEveryEvent(t *testing.T) {
|
||||
t.Errorf("%d runtimes carry the tool runner, want 1", runners)
|
||||
}
|
||||
}
|
||||
|
||||
// AM-D5 (design 46 approval mark §13): a per-machine state reaches its machine's key and the keys below it, `<node>.>`,
|
||||
// and no other machine's, on the account and in the membership alike.
|
||||
func aPerMachineStateReachesItsMachinesKeys(t *testing.T) {
|
||||
runner := func() Declared {
|
||||
return Declared{Module: "node-tools", ToolRunner: true,
|
||||
State: []Bucket{{Module: "node-tools", Name: "approvals", PerMachine: true, KeyTTL: true, TTLSeconds: 172800}}}
|
||||
}
|
||||
r := Records{Nodes: []string{"a", "b"}, Assigned: map[string][]Declared{"a": {runner()}, "b": {runner()}}}
|
||||
users, err := Users(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const bucket = "node-tools_approvals"
|
||||
found := false
|
||||
for _, u := range users {
|
||||
if u.Username() != "a.node-tools" {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
p, err := PermissionsFor(u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for key, reach := range map[string]bool{"a": true, "a.ask.1": true, "a.act.d": true, "b": false, "b.ask.1": false, "ab.ask.1": false} {
|
||||
if MayPublish(p, "$KV."+bucket+"."+key) != reach {
|
||||
t.Errorf("a's runner writes %s's key %s: %v", bucket, key, !reach)
|
||||
}
|
||||
if MayPublish(p, "$JS.API.DIRECT.GET.KV_"+bucket+".$KV."+bucket+"."+key) != reach {
|
||||
t.Errorf("a's runner reads %s's key %s: %v", bucket, key, !reach)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("no account a.node-tools composed")
|
||||
}
|
||||
// The membership names the machine's key, which reaches the keys below it in the same form (mesh-sdk membership).
|
||||
if issued := stateIssuedFor(runner(), "a"); len(issued) != 1 || issued[0].Key != "a" || issued[0].Bucket != bucket {
|
||||
t.Errorf("a's membership issues %+v", issued)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package buscompose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sdkbc "git.novox.be/novox/mesh-sdk/go/buscompose"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -36,8 +37,8 @@ type Bucket struct {
|
||||
History int
|
||||
// TTLSeconds is how long a value lives; zero is until replaced or deleted.
|
||||
TTLSeconds int
|
||||
// PerMachine says each machine's instance writes only the key named for its machine (novox/hq ADR
|
||||
// 0260), and is granted that key and no other.
|
||||
// PerMachine says each machine's instance writes only the key named for its machine and the keys below it (novox/hq
|
||||
// ADR 0260; design 46 approval mark §13), and is granted those and no other machine's.
|
||||
PerMachine bool
|
||||
// KeyTTL says the bucket allows a time to live per key (novox/hq ADR 0433 §1): its owner writes each key with its
|
||||
// own, and the server writes a delete marker, kept KeyTTLMarkerTTL, when it ends.
|
||||
@@ -166,8 +167,11 @@ func stateGrants(a stateAccess) []string {
|
||||
if machineKey == "" {
|
||||
continue // a machine with no usable name reaches none of it
|
||||
}
|
||||
read(bucket, machineKey)
|
||||
out = append(out, "$KV."+bucket+"."+machineKey)
|
||||
// Its machine's key and the keys below it, no other machine's (design 46 approval mark §13).
|
||||
for _, k := range sdkbc.PerMachineKeys(machineKey) {
|
||||
read(bucket, k)
|
||||
out = append(out, "$KV."+bucket+"."+k)
|
||||
}
|
||||
continue
|
||||
}
|
||||
read(bucket, "")
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user