4.4 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | ||
|---|---|---|---|---|---|---|
| resolved | 2026-09-05 |
|
ADR 0049 (a slug for the login) + a shorter minted secret (mesh-control) | 0049-a-consumers-identity-fits-the-tightest-backend.md |
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 0048), 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: <ERROR> 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_<node>_<module> — 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 0048 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
asis 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
asto 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-bucketstates its identifier bounds) and have the mesh derive within them — the most honest, the most work.
Open questions
- Is
asmeant 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?
Resolution
Accepted ADR 0049 (option E): a module declares an optional short slug, and the mesh derives
mesh_<node>_<slug|name>, bounded by the tightest backend (an S3 access key's 20) and refused at
assignment — naming the slug as the remedy — when it still would not fit. The minio grant e2e proved
it: bucketuser declares slug: bkt, so its access key mesh_anchor_bkt (15) is accepted where
mesh_anchor_bucketuser (22) was refused.
Proving that surfaced a second S3 length constraint on the same credential — the secret. The
mesh minted a 43-character password (32 random bytes, base64url), and an S3 secret key is 8–40. Fixed
in mesh-control internal/secrets/seal.go by minting 30 bytes → exactly 40 characters (240 bits,
ample), which fits S3 and every other backend. Both halves of an S3 credential — the access key
(login) and the secret key (password) — now fit the tightest backend, by the same rule.