From 3f3fb99219662ee194f1bf0d102f08a9c7ea0bf6 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 12:33:57 +0200 Subject: [PATCH 1/2] Issue 232: a consumer authenticates against a database its user does not live in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found fixing 225: with the secrets readable the provisioner created both users at once, and photos still could not connect because it asks for admin while its user lives in its own database. The password was never wrong — the grant secret and the consumer's environment hash identically. One fault wore the other's clothes: while no user existed anywhere, the error was a complete account of 225. Worth keeping as a habit — fix the first and look again. --- .../00-report.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 04-ISSUES/232-a-consumer-authenticates-against-a-database-its-user-does-not-live-in/00-report.md diff --git a/04-ISSUES/232-a-consumer-authenticates-against-a-database-its-user-does-not-live-in/00-report.md b/04-ISSUES/232-a-consumer-authenticates-against-a-database-its-user-does-not-live-in/00-report.md new file mode 100644 index 0000000..04d174a --- /dev/null +++ b/04-ISSUES/232-a-consumer-authenticates-against-a-database-its-user-does-not-live-in/00-report.md @@ -0,0 +1,60 @@ +--- +status: resolved +opened: 2026-10-04 +located-in: [mesh-catalog modules/photos] +fixed-by: mesh-catalog#264 +amended-design: +--- + +# 232 — A consumer authenticates against a database its user does not live in, and one fault hid it behind another + +## What was observed + +Fixing [issue 225](../225-a-provisioner-cannot-read-the-grant-secrets-since-its-code-left-the-container/00-report.md) +on the control machine, 2026-10-04. With the grant secrets readable again the provisioner created +both consumers' users at once, and one consumer still could not connect: + +``` +Authentication succeeded | user: mesh_novox_invoice | authDb: mesh_novox_invoice +Authentication succeeded | user: mesh_novox_photos | authDb: mesh_novox_photos +Authentication failed | user: mesh_novox_photos | authDb: admin + UserNotFound: Could not find user "mesh_novox_photos" for db "admin" +``` + +The provider creates each consumer's user **in that consumer's own database**, which is what the +first two lines are. `photos` asks for `admin`. Its sibling `invoicing`, against the same +provider, asks for `${bound:mongodb-database:as}` — the name the mesh gave it — and works. + +The password was never the problem: the grant secret and the value in the consumer's environment +hash identically. + +## Why it matters beyond this instance + +**One fault wore the other's clothes.** While the provisioner could not read its secrets at all, +*no* user existed, so `UserNotFound ... for db "admin"` was a true and complete account of issue +225. Fixing 225 is what made the wrong database visible — before that, every symptom pointed at +the thing that was already broken, and a second fault behind it was indistinguishable. + +That is the general shape worth keeping: **a fault that explains the symptom is not evidence +there is only one.** The check is to fix the first and look again, rather than to close both on +one explanation. + +It also says something about the interface. Which database a consumer authenticates against is +part of what `mongodb-database` means, and it is spelled out by hand in each consumer. Two +consumers of one provider wrote two different answers, and only one was right; nothing compared +them. That is the shape [issue 124](../124-a-consumer-cannot-be-told-what-its-provider-derived/00-report.md) +records for a derived bucket, here for the authentication database — +[ADR 0202](../../02-DECISIONS/0202-a-provider-declares-what-it-derives-for-each-consumer.md)'s +mechanism is what would remove it, by letting the provider say it once. + +## Resolved + +`photos` asks for `${bound:mongodb-database:as}`, as `invoicing` already did (mesh-catalog#264). +**How it is checked:** the module is rebuilt and pushed, and `photos-server` connects — verified +on the control machine rather than inferred from the manifest. + +## What this leaves open + +Nothing compares two consumers' idea of one interface. The provider could serve the +authentication database as a derived value under ADR 0202 and neither consumer would state it; +that is a candidate for the next consumer of this interface, not a repair of this one. From ab1bd5598e8e79e27680cab42740c820c43db158 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 12:37:31 +0200 Subject: [PATCH 2/2] Issues 225, 226, 227 resolved with their live proofs; 232 opened and resolved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 225: a grant secret is composed with the account that reads it — the node's account for a bundle, the declared secrets-owner for a container. Zero EACCES since 12:30:10 where there had been 4330, both users created, mongodb logging Authentication succeeded for each. The harness also stops calling a permanent refusal a race, which is the half that cost three hours. 226: normalising moved to the records, where the provenance is known, and a reference the sweep will not address is skipped rather than ending the sweep. The first build after the roll-out collected 200 and said 1126 remain — the backlog falls with every build instead of standing at 1681 for ever. 227: the three photo modules publish the endpoint they declare, and a catalogue-wide test makes it a rule: a container publishes only a port its module declares, or the mesh has nothing to assign and the number escapes. 232 came out from under 225: photos asked for a database its user does not live in, invisible while no user existed at all. --- .../00-report.md | 26 +++++++++++++++++-- .../00-report.md | 22 ++++++++++++++-- .../00-report.md | 20 ++++++++++++-- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/04-ISSUES/225-a-provisioner-cannot-read-the-grant-secrets-since-its-code-left-the-container/00-report.md b/04-ISSUES/225-a-provisioner-cannot-read-the-grant-secrets-since-its-code-left-the-container/00-report.md index 16435c0..1e21bfd 100644 --- a/04-ISSUES/225-a-provisioner-cannot-read-the-grant-secrets-since-its-code-left-the-container/00-report.md +++ b/04-ISSUES/225-a-provisioner-cannot-read-the-grant-secrets-since-its-code-left-the-container/00-report.md @@ -1,8 +1,8 @@ --- -status: open +status: resolved opened: 2026-10-04 located-in: [mesh-host internal/apply, mesh-controller internal/catalogue] -fixed-by: +fixed-by: mesh-controller#263 amended-design: --- @@ -73,3 +73,25 @@ their containers and made a silent fault visible as a restarting one. The dates is right once and wrong four thousand times is a message that hides its own meaning. - Which other modules' files did the runtime move leave behind? The sweep is the same question for every path the mesh writes for a module: directories, bundles, received files. + +## Resolved, 2026-10-04 + +A grant secret is composed with the account that will read it: the node's account where the +module's code is a bundle the runtime runs, its declared `secrets-owner` where it is still a +container, root where it says neither. The same rule the module's own secrets already followed, +reaching the other kind of secret the mesh writes for a module (mesh-controller#263). + +`givenTo` could not have reached these: it claims the files a bundle's *words* name, and the +harness composes a grant secret's path from the contributions file, which no word names. + +And the harness stops calling a permanent refusal a race (mesh-sdk#22, 0.1.9). After about a +minute it says so plainly, rarely rather than every five seconds, and names what to look at — +who owns the file and who the process runs as. That is the half that cost three hours. + +**How it was checked:** on the control machine, after the push — the grant secrets belong to the +operator's account, **zero `EACCES` since 12:30:10** where there had been 4330, both users were +created, and `mongodb-server` logs `Authentication succeeded` for each. + +It also uncovered [issue 232](../232-a-consumer-authenticates-against-a-database-its-user-does-not-live-in/00-report.md), +which this fault had been hiding: with no user anywhere, "not found in admin" was a complete +account of *this* issue and said nothing about the consumer asking the wrong database. \ No newline at end of file diff --git a/04-ISSUES/226-the-stores-sweep-stops-at-the-first-reference-recorded-with-an-address/00-report.md b/04-ISSUES/226-the-stores-sweep-stops-at-the-first-reference-recorded-with-an-address/00-report.md index ca4c401..b615e34 100644 --- a/04-ISSUES/226-the-stores-sweep-stops-at-the-first-reference-recorded-with-an-address/00-report.md +++ b/04-ISSUES/226-the-stores-sweep-stops-at-the-first-reference-recorded-with-an-address/00-report.md @@ -1,8 +1,8 @@ --- -status: open +status: resolved opened: 2026-10-04 located-in: [mesh-controller cmd/mesh-controller/collect.go, mesh-controller internal/inventory/collection.go] -fixed-by: +fixed-by: mesh-controller#263 amended-design: --- @@ -56,3 +56,21 @@ reports the right number while doing it, which is what made it look healthy. - Only a refusal *by the store* ends a sweep. - **How it is checked:** a sweep over records holding one address-recorded reference and one kept one collects the second; a sweep against a store that refuses stops at the first. + +## Resolved, 2026-10-04 + +Two changes, deliberately separate. + +**Normalising moved to where the provenance is known.** Every reference the sweep handles came +from a build record, so every one is the mesh's own and `Recorded` may read an address-era +reference as the kept one. Not in `LetGo` — it cannot tell `docker.io` from the mesh's store, and +an attempt to put it there was caught at once by the test that says a foreign reference is never +asked about. The guard stays strict; the records speak the one vocabulary. + +**A reference the sweep will not address is `ErrNotOurs`:** skipped, not marked collected, never +a reason to stop. Only the store refusing ends a sweep. + +**How it was checked:** the first build after the roll-out printed *"the artifact store let go of +200 artifact(s) the mesh no longer keeps — 1126 more to collect; the next build asks again"*. +Two hundred is the per-sweep bound working as intended; the backlog is falling with every build +instead of standing at 1681 for ever. \ No newline at end of file diff --git a/04-ISSUES/227-the-photo-apps-admin-client-asks-for-the-port-the-proxy-holds/00-report.md b/04-ISSUES/227-the-photo-apps-admin-client-asks-for-the-port-the-proxy-holds/00-report.md index 906b001..986197c 100644 --- a/04-ISSUES/227-the-photo-apps-admin-client-asks-for-the-port-the-proxy-holds/00-report.md +++ b/04-ISSUES/227-the-photo-apps-admin-client-asks-for-the-port-the-proxy-holds/00-report.md @@ -1,8 +1,8 @@ --- -status: open +status: resolved opened: 2026-10-04 located-in: [mesh-catalog modules/photos] -fixed-by: +fixed-by: mesh-catalog#263, mesh-controller#263 amended-design: --- @@ -42,3 +42,19 @@ gradually. is composed, rather than failing on the machine every pass. The mesh assigns ports ([ADR 0038](../../02-DECISIONS/0038-the-mesh-assigns-the-port.md)); a fixed 80 beside a proxy is a statement it could check. + +## Resolved, 2026-10-04 + +The three photo modules publish the endpoint they declare — `4001:80`, `4012:80`, `4013:80` — +so the software's own port reaches the machine at the port the mesh assigned, and nothing asks +for 80. + +**The rule, rather than three repairs.** A container may publish only a port its module declares: +the short form `"80"` means *publish what the software calls 80*, and the mesh fills in the +machine's half from the port it assigned — which it can only do for a port the module declared. +Four modules publish 80 quite safely because they declare 80. The difference is the declaration, +not the number. A catalogue-wide test in mesh-controller#263 says so, and names all three +offenders against the catalogue as it was. + +**How it was checked:** `photos-admin-client` has been up since the push, where before it failed +on every pass. \ No newline at end of file