Files
hq/04-ISSUES/100-a-minted-secret-cannot-be-the-one-the-service-already-uses/00-report.md
T
jschoubben c4151e6bc4 ADR 0163 built: the take digest, the minted-secret refusal, the networks setting, settings judged where stored, genesis raising the forge as declared; issues 096, 097, 126 resolved
The record gets its built note; designs 05 and 09 the revisions; 086, 098,
099, 100 and 101 stay located because every machine is converged and the
record's live row — a take read on an adopted machine — has not been run;
090 is built in part, its network difference left for the take to say.
2026-10-01 23:45:57 +02:00

81 lines
4.1 KiB
Markdown

---
status: located
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:
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.