diff --git a/04-ISSUES/010-mesh-login-exceeds-s3-access-key-limit/00-report.md b/04-ISSUES/010-mesh-login-exceeds-s3-access-key-limit/00-report.md new file mode 100644 index 0000000..ed0849a --- /dev/null +++ b/04-ISSUES/010-mesh-login-exceeds-s3-access-key-limit/00-report.md @@ -0,0 +1,63 @@ +--- +status: open +opened: 2026-09-05 +located-in: [mesh-control, mesh-catalog] +fixed-by: +amended-design: +--- + +# The mesh's derived login does not fit every backend's identity rules — S3 rejects it + +## What was observed + +Proving the provider/consumer contract per backend (ADR 0053), redis and postgres passed: a +consumer authenticated against the provider with the login the mesh derived and the password +the mesh minted. **minio failed**, and not on the credential — on the *name*: + +``` +mc: Unable to add a new service account. The access key is invalid. + (access key length should be between 3 and 20). +``` + +The mesh derives a consumer's login as `mesh__` — here `mesh_anchor_bucketuser`, +22 characters. That is a valid postgres role and a valid redis ACL user, so those providers +create it verbatim. S3 access keys are capped at **20 characters**, so minio refuses to create +the service account under it, and the provisioner retries forever while the consumer, holding +that same too-long access key, could never present it either. + +## Why it matters beyond this instance + +ADR 0053 says a provider creates *exactly* the login the mesh derived, so that the two ends +agree by construction — the mesh hands the same name to the provider (to create) and the +consumer (to present). That only holds if the derived name is one every provider can accept. +It is not: the mesh's `as` is a single format with no knowledge of a backend's identity rules, +and S3's are stricter than a database's. Any provider whose backend constrains identifiers more +tightly than postgres — a length cap, a charset, a required prefix — inherits this, and the +failure lands at provision time, per consumer, as an infinite retry rather than a refusal at +assignment. + +This also shows the seam is real, not cosmetic: `as` is doing two jobs — a stable per-consumer +identity the two ends must agree on, and a literal identifier a specific backend must accept — +and those are not always the same string. + +## The shape of a fix (open, not decided) + +- **Constrain the derivation** so `as` is broadly acceptable — short (≤ 20), a conservative + charset, deterministic. This keeps "the provider creates exactly what the mesh derived" true + everywhere, at the cost of a less legible name, and it is a mesh-wide identity change (every + provider that already created the longer name would see it change). +- **Let a provider map `as` to a backend-valid identifier** it derives the same way on create + and on the consumer's behalf — but the consumer is generic and cannot run minio's mapping, so + this only works if the mapped identifier is *delivered back* to the consumer. That is the + data-provision return path this era keeps meeting (umami's siteId, cloudflare's record) and + does not yet have. +- **Declare the constraint on the interface** (`s3-bucket` states its identifier bounds) and + have the mesh derive within them — the most honest, the most work. + +## Open questions + +- Is `as` meant to be human-legible, or is a short opaque token acceptable — i.e., can the + derivation simply be shortened without anyone minding? +- Do redis/postgres actually want the long name, or did it only survive because they are + permissive? If nothing needs it long, the cheap fix is to cap it. +- Does this fold into the same decision as the data-provision return path, or is it separate?