No client names a holder: each reader keeps its own constant, held to its manifest (ADR 0625 section 4, issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 18:37:28 +01:00
parent 720efe6339
commit 78a1b5648b
11 changed files with 67 additions and 25 deletions
@@ -85,7 +85,12 @@ const (
// not read here leaves its keys asked, saying its entries are not read. route-proxy (mesh-control) and node-tools
// (mesh-tools) answer route and mcp-endpoint by the provider helper from outside this catalogue (issue 774, sweep of
// 2026-10-30 20:10: their keys stood asked on every node).
var BoundReads = []string{"gitea", "keycloak", "postgres", "route-adapter", "route-proxy", "node-tools", "mesh-vault"}
var BoundReads = []string{"gitea", "keycloak", "postgres", "route-adapter", "route-proxy", "node-tools", vaultModule}
// vaultModule is the holder of the seat mesh-vault, the provider of every credential: named here from this module's own
// manifest (its `reads` has mesh-vault.bound; TestTheManifestDeclaresWhatTheCodeKeeps), since no client names a holder
// (novox/hq ADR 0625 §4).
const vaultModule = "mesh-vault"
// boundRead is a provider's entries as this module reads them.
func boundRead(module string) string { return module + ".bound" }
@@ -299,7 +304,7 @@ func (h *Holder) ComposeNodeWith(ctx context.Context, node string, retireMany bo
continue
}
cred := meshprovisionerclient.Provision{Provision: cp, Local: cl, Consumer: meshprovisionerclient.End{Module: cm, Node: cn},
Provider: meshprovisionerclient.End{Module: meshprovisionerclient.VaultModule, Node: vaultNode}}
Provider: meshprovisionerclient.End{Module: vaultModule, Node: vaultNode}}
if cn == node {
if _, seen := needed[ck]; !seen {
needed[ck] = cred
@@ -404,7 +409,7 @@ func (h *Holder) providerPlan(p catalogue.PlanProvision, node string, here map[s
// vaultNode is the node mesh-vault is placed on: the provider of every credential.
func (h *Holder) vaultNode() string {
nodes := h.placedOn(meshprovisionerclient.VaultModule)
nodes := h.placedOn(vaultModule)
if len(nodes) == 0 {
return ""
}
@@ -134,9 +134,9 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
published(t, mem, c)
vaultManifest := `{"module":"mesh-vault","version":"1","state":["bound"]}` // the vault keeps its outcomes as entries
vaultDigest := catalogclient.ManifestDigest([]byte(vaultManifest))
vaultValue, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: meshprovisionerclient.VaultModule, Manifest: vaultManifest})
vaultValue, _ := catalogclient.EncodeManifest(catalog.ManifestsByDigestValue{Module: vaultModule, Manifest: vaultManifest})
mem.write(t, ManifestsRead, mustKey(catalogclient.ManifestsKey(vaultDigest)), string(vaultValue))
mem.write(t, ModulesRead, meshprovisionerclient.VaultModule, `{"v":1,"none2":"the test's","manifest":"`+vaultDigest+`"}`)
mem.write(t, ModulesRead, vaultModule, `{"v":1,"none2":"the test's","manifest":"`+vaultDigest+`"}`)
for state, keys := range mem.states {
for key, raw := range keys {
if err := s.Put(state, key, json.RawMessage(raw)); err != nil {
@@ -217,7 +217,7 @@ func TestTheHolderPlansProvidesAndAsksOnTheBus(t *testing.T) {
}
// The providers' outcomes: the vault provides the credential where restic has one, postgres runs healthy.
for _, dep := range state(key).DependsOn {
if err := s.Put(boundRead(meshprovisionerclient.VaultModule), dep, map[string]any{"state": "provided"}); err != nil {
if err := s.Put(boundRead(vaultModule), dep, map[string]any{"state": "provided"}); err != nil {
t.Fatal(err)
}
}
@@ -334,6 +334,10 @@ func TestTheManifestDeclaresWhatTheCodeKeeps(t *testing.T) {
t.Errorf("the manifest lists %s, which is not served", tool)
}
}
// The vault the holder places credentials on is the holder its manifest reads the bound outcomes of.
if !slices.Contains(m.Reads, vaultModule+".bound") || !slices.Contains(BoundReads, vaultModule) {
t.Errorf("the vault is %q: the manifest reads %v and the holder reads the bound states of %v", vaultModule, m.Reads, BoundReads)
}
// The builds read serves only haveOutcome's offer check (ADR 0603 clause 5: which manifest the build a provider runs
// was made from) and makes no plan, so ADR 0593 §8 ("it composes nothing") holds.
// The assigner's placement and wants and the indexer's modules and manifests are asked once of their owners' read
@@ -12,7 +12,6 @@ import (
"os"
"time"
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
@@ -78,8 +77,8 @@ func NewChecker() *Checker {
// self-check's own (first-view-unanswered), written into its state findings.
func newViews() *views.Reader {
r := &views.Reader{Module: Module, Logf: logf, Findings: meshconditionsclient.FindingWriter{State: stdio.State("findings")}, Acts: meshhandactsclient.Recorder{Ask: stdio.Ask}}
r.Follow(assignerclient.Module, placing.StatePlacement, PlacementTool)
r.Follow(assignerclient.Module, placing.StateSeatHolders, SeatHoldersTool)
r.Follow(assignerModule, placing.StatePlacement, PlacementTool)
r.Follow(assignerModule, placing.StateSeatHolders, SeatHoldersTool)
r.Follow(indexerModule, "modules", ModulesTool)
r.Follow(indexerModule, "manifests", ManifestsTool)
return r
@@ -6,6 +6,8 @@ import (
"path/filepath"
"slices"
"testing"
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
)
type manifest struct {
@@ -69,6 +71,14 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) {
if !slices.Equal(m.Consume, Consumes) {
t.Errorf("consumes %v, want the views families the probes follow, %v", m.Consume, Consumes)
}
for _, view := range []string{assignerModule + ".views." + placing.StatePlacement, assignerModule + ".views." + placing.StateSeatHolders} {
if !slices.Contains(m.Consume, view) {
t.Errorf("the probes follow %s, the manifest consumes %v", view, m.Consume)
}
}
if PlacementRead != "mesh-delivery-assigner.placement" || SeatHoldersRead != "mesh-delivery-assigner.seat-holders" {
t.Errorf("the states are read as %s and %s", PlacementRead, SeatHoldersRead)
}
states := map[string]int{}
for _, s := range m.State {
var d struct {
@@ -26,8 +26,8 @@ import (
// The states the machine probes read (declared under `reads`), and the verb D8 asks each node.
const (
PlacementRead = assignerclient.Module + "." + "placement"
SeatHoldersRead = assignerclient.SeatHoldersRead
PlacementRead = assignerModule + "." + placing.StatePlacement
SeatHoldersRead = assignerModule + "." + placing.StateSeatHolders
PresenceRead = watchmanModule + ".presence"
BannedSeat = "node-intrusion-prevention"
BannedVerb = "seat:" + BannedSeat + ".banned"
@@ -226,7 +226,9 @@ func probeBans(ctx context.Context, c *Checker) ([]meshconditionsclient.Observat
// The holders whose states the probes read, one constant each (a reader names its holder once; ADR 0634 §4).
const (
watchmanModule = "mesh-delivery-watchman"
watchmanModule = "mesh-delivery-watchman"
// assignerModule is the holder of the seat mesh-module-assigner, named from this module's manifest (consumes: its views).
assignerModule = "mesh-delivery-assigner"
indexerModule = "mesh-delivery-indexer"
nodeEngineModule = "mesh-host"
)
@@ -122,12 +122,12 @@ func (v *Vault) outcomes() (map[string]BoundOutcome, error) {
consumer := meshprovisionerclient.End{Module: a.Module, Node: a.Node}
for _, p := range a.Provisioning.Provisions {
provider := meshprovisionerclient.End{Module: p.ProviderModule, Node: p.Provider}
if p.ProviderModule == meshprovisionerclient.VaultModule && p.Provision == "secret" {
if p.ProviderModule == vaultModule && p.Provision == "secret" {
// The vault's own offer: a value it hands the consumer's client on request (`get`), which it serves as
// soon as it runs.
key, err := meshprovisionerclient.Key(a.Node, a.Module, p.Provision, p.Local)
if err == nil {
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: meshprovisionerclient.VaultModule, Provision: p.Provision,
out[key] = BoundOutcome{Provider: p.Provider, ProviderModule: vaultModule, Provision: p.Provision,
Consumer: a.Node, Module: a.Module, Local: p.Local, State: meshprovisionerclient.Provided,
Why: "the vault hands it to the consumer's client on request"}
}
@@ -143,7 +143,7 @@ func (v *Vault) outcomes() (map[string]BoundOutcome, error) {
if err != nil || name == "" {
continue
}
o := BoundOutcome{Provider: p.Provider, ProviderModule: meshprovisionerclient.VaultModule, Provision: cp, Consumer: cn,
o := BoundOutcome{Provider: p.Provider, ProviderModule: vaultModule, Provision: cp, Consumer: cn,
Module: cm, Local: cl, VaultName: name}
rec, held, err := v.Store.Read(name)
switch {
+1 -1
View File
@@ -866,6 +866,6 @@ func (e *Env) credentialsState() []PairOutcome {
// §1, §6): a provision `secret-<provision>` whose provider is the vault, in state asked.
func credentialAsked(raw []byte) bool {
p, err := meshprovisionerclient.Decode(raw)
return err == nil && p.State == meshprovisionerclient.Asked && p.Provider.Module == meshprovisionerclient.VaultModule &&
return err == nil && p.State == meshprovisionerclient.Asked && p.Provider.Module == vaultModule &&
strings.HasPrefix(p.Provision, catalogue.CredentialProvisionPrefix)
}
@@ -16,9 +16,6 @@ import (
"strings"
)
// PlacementRead is the seat mesh-module-assigner's placement, as the vault's manifest reads it (mesh-sdk's contract).
const PlacementRead = assignerclient.PlacementRead
// PlacementReader reads the seat's placement: every key, and one key's value (nil when there is none).
type PlacementReader struct {
Keys func() ([]string, error)
@@ -1,11 +1,15 @@
package main
import (
"encoding/json"
"os"
"slices"
"strings"
"testing"
vaultclient "git.novox.be/novox/mesh-contracts/identity-and-access/go/clients/meshvaultclient"
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
placing "git.novox.be/novox/mesh-contracts/placement/go/seats/meshmoduleassigner"
"strings"
"testing"
)
// placementsOf is the seat mesh-module-assigner's placement of each node, as the vault reads it.
@@ -67,3 +71,22 @@ func TestTheVaultReadsWhereModulesStandFromPlacement(t *testing.T) {
t.Errorf("two bus nodes: %v", err)
}
}
// The assigner's tool and views family the vault names are the ones its manifest invokes and consumes; no client names a
// holder (novox/hq ADR 0625 §4).
func TestThePlacementNamesAreTheManifests(t *testing.T) {
raw, err := os.ReadFile("../../module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Invokes []string `json:"invokes"`
Consumes []string `json:"consumes"`
}
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
if !slices.Contains(m.Invokes, PlacementTool) || !slices.Contains(m.Consumes, PlacementViewsEvent) {
t.Errorf("the vault asks %s and follows %s, its manifest invokes %v and consumes %v", PlacementTool, PlacementViewsEvent, m.Invokes, m.Consumes)
}
}
@@ -10,7 +10,6 @@ import (
"context"
"encoding/json"
"fmt"
assignerclient "git.novox.be/novox/mesh-contracts/placement/go/clients/meshmoduleassignerclient"
"sort"
"strings"
"time"
@@ -22,8 +21,11 @@ import (
// PlacementTool is the assigner's ordinary read tool of its placement; PlacementViewsEvent the event of its views family.
const (
PlacementTool = assignerclient.Module + ".assigner_read_placement"
PlacementViewsEvent = assignerclient.Module + ".views.placement"
// assignerModule is the holder of the seat mesh-module-assigner, named from this module's manifest (invokes its read tool,
// consumes its views; TestThePlacementNamesAreTheManifests).
assignerModule = "mesh-delivery-assigner"
PlacementTool = assignerModule + ".assigner_read_placement"
PlacementViewsEvent = assignerModule + ".views.placement"
FirstViewsTool = "vault_first_views"
FirstViewAgainTool = "vault_first_view_again"
)
@@ -38,7 +40,7 @@ var placementView *views.Follower
// it is held (a first view's keys too).
func newPlacementView(ask func(key string, body any) (json.RawMessage, error), gone func(key string), changed func(),
findings views.FindingWriter) *views.Follower {
return &views.Follower{Owner: assignerclient.Module, Tool: PlacementTool, State: "placement", Logf: say,
return &views.Follower{Owner: assignerModule, Tool: PlacementTool, State: "placement", Logf: say,
Ask: func(_ context.Context, after string) (views.Answer, error) {
body := map[string]any{"values": true}
if after != "" {