From c818e9c766abbde01d1597ede28996074f59b9f3 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 6 Oct 2026 16:08:30 +0200 Subject: [PATCH] Issue 274: a provider is granted only the consumers bound to it --- .../00-report.md | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 04-ISSUES/274-a-provider-was-granted-consumers-bound-elsewhere/00-report.md diff --git a/04-ISSUES/274-a-provider-was-granted-consumers-bound-elsewhere/00-report.md b/04-ISSUES/274-a-provider-was-granted-consumers-bound-elsewhere/00-report.md new file mode 100644 index 0000000..f680f97 --- /dev/null +++ b/04-ISSUES/274-a-provider-was-granted-consumers-bound-elsewhere/00-report.md @@ -0,0 +1,103 @@ +--- +status: located +opened: 2026-10-06 +located-in: [mesh-controller] +fixed-by: novox/mesh-controller PR #87 +amended-design: +--- + +# 274. A provider was granted consumers bound elsewhere + +## Symptom + +After [issue 273](../273-a-rule-for-the-resolver-moved-a-machines-databases/00-report.md), the five +applications on the home server were pinned back to the home server's own store. They run there, on +their own data. The operator confirmed it and asked for the databases made for them on the control +node's store to be retired: +[ADR 0230](../../02-DECISIONS/0230-a-consumer-the-mesh-stops-asking-for-is-retired-and-deleted-only-by-a-person.md) +disables them, keeps their data, and leaves deleting them to a person. + +They were never retired. Read on 2026-10-06, without changing anything: + +- The control node's plan still carried a grant file for each of the five, one per consumer of the + relational database provision on the home server. +- The control node's database provider still counted all five as asked for. It listed them as held, + and nothing was waiting on a retirement. +- Each consumer's own resolution bound it to the home server's store, and its binding file said so. + +The mesh asked one provider to keep five logins for consumers bound to another. Nothing said so. +Under ADR 0230, the provider retires only what the mesh stops asking for, so it would have kept them +forever. + +## Cause + +Which consumers a provider is granted was read from the wrong record. The controller enumerated every +pair credential on record from the provider. It granted each one whose consumer still *required* the +provision. It never checked *which provider* that requirement was bound to. + +A pair credential records that the provider was asked by a consumer once. It does not record that it +is still asked. The issue 273 move made a credential from the control node for each of the five. The +pin back made the consumers use the home server's credential again. The control node's credentials +stayed on record, so the control node stayed granted. + +Before issue 273 this gap had no visible effect: only a person moves a consumer, and it is rare. +[ADR 0232](../../02-DECISIONS/0232-a-binding-to-a-consumers-data-moves-only-by-a-person.md) makes +a pin the only way to move a consumer of a provision that keeps data. That makes this the path every +such move takes, and every move left a provider granted a consumer it no longer serves. + +## Fix + +The fix is novox/mesh-controller PR #87. **A provider is granted exactly the consumers whose own +current resolution binds them to it.** A credential is identified by its provision, the consuming +module and its local name. + +- A consumer that still asks, but whose resolution binds that credential to another provider (or, + under that local name, to none), is withdrawn exactly like a consumer nobody asks for. The provider + is no longer told about it, sees it unasked, and retires it under ADR 0230 with its data kept. +- The withdrawal is reported. The provider's `plan` and `push` name each such consumer, where it is + bound now, and that the login is retired and its data kept until `cleanup delete`. +- **The pair credential is kept on record, not forgotten.** It is the key to the login the provider + still holds, disabled, beside the consumer's data. A person who pins the consumer back finds it. + Dropping it while the provider holds that login would make a pin back mint a new credential for + data the old one owns. +- The binding is read from the same resolution the grant was already read from. An unreadable + resolution stays an error, never an empty answer + ([issue 152](../152-a-nodes-plan-failure-silently-drops-its-routed-names/00-report.md)). A kept + binding (ADR 0232) is a resolution binding the consumer to its recorded provider, so that provider + keeps the grant. + [ADR 0225](../../02-DECISIONS/0225-a-consumers-identity-is-bounded-by-the-provision-it-requires.md)'s + overflow check applies to what is still granted. + +After rollout, the five become unasked on the control node's provider. After five passes they are +candidates for retirement. Five is more than the provider's bound of three, so it retires nothing, +and the controller raises an urgent *retire-waiting* condition that names them. The operator answers +it with `retire approve`. + +## How it is checked + +Tests in the controller run through its real stores. + +- **Today's state.** A consumer is pinned to another machine's store and sent. It is then pinned back + beside its data, so a credential from each store is on record. The store it left no longer grants + it, its declaration no longer carries it, and the push names it. The store it is bound to grants + it. Both credentials stay on record. This test fails without the fix. +- **An unreadable resolution.** A consumer whose resolution cannot be completed makes the grants an + error, not a withdrawal. +- **The rule on a resolution.** A credential is bound to the provider of a need for exactly that + credential. A need answered by a record binds nothing, and another local name is another + credential. + +## Left open + +- **When a kept credential is forgotten.** It should be dropped once the provider reports that + consumer deleted by `cleanup delete`. Nothing connects the two yet, so these credentials stay on + record after the data is gone. They are reported on every plan of the provider until then. +- **`rotate` and a credential bound elsewhere.** Rotating the provision for one of these consumers + still lists both holders. Rotating the unbound one discards it, and nothing remakes it, because no + need is bound there. That is harmless, since its provider is no longer granted it, but it is the + one path that forgets such a credential, and it does so without saying why. +- **A consumer whose set does not resolve.** Its grants are skipped, not kept: the existing rule that + a set that does not resolve is that machine's problem. Under ADR 0230, a provider then sees that + consumer unasked and, after five passes, retires it, so a broken set elsewhere can retire a working + consumer's login. The retirement bound and the person's approval limit the damage, but this is the + same class of withdrawal-on-a-failed-read as issue 152, and it should be decided on its own.