Issue 100: a secret the mesh mints cannot be the one the service it takes over already uses #86
@@ -0,0 +1,67 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user