From af66ccb8335aba77b6147e6ab891972b818bd1b5 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 30 Oct 2026 16:40:46 +0100 Subject: [PATCH] Grant the provisioner client every machine's plans, as it watches them, so g14 and shanks read their plans again (issue 774) 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. --- go/buscompose/buscompose_test.go | 10 ++++++---- go/buscompose/provisioner.go | 9 ++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/go/buscompose/buscompose_test.go b/go/buscompose/buscompose_test.go index 722171e..5eb1bef 100644 --- a/go/buscompose/buscompose_test.go +++ b/go/buscompose/buscompose_test.go @@ -658,6 +658,9 @@ func healthChecks(t *testing.T) { "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", "$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). "$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. @@ -692,10 +695,9 @@ func healthChecks(t *testing.T) { // Each writes its own state alone, and these users are composed. "a.disk-load": {"$KV.restic_good.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 - // V3 until the lease write. - "provisioner.a": {"$JS.API.DIRECT.GET.KV_mesh-provisioner_plans.$KV.mesh-provisioner_plans.b.keycloak", - "$KV.mesh-provisioner_plans.a.keycloak", "$KV." + ProvisionerNeedsBucket + ".b.keycloak.db.x"}, + // It reads every machine's plans (a provider's consumers are bound there, ADR 0373) but writes none, nor another + // machine's needs, which it keeps for the client before V3 until the lease write. + "provisioner.a": {"$KV.mesh-provisioner_plans.a.keycloak", "$KV." + ProvisionerNeedsBucket + ".b.keycloak.db.x"}, // Only the assigner writes seat-holders. "a.artifact-store-tools": {"$KV.mesh-delivery-assigner_seat-holders.mesh-broker"}, // Only disk-load writes its readings. diff --git a/go/buscompose/provisioner.go b/go/buscompose/provisioner.go index 5221bf7..fc25434 100644 --- a/go/buscompose/provisioner.go +++ b/go/buscompose/provisioner.go @@ -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, `.` (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, provisions.PlansBucket} +var ProvisionerReads = []string{ConfigResolvedBucket} // 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 // reads to fill ${consumers:} 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. -var ProvisionerReadsWhole = []string{AssignmentsBucket} +// and plans, 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{AssignmentsBucket, provisions.PlansBucket} // 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, `..[.]`, to fill -- 2.54.0