The assigner's state and verb placement are assignments, with its events, tools, views and every caller; the assigner copies the retired state at its start (issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
// **The accounts the grants part wants, and the one shape a held account takes** (novox/hq ADR 0561 §1, §2; issue 774).
|
||||
//
|
||||
// The bus composer derives every user from placement, the manifests and the wants. For each account the mesh mints (a
|
||||
// The bus composer derives every user from assignments, the manifests and the wants. For each account the mesh mints (a
|
||||
// module's, a module's process's, a machine's provisioner client's), the grants part writes one key of its state
|
||||
// `accounts`, keyed by the bus user, holding the account's fields that are not secret: what the controller's credential
|
||||
// carries today beside the password. The vault watches that state, mints the user it holds no value for, and keeps the
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/meshbroker"
|
||||
"git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
||||
"git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshnodeenroller"
|
||||
"git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient"
|
||||
"git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault"
|
||||
@@ -57,17 +57,17 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
|
||||
t.Run("every node's compositions are read by their named readers alone and written by their node", everyNodesCompositions)
|
||||
t.Run("a module's ledger is written by its ledger account alone", aLedgerIsWrittenByItsLedgerAccountAlone)
|
||||
t.Run("the bus module's process alone keeps every module's bus state and serves the broker's verbs", theBusModulesProcessKeepsBusState)
|
||||
// ADR 0567 §3, §9: a node reads every key of the seat's placement and its candidates (it composes with the whole
|
||||
// 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"), " ")
|
||||
for bucket, granted := range map[string]bool{PlacementBucket: true, PlacementCandidatesBucket: true,
|
||||
PlacementProposalsBucket: false} {
|
||||
for bucket, granted := range map[string]bool{AssignmentsBucket: true, AssignmentsCandidatesBucket: true,
|
||||
AssignmentsProposalsBucket: false} {
|
||||
if strings.Contains(reads, "$JS.API.DIRECT.GET.KV_"+bucket+".>") != granted || strings.Contains(reads, "$KV."+bucket+".laptop") {
|
||||
t.Errorf("a node is granted %s: %v, want %v", bucket, !granted, granted)
|
||||
}
|
||||
}
|
||||
// It hears the creation of those two buckets, and of no other.
|
||||
if got := NodeComposeAdvisories(); len(got) != 2 || got[0] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_placement" ||
|
||||
if got := NodeComposeAdvisories(); len(got) != 2 || got[0] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_assignments" ||
|
||||
got[1] != "$JS.EVENT.ADVISORY.STREAM.CREATED.KV_mesh-delivery-assigner_candidates" {
|
||||
t.Errorf("a node hears the creation of %v", got)
|
||||
}
|
||||
@@ -513,11 +513,11 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
|
||||
vault := ClaimedSeat{Seat: "mesh-vault", Scope: "mesh", Serves: []string{"get", "accept", "secret-ask", "issued",
|
||||
"rotate", "move-in", "node-key", "own", "move-asks"}}
|
||||
controller := ClaimedSeat{Seat: "mesh-controller", Scope: "mesh", Serves: strings.Split("tools,calls,status,nodes,"+
|
||||
"node,placement-export,modules,seats,builds,plans,delivery-plan,delivery-order,delivery-check,deliver,"+
|
||||
"node,assignments-export,modules,seats,builds,plans,delivery-plan,delivery-order,delivery-check,deliver,"+
|
||||
"delivery-stop,delivery-backups-again,delivery-walks,plan,assign,unassign,pin,unpin,push,rotate,give,issued,"+
|
||||
"secret-move,secret-ask,issue,token,settings,command,queue,cancel,clear,replay,try,kill,pause,resume,hand-act,"+
|
||||
"drill,warranted,hand-acts,durations,conditions,healers,doctor,upgrade,bus,retire,cleanup,judge-directories,"+
|
||||
"root-free,dead-letters,data,build,artifacts,held,collect,move-placement,move-config,mirrors,images", ",")}
|
||||
"root-free,dead-letters,data,build,artifacts,held,collect,move-assignments,move-config,mirrors,images", ",")}
|
||||
at := "nats://10.0.0.1:4222"
|
||||
wanted := map[string]struct {
|
||||
w WantedAccount
|
||||
@@ -790,7 +790,7 @@ func healthChecks(t *testing.T) {
|
||||
}
|
||||
// ADR 0571 clause 1: a read naming another module's log is issued to the reader as a log it reads, never writes.
|
||||
if m := MembershipFor("a", Declared{Module: IndexerModule, Reads: []string{builds.BuildLogRead, "x.y"}},
|
||||
Placements{Logs: map[string]bool{builds.BuildLogRead: true}}); len(m.Logs) != 1 || m.Logs[0].Writes ||
|
||||
Assignments{Logs: map[string]bool{builds.BuildLogRead: true}}); len(m.Logs) != 1 || m.Logs[0].Writes ||
|
||||
m.Logs[0].Stream != "LOG_mesh-delivery-builder_build-log" || m.Logs[0].Name != builds.BuildLogRead {
|
||||
t.Errorf("the indexer's membership issues the logs %+v", m.Logs)
|
||||
}
|
||||
@@ -873,7 +873,7 @@ func healthChecks(t *testing.T) {
|
||||
{"a flat event of a node seat held by a module is said flat", Declared{Module: "pause", Holds: []Seat{{Name: "node-power",
|
||||
Scope: "node", Emits: []string{"slept"}}}}, "mesh.seat.node-power.event.slept", true},
|
||||
} {
|
||||
m := MembershipFor("a", c.d, Placements{})
|
||||
m := MembershipFor("a", c.d, Assignments{})
|
||||
listed := m.SeatTraffic != nil && slices.ContainsFunc(m.SeatTraffic.Publish, func(p string) bool { return p == c.subject })
|
||||
perms, err := PermissionsFor(Principal{Kind: KindNodeTools, Node: "a", Carries: []Declared{c.d}})
|
||||
if err != nil {
|
||||
@@ -1019,7 +1019,7 @@ func everyNodesCompositions(t *testing.T) {
|
||||
!MayPublish(runner, "$JS.API.DIRECT.GET.KV_composed-two.$KV.composed-two.two.node") {
|
||||
t.Errorf("the runtime carrying the provisioner cannot read every node's compositions")
|
||||
}
|
||||
m := MembershipFor("one", Declared{Module: "mesh-delivery-view", Reads: []string{ComposedRead}}, Placements{})
|
||||
m := MembershipFor("one", Declared{Module: "mesh-delivery-view", Reads: []string{ComposedRead}}, Assignments{})
|
||||
if !slices.ContainsFunc(m.State, func(s StateIssued) bool {
|
||||
return s.Name == ComposedRead && s.PerNode && s.Bucket == ComposedBucketPrefix && !s.Writes
|
||||
}) {
|
||||
|
||||
@@ -22,7 +22,7 @@ func theGrantsPartCreatesOnlyDeclaredStreams(t *testing.T) {
|
||||
Streams: []ModuleStream{{Module: "mesh-delivery-builder", Name: "BUILDASKS", Events: []string{"ask"}}}},
|
||||
{Module: GrantsModule, State: []Bucket{{Module: GrantsModule, Name: "assertions"}}, Reads: []string{VaultBusAccounts}},
|
||||
{Module: "node-tools", ToolRunner: true},
|
||||
// The node-engine's manifest as a placement names it (issue 774, met twice on 2026-10-30): no account of its
|
||||
// The node-engine's manifest as assignments names it (issue 774, met twice on 2026-10-30): no account of its
|
||||
// own, so the runner would carry it, and its state is the node's to write alone.
|
||||
{Module: EngineModule, NoAccount: true, Consumes: []string{"restic.backed-up"},
|
||||
State: []Bucket{{Module: EngineModule, Name: "health"}, {Module: EngineModule, Name: EngineActsState}}},
|
||||
|
||||
@@ -119,7 +119,7 @@ func ComposedFor(f UserListFixture) (string, []string, error) {
|
||||
|
||||
// MembershipsFor is every membership a fixture's records issue, by `<node>.<module>`, as the composer makes them.
|
||||
func MembershipsFor(f UserListFixture) map[string]Membership {
|
||||
where := PlacementsOf(f.Records, f.Records.Interchangeable)
|
||||
where := AssignmentsOf(f.Records, f.Records.Interchangeable)
|
||||
out := map[string]Membership{}
|
||||
for node, ds := range f.Records.Assigned {
|
||||
for _, d := range ds {
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
// grant is the same composition read the other way. The shape issued today is the shape the mesh
|
||||
// already had, so nothing moves when a membership first arrives; only who decides it moves.
|
||||
|
||||
// Placements is where every module runs, for deciding which instance answers for the module.
|
||||
type Placements struct {
|
||||
// Assignments is where every module runs, for deciding which instance answers for the module.
|
||||
type Assignments struct {
|
||||
// Nodes is each module's machines.
|
||||
Nodes map[string][]string
|
||||
// Interchangeable is each module whose definition says its instances are the same anywhere,
|
||||
@@ -33,14 +33,14 @@ type Placements struct {
|
||||
// plain subject: when it is the only instance, or when the definition says instances are
|
||||
// interchangeable. A stateful module on two machines gets only its machines' subjects, so a call
|
||||
// that names none reaches nothing rather than the wrong store.
|
||||
func (p Placements) AnswersForTheModule(module string) bool {
|
||||
func (p Assignments) AnswersForTheModule(module string) bool {
|
||||
return len(p.Nodes[module]) <= 1 || p.Interchangeable[module]
|
||||
}
|
||||
|
||||
// MembershipFor composes one assignment's membership from what it declared and where everything
|
||||
// runs. The subjects are the ones PermissionsFor grants, derived here once more only until the
|
||||
// grant itself is read from the membership — which is the next step, not this one.
|
||||
func MembershipFor(node string, d Declared, where Placements) Membership {
|
||||
func MembershipFor(node string, d Declared, where Assignments) Membership {
|
||||
own := "mesh.mod." + d.Module
|
||||
m := Membership{
|
||||
Node: node, Module: d.Module,
|
||||
@@ -101,9 +101,9 @@ func MembershipFor(node string, d Declared, where Placements) Membership {
|
||||
return m
|
||||
}
|
||||
|
||||
// PlacementsOf reads where everything runs from the records the bus's accounts are composed from.
|
||||
func PlacementsOf(r Records, interchangeable map[string]bool) Placements {
|
||||
p := Placements{Nodes: map[string][]string{}, Interchangeable: interchangeable, Logs: DeclaredLogs(r)}
|
||||
// AssignmentsOf reads where everything runs from the records the bus's accounts are composed from.
|
||||
func AssignmentsOf(r Records, interchangeable map[string]bool) Assignments {
|
||||
p := Assignments{Nodes: map[string][]string{}, Interchangeable: interchangeable, Logs: DeclaredLogs(r)}
|
||||
for node, declared := range r.Assigned {
|
||||
for _, d := range declared {
|
||||
p.Nodes[d.Module] = append(p.Nodes[d.Module], node)
|
||||
@@ -151,7 +151,7 @@ func PlacementsOf(r Records, interchangeable map[string]bool) Placements {
|
||||
// routerSeat is the seat the router of asks holds (novox/hq ADR 0259 §3).
|
||||
const routerSeat = "operator-channel"
|
||||
|
||||
// placedCapabilities is what a kind's claim promises, as far as its placement lets the router believe it
|
||||
// placedCapabilities is what a kind's claim promises, as far as its assignments lets the router believe it
|
||||
// (novox/hq ADR 0259 §8): `verified-sender` only from a holder that runs as an account of its own, on a bus
|
||||
// account of its own — never one the machine's runtime carries as the operator's account — and only while
|
||||
// its machine and the router's were root-free when composed (rootFree; the review of 2026-10-09, H3). The
|
||||
|
||||
@@ -15,20 +15,20 @@ const (
|
||||
// EnrolmentModule is the module holding the seat mesh-node-enroller.
|
||||
EnrolmentModule = "mesh-enrolment"
|
||||
|
||||
// PlacementBucket and SeatHoldersBucket are the assigner's states placement and seat-holders (reads:
|
||||
// mesh-delivery-assigner.placement; the seat-holders state is read as a named reader), PlacementRead the address of the first.
|
||||
PlacementBucket = AssignerModule + "_placement"
|
||||
PlacementRead = AssignerModule + ".placement"
|
||||
// AssignmentsBucket and SeatHoldersBucket are the assigner's states assignments and seat-holders (reads:
|
||||
// mesh-delivery-assigner.assignments; the seat-holders state is read as a named reader), AssignmentsRead the address of the first.
|
||||
AssignmentsBucket = AssignerModule + "_assignments"
|
||||
AssignmentsRead = AssignerModule + ".assignments"
|
||||
SeatHoldersRead = AssignerModule + ".seat-holders"
|
||||
SeatHoldersBucket = AssignerModule + "_seat-holders"
|
||||
|
||||
AssignerWantsBucket = AssignerModule + "_wants"
|
||||
PlacementCandidatesBucket = AssignerModule + "_candidates"
|
||||
PlacementProposalsBucket = AssignerModule + "_placement-proposals"
|
||||
CandidatesBucket = IndexerModule + "_candidates"
|
||||
ModulesBucket = IndexerModule + "_modules"
|
||||
ManifestsBucket = IndexerModule + "_manifests"
|
||||
VerdictsBucket = IndexerModule + "_verdicts"
|
||||
AssignerWantsBucket = AssignerModule + "_wants"
|
||||
AssignmentsCandidatesBucket = AssignerModule + "_candidates"
|
||||
AssignmentsProposalsBucket = AssignerModule + "_assignments-proposals"
|
||||
CandidatesBucket = IndexerModule + "_candidates"
|
||||
ModulesBucket = IndexerModule + "_modules"
|
||||
ManifestsBucket = IndexerModule + "_manifests"
|
||||
VerdictsBucket = IndexerModule + "_verdicts"
|
||||
|
||||
EnrolmentsState = "enrolments"
|
||||
EnrolmentsBucket = EnrolmentModule + "_" + EnrolmentsState
|
||||
|
||||
@@ -32,7 +32,7 @@ func namesAreTheManifests(t *testing.T) {
|
||||
{"reads", HaveBucket[:strings.Index(HaveBucket, "_")] + ".have", HaveBucket},
|
||||
{"reads", NodeLoginsRead, NodeLoginsBucket},
|
||||
{"reads", EnrolmentsRead, EnrolmentsBucket},
|
||||
{"invokes", PlacementRead, PlacementBucket},
|
||||
{"invokes", AssignmentsRead, AssignmentsBucket},
|
||||
{"invokes", AssignerModule + ".wants", AssignerWantsBucket},
|
||||
{"invokes", IndexerModule + ".modules", ModulesBucket},
|
||||
{"invokes", IndexerModule + ".manifests", ManifestsBucket},
|
||||
|
||||
@@ -740,7 +740,7 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
||||
// its own modules reads the other nodes' public records, which its firewall and resolver need (ADR 0547 §2). Read
|
||||
// only: each node still writes its own keys alone.
|
||||
pub = append(pub, readGrants(HaveBucket, "*."+HaveNodeWord)...)
|
||||
// And what it composes its own modules from (ADR 0547 §2, ADR 0567 §9): the seat mesh-placement's placement and
|
||||
// And what it composes its own modules from (ADR 0547 §2, ADR 0567 §9): the seat mesh-module-assigner's assignments and
|
||||
// candidates, the indexer's modules and manifests, the builder's builds (the manifest of a build its want names), and mesh-config's resolved values of
|
||||
// its own node alone. Read only.
|
||||
pub = append(pub, NodeComposeReads(p.Node)...)
|
||||
@@ -779,7 +779,7 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
||||
}
|
||||
sub = append(sub, subject)
|
||||
}
|
||||
// And the creation of the seat mesh-placement's two states it composes from (ADR 0567 §9), so a node agent started
|
||||
// And the creation of the seat mesh-module-assigner's two states it composes from (ADR 0567 §9), so a node agent started
|
||||
// before they exist binds each when the bus says it was made, with no timer; no other bucket's.
|
||||
sub = append(sub, NodeComposeAdvisories()...)
|
||||
// And, while the vault serves it, the vault's move-in in its own name and nobody else's (issue 676): the
|
||||
@@ -1535,7 +1535,7 @@ func CreateConsumerGrants(consumers []ModuleConsumer) []string {
|
||||
}
|
||||
|
||||
// GrantsModule is the one module whose principal publishes the modules' memberships (novox/hq ADR 0547 §5): the grants
|
||||
// part, which composes them from the manifests and placement. It defines no stream and is granted no other subject here.
|
||||
// part, which composes them from the manifests and assignments. It defines no stream and is granted no other subject here.
|
||||
const GrantsModule = "mesh-grants"
|
||||
|
||||
// The grants part's state memberships (novox/hq ADR 0571 clause 7): one key per assignment, `<node>.<module>`, its
|
||||
|
||||
@@ -31,22 +31,22 @@ const ProvisionerSecret = "provisioner-broker"
|
||||
var ConfigResolvedBucket = BucketName("mesh-config", "resolved")
|
||||
|
||||
// ProvisionerReads are the domains' buckets a provisioner client reads its own machine's keys of, each keyed
|
||||
// `<node>.…` (research 051): placement, the controller's assignments mirror (`<node>.<module>`), whose value carries
|
||||
// `<node>.…` (research 051): assignments, the controller's assignments mirror (`<node>.<module>`), whose value carries
|
||||
// the provisioner's plan of a module handed to it (inventory.Provisioning, design 57 P5); and config,
|
||||
// mesh-config's resolved values; and the seat mesh-provisioner's plans of its machine, `<node>.<module>` (novox/hq ADR
|
||||
// 0570 clause 2). **Named one by one**, as the controller's asks are: a domain that moves to the
|
||||
// provisioner adds its bucket here, and a bucket not named is one the client cannot read.
|
||||
var ProvisionerReads = []string{ConfigResolvedBucket}
|
||||
|
||||
// ProvisionerReadsWhole are the buckets a provisioner client reads every key of: the seat mesh-placement's placement,
|
||||
// ProvisionerReadsWhole are the buckets a provisioner client reads every key of: the seat mesh-module-assigner's assignments,
|
||||
// where a provider's consumers are placed on their own machines, which the provider machine's client reads to fill
|
||||
// ${consumers:<provision>} with each one's credential (novox/hq ADR 0373, issue 679; ADR 0567, issue 1126: the
|
||||
// controller's assignments mirror it read before is gone). It holds placement, never a secret's value; the client
|
||||
// controller's assignments mirror it read before is gone). It holds assignments, never a secret's value; the client
|
||||
// writes none of it. **The seat's plans are read whole for the same
|
||||
// reason** (2026-10-30 recovery, issue 774): the client V3 watches every machine's plans to bind a provider's
|
||||
// consumers, and granted only its own machine's keys it read none. ADR 0570 clause 3 has providers act on the seat's
|
||||
// provision keys instead; once the client does, plans go back to its own machine's keys.
|
||||
var ProvisionerReadsWhole = []string{PlacementBucket, ProvisionerPlansBucket}
|
||||
var ProvisionerReadsWhole = []string{AssignmentsBucket, ProvisionerPlansBucket}
|
||||
|
||||
// BoundState is a providing module's state of entries, one per consumer (mesh-sdk go/provider, issue 678): a
|
||||
// consumer's machine's client reads its own keys of it, `<node>.<module>.<provision>[.<local>]`, to fill
|
||||
|
||||
@@ -334,24 +334,24 @@ func stateNames(buckets []Bucket) []string {
|
||||
const ResolvedBucket = "mesh-config_resolved"
|
||||
|
||||
// NodeComposeReads is what a node's agent reads to compose its own modules (novox/hq ADR 0547 §2, ADR 0567 §3, §9,
|
||||
// issue 1126): every key of the seat mesh-placement's placement and candidates (both the assigner's states), of the
|
||||
// issue 1126): every key of the seat mesh-module-assigner's assignments and candidates (both the assigner's states), of the
|
||||
// indexer's modules and manifests and of the builder's builds, and of mesh-config's resolved values only its own node's,
|
||||
// `<node>.>`. Every candidate key, not only `*.<node>`: a node answering its candidate composes with the whole
|
||||
// proposal laid over (the seed places every node at once, and a binding crosses nodes), and nothing in a placement is
|
||||
// proposal laid over (the seed places every node at once, and a binding crosses nodes), and nothing in assignments is
|
||||
// secret. Reads only; the node is checked as every subject token is by the caller's principal.
|
||||
func NodeComposeReads(node string) []string {
|
||||
var out []string
|
||||
for _, bucket := range []string{PlacementBucket, PlacementCandidatesBucket, ModulesBucket,
|
||||
for _, bucket := range []string{AssignmentsBucket, AssignmentsCandidatesBucket, ModulesBucket,
|
||||
ManifestsBucket, builds.BuilderBuildsBucket} {
|
||||
out = append(out, readGrants(bucket, "")...)
|
||||
}
|
||||
return append(out, readGrants(ResolvedBucket, node+".>")...)
|
||||
}
|
||||
|
||||
// NodeComposeAdvisories are the stream-created advisories of the seat mesh-placement's placement and candidates
|
||||
// NodeComposeAdvisories are the stream-created advisories of the seat mesh-module-assigner's assignments and candidates
|
||||
// (ADR 0567 §9): what a node agent hears to bind each once it is made. Those two and no wider.
|
||||
func NodeComposeAdvisories() []string {
|
||||
return []string{StreamCreatedAdvisory(PlacementBucket), StreamCreatedAdvisory(PlacementCandidatesBucket)}
|
||||
return []string{StreamCreatedAdvisory(AssignmentsBucket), StreamCreatedAdvisory(AssignmentsCandidatesBucket)}
|
||||
}
|
||||
|
||||
// ServedReadGrants is the read of each key of mesh-config's resolved values named, `<node>.<module>.<key>`
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -3,7 +3,7 @@
|
||||
"rows": [
|
||||
{
|
||||
"name": "module",
|
||||
"held": "{\"url\":\"nats://10.0.0.1:4222\",\"fingerprint\":\"ab:cd\",\"node\":\"laptop\",\"module\":\"cap-bot\",\"user\":\"laptop.cap-bot\",\"password\":\"pw-module\",\"claims\":[{\"seat\":\"mesh-vault\",\"scope\":\"mesh\",\"serves\":[\"get\",\"accept\",\"secret-ask\",\"issued\",\"rotate\",\"move-in\",\"node-key\",\"own\",\"move-asks\"]},{\"seat\":\"node-service-manager\",\"scope\":\"node\",\"serves\":[\"status\"]},{\"seat\":\"mesh-controller\",\"scope\":\"mesh\",\"serves\":[\"tools\",\"calls\",\"status\",\"nodes\",\"node\",\"placement-export\",\"modules\",\"seats\",\"builds\",\"plans\",\"delivery-plan\",\"delivery-order\",\"delivery-check\",\"deliver\",\"delivery-stop\",\"delivery-backups-again\",\"delivery-walks\",\"plan\",\"assign\",\"unassign\",\"pin\",\"unpin\",\"push\",\"rotate\",\"give\",\"issued\",\"secret-move\",\"secret-ask\",\"issue\",\"token\",\"settings\",\"command\",\"queue\",\"cancel\",\"clear\",\"replay\",\"try\",\"kill\",\"pause\",\"resume\",\"hand-act\",\"drill\",\"warranted\",\"hand-acts\",\"durations\",\"conditions\",\"healers\",\"doctor\",\"upgrade\",\"bus\",\"retire\",\"cleanup\",\"judge-directories\",\"root-free\",\"dead-letters\",\"data\",\"build\",\"artifacts\",\"held\",\"collect\",\"move-placement\",\"move-config\",\"mirrors\",\"images\"]},{\"seat\":\"no-such-seat\",\"scope\":\"node\"}]}"
|
||||
"held": "{\"url\":\"nats://10.0.0.1:4222\",\"fingerprint\":\"ab:cd\",\"node\":\"laptop\",\"module\":\"cap-bot\",\"user\":\"laptop.cap-bot\",\"password\":\"pw-module\",\"claims\":[{\"seat\":\"mesh-vault\",\"scope\":\"mesh\",\"serves\":[\"get\",\"accept\",\"secret-ask\",\"issued\",\"rotate\",\"move-in\",\"node-key\",\"own\",\"move-asks\"]},{\"seat\":\"node-service-manager\",\"scope\":\"node\",\"serves\":[\"status\"]},{\"seat\":\"mesh-controller\",\"scope\":\"mesh\",\"serves\":[\"tools\",\"calls\",\"status\",\"nodes\",\"node\",\"assignments-export\",\"modules\",\"seats\",\"builds\",\"plans\",\"delivery-plan\",\"delivery-order\",\"delivery-check\",\"deliver\",\"delivery-stop\",\"delivery-backups-again\",\"delivery-walks\",\"plan\",\"assign\",\"unassign\",\"pin\",\"unpin\",\"push\",\"rotate\",\"give\",\"issued\",\"secret-move\",\"secret-ask\",\"issue\",\"token\",\"settings\",\"command\",\"queue\",\"cancel\",\"clear\",\"replay\",\"try\",\"kill\",\"pause\",\"resume\",\"hand-act\",\"drill\",\"warranted\",\"hand-acts\",\"durations\",\"conditions\",\"healers\",\"doctor\",\"upgrade\",\"bus\",\"retire\",\"cleanup\",\"judge-directories\",\"root-free\",\"dead-letters\",\"data\",\"build\",\"artifacts\",\"held\",\"collect\",\"move-assignments\",\"move-config\",\"mirrors\",\"images\"]},{\"seat\":\"no-such-seat\",\"scope\":\"node\"}]}"
|
||||
},
|
||||
{
|
||||
"name": "module-no-claims-no-fingerprint",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -75,7 +75,7 @@ type Declared struct {
|
||||
// 0495 clause 3, issue 1790): granted nothing, kept to be said.
|
||||
ReadsRefused []string
|
||||
// ReadLogs are, of its reads, those naming another module's log (`<module>.<log>`, novox/hq ADR 0571 clause 1): filled
|
||||
// by Users and PlacementsOf from the logs the modules placed anywhere declare, never by a caller.
|
||||
// by Users and AssignmentsOf from the logs the modules placed anywhere declare, never by a caller.
|
||||
ReadLogs []string
|
||||
// KeyedReads are keys of other modules' state it reads, each one key alone: what a seat's holder is
|
||||
// granted for the pieces the modules beside it offer (novox/hq ADR 0260).
|
||||
@@ -231,7 +231,7 @@ func Users(r Records) ([]Principal, error) {
|
||||
}
|
||||
}
|
||||
for _, d := range r.Assigned[node] {
|
||||
// **The node-engine's manifest is no placed module's** (novox/hq ADR 0563 §2, §3; issue 774): where a placement
|
||||
// **The node-engine's manifest is no placed module's** (novox/hq ADR 0563 §2, §3; issue 774): where assignments
|
||||
// names it, it adds no user, no process and nothing the runner carries; its grants are the node's account's.
|
||||
if !Composed(d) {
|
||||
continue
|
||||
@@ -541,7 +541,7 @@ func boundBucketsOf(r Records) []string {
|
||||
}
|
||||
|
||||
// Composed says a placed module is composed as one: its own user, its processes and what the runner carries of it.
|
||||
// The node-engine's manifest (EngineModule) is not, though a placement may name it: each node's engine speaks as the
|
||||
// The node-engine's manifest (EngineModule) is not, though assignments may name it: each node's engine speaks as the
|
||||
// node's account, which alone writes the engine's state under its own key (engineGrants, novox/hq ADR 0563 §2, §3), and
|
||||
// Records.Engine creates that state. Granted to another account it is a second writer, and the writers table refuses
|
||||
// the whole list (issue 774: met for mesh-host_acts and then mesh-host_health, 2026-10-30).
|
||||
|
||||
Reference in New Issue
Block a user