Found at the second cutover. Carrying a value in works only for a module's own secrets; a secret answered by the provision is minted, and six catalogue modules take one that way.
68 lines
3.3 KiB
Markdown
68 lines
3.3 KiB
Markdown
---
|
|
status: open
|
|
opened: 2026-09-23
|
|
located-in: []
|
|
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.
|