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