ADR 0053 (proposed) — a provider creates the credential the mesh minted, and seals nothing
Issue 008's trace confirmed the premise in control-plane code: the mesh already mints one password per consumer/provider pair and delivers the provider its copy (SecretFor/SecretsFrom/grantsFor -> Grant.Sealed; the receives contribution carries As + Secret). The provisioner's symmetric seal is an orphaned, contradictory second model. ADR 0053 corrects the provider contract in one place (the sdk harness): providers create the resource with the mesh-supplied login and password and drop seal/key/return entirely. Reframe 008 as contract-first (every provider, not four). Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -62,33 +62,55 @@ would authenticate with the mesh's password against a resource created with the
|
||||
|
||||
## Why it matters beyond this instance
|
||||
|
||||
The module-runtime model is what every provider now follows — a cache, an object store, a
|
||||
database. As written, each carries a provisioner that cannot start (no key), and that, if it
|
||||
did, would create resources with the wrong password and seal them for a reader that does not
|
||||
exist. The rule the design states — "a consumer receives a sealed credential and unseals it" —
|
||||
is enforced by nothing, because no consumer unseals and no shared key exists to unseal with.
|
||||
This is not a four-module problem. The provider contract lives in **one place** — the sdk's
|
||||
`runProvisioner(resource, adapter)` harness — and every provider is built on it. Four exist
|
||||
today (redis, postgres, minio, umami); a mesh of any size ends up with many. Whatever the
|
||||
provisioner harness does, every present and future provider inherits, so the orphaned
|
||||
symmetric seal is a fault stamped into the interface, not into four adapters. That also sets
|
||||
the cost of getting it wrong: a contract N providers depend on is N migrations to change
|
||||
later, which is the argument for settling it deliberately now rather than patching around it.
|
||||
|
||||
As written, each provider carries a provisioner that cannot start (no key), and that, if it
|
||||
did, would create resources with a password it invented — a *different* password from the one
|
||||
the mesh minted and handed the consumer — and seal them for a reader that does not exist. The
|
||||
rule the design states, "a consumer receives a sealed credential and unseals it," is enforced
|
||||
by nothing: no consumer unseals, and no shared key exists to unseal with.
|
||||
|
||||
## The mesh already does this — confirmed
|
||||
|
||||
The premise the fix rests on is not a hope; it is in the control plane today. For a served
|
||||
interface, `Inventory.SecretFor` mints one password per (consumer, provider) pair via
|
||||
`secrets.Make`, sealing it to **both** node keys — `ForConsumer` and `ForProvider`.
|
||||
`SecretsFrom(provider)` is documented as "every credential a provider node was issued, so it
|
||||
can be told what to create," and `grantsFor` (plan.go) hands the provider node one `Grant` per
|
||||
consumer carrying `Sealed: ForProvider`. The provider receives, at the path its `receives`
|
||||
names, one `Contribution` per consumer: the login to create (`As`, derived by the mesh so both
|
||||
ends agree — 04-ISSUES/023), the consumer's address (`At`) and requested `Values`, and
|
||||
`Secret`, the file holding that consumer's password sealed to this provider and unsealed by
|
||||
its host. Everything the provisioner needs is delivered. It reads the wrong files
|
||||
(`*.grant.json`, which nothing writes) and invents a password instead of reading the one in
|
||||
`Secret`.
|
||||
|
||||
## The fix this points to
|
||||
|
||||
Not "deliver the seal key." **Align the provider with the mesh's existing credential path and
|
||||
delete the symmetric seal:**
|
||||
A **one-place contract change in the sdk harness**, plus re-pointing today's adapters at it —
|
||||
not per-provider surgery, and inherited correctly by every provider after them:
|
||||
|
||||
- The provisioner should stop generating a password and stop sealing. It should read the
|
||||
per-consumer password the mesh already mints and delivers to the provider (`ForProvider`,
|
||||
unsealed onto the machine by the host), and *create the resource with that password*.
|
||||
- The consumer already receives the matching password as plaintext its own host wrote — no
|
||||
change needed there.
|
||||
- `runProvisioner`'s `sealKey`, `seal()`, `writeSealedCredential`, and the `*.grant.json` /
|
||||
`*.credential` file dance come out; what replaces them is a reconcile driven by the
|
||||
contributions file the mesh already writes to the provider's `receives` path.
|
||||
- `runProvisioner` reconciles the mesh-delivered `receives` contributions (not `*.grant.json`):
|
||||
for each consumer, create the resource under the login `As` with the password read from the
|
||||
delivered `Secret` file, for its `Values`; withdraw the login when a consumer leaves the file.
|
||||
- The adapter stops generating a password and stops returning a credential — it is handed the
|
||||
name and the password and only makes the resource exist. Roughly `create({as, password,
|
||||
values})` / `remove({as})`, no return.
|
||||
- `sealKey`, `seal()`, `writeSealedCredential`, `MESH_SEAL_KEY`, and the `.credential` file
|
||||
leave entirely; the consumer already receives its copy through the mesh's own channel.
|
||||
|
||||
This is proposed as ADR 0053, which defines the corrected provider contract, for ratification.
|
||||
|
||||
## Open questions
|
||||
|
||||
- Does the mesh mint and deliver `ForProvider` for a *served interface* today (redis-cache,
|
||||
postgres-database), or only for the own-secret case the trace followed? Confirm the provider
|
||||
actually receives each consumer's password before reworking the adapter around it.
|
||||
- What is the adapter contract after the change — `create(consumer, password)` rather than
|
||||
`create(grant) -> Credential`? That is a breaking change to the four providers (redis,
|
||||
postgres, minio, umami) and wants an ADR, since it changes what a provider module *is*.
|
||||
- Is any of the symmetric `seal()`/`unseal()` primitive still used for anything legitimate, or
|
||||
does it leave with the provisioner?
|
||||
- Does anything legitimate still use the symmetric `seal()`/`unseal()` primitive, or does it
|
||||
leave with the provisioner?
|
||||
- On withdrawal the mesh drops the consumer from the contributions file; is "remove the login"
|
||||
the whole of teardown for every provider, or does an object store (data left behind) need a
|
||||
policy the contract should name?
|
||||
|
||||
Reference in New Issue
Block a user