85 lines
4.7 KiB
Markdown
85 lines
4.7 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-23
|
|
located-in: [mesh-controller cmd/mesh-controller/adoption.go (take previews nothing), mesh-host internal/apply (the comparison and the record)]
|
|
fixed-by: mesh-controller 201 (`secret accept --provider` reaches a required secret), 206 (a module's secrets listed with origin; a minted one for found data refuses unless `--mint <name>`)
|
|
amended-design:
|
|
---
|
|
|
|
# 100 — A secret the mesh mints cannot be the one the service it takes over already uses
|
|
|
|
## What was observed
|
|
|
|
Cutting a second service over on the control-node, 2026-09-23. The service signs its sessions with
|
|
a secret the machine has had for as long as it has been running. The mesh's module declares that
|
|
secret as one it *requires*, answered by the provision that makes secrets — so the mesh minted a
|
|
new one, and the running service was handed a value it had never seen.
|
|
|
|
The obvious thing was tried first: carry the old value in, the way the mesh already carries a value
|
|
it "did not make and cannot invent".
|
|
|
|
```
|
|
secret accept <node> <module> app-secret
|
|
→ does not declare "app-secret" as an own secret; it declares: broker
|
|
```
|
|
|
|
Carrying a value works only for a module's **own** secrets. A secret answered by the provision is
|
|
minted, and there is no way to say *this one already exists, here it is*. The two categories look
|
|
the same in a manifest and behave oppositely at a cutover.
|
|
|
|
Here it cost one re-login and nothing more. That is luck about which service went second.
|
|
|
|
**Six modules in the catalogue take a secret this way**, and the failure is different in each:
|
|
|
|
- A service whose **setup already ran** ignores the value entirely. The mesh then holds a
|
|
credential that does not work, believes it is the admin's, and nothing says otherwise until
|
|
somebody tries to use it.
|
|
- A credential **other systems were configured with** — a streaming source password, an API token —
|
|
changes under them. The service is fine; its clients are not, and they fail somewhere else.
|
|
- A secret that **keys stored data** makes that data unreadable. None of the six is known to be in
|
|
this class, and nothing in the mesh would stop one being added.
|
|
|
|
None of these is reported. The cutover succeeds, the service answers, and the damage is at a
|
|
distance.
|
|
|
|
## Why it matters beyond this instance
|
|
|
|
The mesh's model is right for a service it raises: nobody should know a password, so the mesh makes
|
|
one. Taking over a service that already exists inverts it — **the value is a fact about the machine,
|
|
and the mesh's job is to learn it, not to choose it.** Every migration is the second case, and the
|
|
mesh has a mechanism for exactly this (`secret accept`) that the provision cannot reach.
|
|
|
|
It also makes a class of module quietly un-migratable. A module is written with `requires: secret`
|
|
because that is the normal way to be given a password. The same choice, met at a cutover, means
|
|
the module can only be installed fresh.
|
|
|
|
## Open questions
|
|
|
|
- Should a provision-answered secret be acceptable too — the operator saying *this one exists* —
|
|
and does that belong to the module (it knows the secret pre-exists), the node, or the act of
|
|
taking?
|
|
- What should `take` do when a module it is cutting over has secrets the mesh minted and the
|
|
service is already initialised? Refuse, warn, or ask — and how would it know the service is
|
|
initialised?
|
|
- A module's manifest does not say whether a secret **keys stored data** or merely authenticates.
|
|
Should it, so the dangerous case can be refused rather than discovered?
|
|
- What is the reverse path: the mesh has minted one, the service ignored it, and the working value
|
|
is still on the machine. Nothing reconciles those.
|
|
|
|
## Decided, 2026-10-01
|
|
|
|
[ADR 0163](../../02-DECISIONS/0163-taking-a-module-over-is-a-comparison.md), rules 2 and 3: a minted secret for found data refuses; secret accept reaches required secrets. Building follows,
|
|
host first, then the controller's `take`.
|
|
|
|
## Built, 2026-10-02
|
|
|
|
`secret accept <node> <module> <name> --provider <node>` reaches a required secret (mesh-controller 201).
|
|
The pull request after it reads every secret a module holds on a machine with its origin, and a take
|
|
of a module whose data was found refuses a minted, unaccepted one — naming the accept that carries
|
|
the existing value in, or `--mint <name>` to let the service take the new one. Stays located until
|
|
a take is read on an adopted machine.
|
|
|
|
## Resolved, 2026-10-02
|
|
|
|
Closed on the operator's decision of 2026-10-02 with the built and tested code live on every machine (mesh-controller 206, mesh-host 64), not on a take read on an adopted machine: every machine of this mesh is converged, so none holds a found thing to compare, and the record's live row — ADR 0163's last — will be read at the next real adoption rather than staged. Said here so nobody later believes that row was run.
|