Grant the provisioner client every machine's plans, as it watches them, so g14 and shanks read their plans again (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-sdk@1d9de5dbb63b
mesh/delivery-group group fix/provisioner-reads-every-plan merged: every member is merged
mesh/check being carried to af66ccb8335a on 1d9de5dbb63b | from 42988a734b3d | novox/mesh-sdk#137 in group fix/provisioner-reads-every-plan

The client V3 watches the seat's plans whole to bind a provider's consumers (ADR 0373); the grant gave each machine
only its own keys, so its watch was refused and no files were placed. Quick fix, chosen in a recovery: plans join
the buckets a client reads whole (references, never secret values). Thorough fix, queued: the client fills consumers
from the seat's provision keys (ADR 0570 clause 3) and plans go back to its own machine's keys.
This commit is contained in:
2026-10-30 16:40:46 +01:00
parent 42988a734b
commit af66ccb833
2 changed files with 12 additions and 7 deletions
+6 -4
View File
@@ -658,6 +658,9 @@ func healthChecks(t *testing.T) {
"a.disk-load": {"$KV.disk-load_readings.a.pg.data"}, "a.disk-load": {"$KV.disk-load_readings.a.pg.data"},
"provisioner.a": {"$JS.API.DIRECT.GET.KV_mesh-provisioner_plans.$KV.mesh-provisioner_plans.a.keycloak", "provisioner.a": {"$JS.API.DIRECT.GET.KV_mesh-provisioner_plans.$KV.mesh-provisioner_plans.a.keycloak",
"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_plans.c1.$KV.mesh-provisioner_plans.a.keycloak", "$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_plans.c1.$KV.mesh-provisioner_plans.a.keycloak",
// Every machine's plans, as the client V3 watches them (issue 774, 2026-10-30).
"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_plans.c1.$KV.mesh-provisioner_plans.>",
"$JS.API.DIRECT.GET.KV_mesh-provisioner_plans.$KV.mesh-provisioner_plans.b.keycloak",
// The needs, for the client before V3, until the lease write (review round 3). // The needs, for the client before V3, until the lease write (review round 3).
"$KV." + ProvisionerNeedsBucket + ".a.keycloak.db.x"}, "$KV." + ProvisionerNeedsBucket + ".a.keycloak.db.x"},
// ADR 0571 clause 3: restic's bundle writes each item's last good night as its own state; the self-check reads it. // ADR 0571 clause 3: restic's bundle writes each item's last good night as its own state; the self-check reads it.
@@ -692,10 +695,9 @@ func healthChecks(t *testing.T) {
// Each writes its own state alone, and these users are composed. // Each writes its own state alone, and these users are composed.
"a.disk-load": {"$KV.restic_good.a.pg.data"}, "a.disk-load": {"$KV.restic_good.a.pg.data"},
"a.mesh-delivery-builder": {"$KV.disk-load_readings.a.pg.data"}, "a.mesh-delivery-builder": {"$KV.disk-load_readings.a.pg.data"},
// Another machine's plans are not its to read, nor another machine's needs, which it keeps for the client before // It reads every machine's plans (a provider's consumers are bound there, ADR 0373) but writes none, nor another
// V3 until the lease write. // machine's needs, which it keeps for the client before V3 until the lease write.
"provisioner.a": {"$JS.API.DIRECT.GET.KV_mesh-provisioner_plans.$KV.mesh-provisioner_plans.b.keycloak", "provisioner.a": {"$KV.mesh-provisioner_plans.a.keycloak", "$KV." + ProvisionerNeedsBucket + ".b.keycloak.db.x"},
"$KV.mesh-provisioner_plans.a.keycloak", "$KV." + ProvisionerNeedsBucket + ".b.keycloak.db.x"},
// Only the assigner writes seat-holders. // Only the assigner writes seat-holders.
"a.artifact-store-tools": {"$KV.mesh-delivery-assigner_seat-holders.mesh-broker"}, "a.artifact-store-tools": {"$KV.mesh-delivery-assigner_seat-holders.mesh-broker"},
// Only disk-load writes its readings. // Only disk-load writes its readings.
+6 -3
View File
@@ -39,13 +39,16 @@ var ConfigResolvedBucket = BucketName("mesh-config", "resolved")
// mesh-config's resolved values; and the seat mesh-provisioner's plans of its machine, `<node>.<module>` (novox/hq ADR // 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 // 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. // provisioner adds its bucket here, and a bucket not named is one the client cannot read.
var ProvisionerReads = []string{ConfigResolvedBucket, provisions.PlansBucket} var ProvisionerReads = []string{ConfigResolvedBucket}
// ProvisionerReadsWhole are the buckets a provisioner client reads every key of: the controller's assignments mirror, // ProvisionerReadsWhole are the buckets a provisioner client reads every key of: the controller's assignments mirror,
// where a provider's consumers are bound to it in their own machines' plans, which the provider machine's client // where a provider's consumers are bound to it in their own machines' plans, which the provider machine's client
// reads to fill ${consumers:<provision>} with each one's credential (novox/hq ADR 0373, issue 679). It holds placement // reads to fill ${consumers:<provision>} with each one's credential (novox/hq ADR 0373, issue 679). It holds placement
// and plans, never a secret's value; the client writes none of it. // and plans, never a secret's value; the client writes none of it. **The seat's plans are read whole for the same
var ProvisionerReadsWhole = []string{AssignmentsBucket} // 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{AssignmentsBucket, provisions.PlansBucket}
// BoundState is a providing module's state of entries, one per consumer (mesh-sdk go/provider, issue 678): a // 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 // consumer's machine's client reads its own keys of it, `<node>.<module>.<provision>[.<local>]`, to fill