61 lines
3.0 KiB
Markdown
61 lines
3.0 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-13
|
|
located-in: [mesh-host internal/apply (container spec digests)]
|
|
fixed-by: mesh-host e7f94e0 (restart-on digests folded into the container spec, a standing comparison); unit-tested in apply_test.go, no lab assertion yet
|
|
amended-design:
|
|
---
|
|
|
|
# 045 — A container keeps the values it started with
|
|
|
|
## Symptom
|
|
|
|
A module reads its database credentials from a file the mesh composes. The file was written, then
|
|
rewritten with a different value once the mesh had more to say — the provider's address was not
|
|
known the first time and was the second.
|
|
|
|
The container went on running with the first version, indefinitely, and reported nothing. Its own
|
|
view:
|
|
|
|
```
|
|
DATABASE_URL=postgresql://…@:20000/… what the container holds
|
|
DATABASE_URL=postgresql://…@<provider>:20000/… what the file on disk says
|
|
```
|
|
|
|
A module may declare that it restarts when one of its files changes, and this one now does. That
|
|
did not help: the restart fires when the file changes *during an apply*, and the change had already
|
|
happened before the declaration naming it arrived. Applying again is a no-op, because nothing
|
|
changed that time either. The only thing that recovered it was removing the module from the machine
|
|
and putting it back.
|
|
|
|
## Why this matters
|
|
|
|
**Reconciliation compares intentions, not what is actually running.** Two intentions in a row can
|
|
both be applied successfully and still leave a container holding values from neither, because a
|
|
container captures its environment once, when it is created, and nothing afterwards re-reads it.
|
|
|
|
The failure is silent in the worst way: the container is up, the machine reports success, the mesh
|
|
reports every module current, and the thing inside is using a credential the mesh no longer
|
|
believes in. Nothing in the system is in a position to notice — the host knows what it wrote, and
|
|
the module knows what it read, and no one compares the two.
|
|
|
|
It is not specific to credentials. Any composed value delivered through a file a container reads at
|
|
start has the same shape, which is most of what the mesh delivers.
|
|
|
|
## How it would be checked
|
|
|
|
A machine reporting a module as running should be able to say *what it is running with* — not what
|
|
was last written for it. The check is then a comparison the mesh can make on every heartbeat rather
|
|
than a property of one apply: for each container, does what it holds match what the files it was
|
|
built from now say. A mismatch is a drift the mesh can act on, and today it is not expressible at
|
|
all.
|
|
|
|
## Open questions
|
|
|
|
- Should the host recreate such a container on its own, or report the drift and let the mesh decide?
|
|
Recreating is the obvious repair and is also an unannounced restart of a running service.
|
|
- Is "the files a container was created from" something the host already knows, or does a
|
|
declaration have to say it? A restart-on list is close to this, but it is written by the module
|
|
author and is therefore exactly as complete as they remembered to make it.
|
|
- Does the same gap exist for values the mesh delivers by other means than a file?
|