Reconcile: adopt initialization's consolidated HQ as canonical, re-home this session's new work #24
@@ -162,6 +162,13 @@ same module. There is one derivation here, and there should stay one.
|
|||||||
**A live listing returned credentials in plaintext.** Not a coverage question, but the reason
|
**A live listing returned credentials in plaintext.** Not a coverage question, but the reason
|
||||||
sealing is worth its inconvenience.
|
sealing is worth its inconvenience.
|
||||||
|
|
||||||
|
**An image store is a module, and was written up here as something the mesh does.** It was
|
||||||
|
considered for the substrate and removed, because the test is not *can it grant itself one* —
|
||||||
|
nearly anything passes that — but whether the control plane needs it before it can give its first
|
||||||
|
instruction. It does not. So a registry somebody runs for their own images is the same module as
|
||||||
|
the one the mesh runs for its own: it offers a place to push, and claims that role once per
|
||||||
|
machine.
|
||||||
|
|
||||||
**A rule was enforced only at the far end.** A module may not declare an action, and the host
|
**A rule was enforced only at the far end.** A module may not declare an action, and the host
|
||||||
refused one correctly — but the control plane accepted it into the catalogue, resolved it and
|
refused one correctly — but the control plane accepted it into the catalogue, resolved it and
|
||||||
pushed it, so the refusal arrived on a machine with nothing tying it back to the manifest. The
|
pushed it, so the refusal arrived on a machine with nothing tying it back to the manifest. The
|
||||||
|
|||||||
@@ -84,14 +84,38 @@ system's seven images named repositories that **do not exist** — it publishes
|
|||||||
registry entirely — and one of the seven had been renamed upstream. Nothing that only checks the
|
registry entirely — and one of the seven had been renamed upstream. Nothing that only checks the
|
||||||
shape of a reference could ever have found either.
|
shape of a reference could ever have found either.
|
||||||
|
|
||||||
|
## The mechanism already existed, and this issue was wrong about that
|
||||||
|
|
||||||
|
**Corrected 2026-09-01, the same day.** This was filed saying nothing turns a tag into a digest.
|
||||||
|
That is false, and the answer had been designed and built before any of it was written.
|
||||||
|
|
||||||
|
A module does not name an image at all. It names an **artifact**, and declares where that artifact
|
||||||
|
comes from:
|
||||||
|
|
||||||
|
```
|
||||||
|
build.artifacts: [{ name: "gitea", kind: "upstream", from: "gitea/gitea:1.22" }]
|
||||||
|
resources: [{ id: "server", type: "container", artifact: "gitea", … }]
|
||||||
|
```
|
||||||
|
|
||||||
|
`kind: upstream` means *an image somebody else built, mirrored into the mesh's own registry and
|
||||||
|
pinned by the digest it lands with*. The builder produces it; the manifest the mesh holds is
|
||||||
|
derived, with `artifact` replaced by the real reference and the key removed, because the host has
|
||||||
|
never heard of that word. A resource naming an artifact nothing produced is refused.
|
||||||
|
|
||||||
|
So the two-document split this issue described as the shape of a fix **is the design**, and it
|
||||||
|
covers both cases it said were unsolved: an image the mesh builds, and an image somebody else
|
||||||
|
built. Mirroring also removes something worse than a stale pin — every machine needing a route to
|
||||||
|
a public registry, and a tag a stranger can move.
|
||||||
|
|
||||||
|
**What was actually wrong was the examples.** They hard-coded image references instead of naming
|
||||||
|
artifacts, so they inherited a problem the design does not have. Pinning twelve of them by hand
|
||||||
|
was treating the symptom, and left the reference pointing at a public registry rather than the
|
||||||
|
mesh's own.
|
||||||
|
|
||||||
## What is still open
|
## What is still open
|
||||||
|
|
||||||
**Nothing turns a tag into a digest as part of the mesh's own work.** It was done by hand here,
|
**The examples should name upstream artifacts** rather than carry hand-pinned digests. That is the
|
||||||
which is exactly what this issue says a person should not be asked to do. The shape of the answer
|
remaining work, and it is a rewrite of five manifests rather than a mechanism to build.
|
||||||
is unchanged: a person writes a tag, the mesh resolves it once and records both, and *this pin is
|
|
||||||
behind its tag* becomes a question the mesh can answer.
|
|
||||||
|
|
||||||
**The mesh's own provisioner images still cannot be pinned in a repository**, because their digest
|
The refusal added here stays: a placeholder must not reach a machine whatever the reason it is
|
||||||
does not exist until they are built and pushed. That is the bundle's problem, and the bundle solves
|
there.
|
||||||
it by writing the digest down after building. A module naming an image the mesh builds needs the
|
|
||||||
same treatment, and does not have it.
|
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
status: located
|
||||||
|
opened: 2026-09-01
|
||||||
|
located-in: [mesh-control]
|
||||||
|
fixed-by:
|
||||||
|
amended-design:
|
||||||
|
---
|
||||||
|
|
||||||
|
# 026 — The data directories are mounted and never declared
|
||||||
|
|
||||||
|
## Symptom
|
||||||
|
|
||||||
|
Four modules mount **fourteen host paths** that no resource in those modules declares:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitea /services/gitea/gitea
|
||||||
|
postgres /services/postgres/db-data
|
||||||
|
minio /services/minio/data/data1-1
|
||||||
|
mailu eleven more, including the mail spool and the admin database
|
||||||
|
```
|
||||||
|
|
||||||
|
Each is a bind mount on a container. None is a `directory` resource. The mesh has never heard of
|
||||||
|
any of them.
|
||||||
|
|
||||||
|
## What that costs
|
||||||
|
|
||||||
|
**They are created by the container runtime, as root.** A bind mount whose source does not exist
|
||||||
|
is created for you, owned by root, with whatever mode the runtime picks. So `owner` and `mode` —
|
||||||
|
which exist precisely so a module can say who its data belongs to — are silently not applied to
|
||||||
|
the only directories that hold data.
|
||||||
|
|
||||||
|
**The protection that exists for exactly this does not reach them.** A directory the mesh declared
|
||||||
|
and no longer wants is *kept*, not removed, when it holds anything the mesh did not put there
|
||||||
|
([ADR 0030](../../02-DECISIONS/0030-data-outlives-the-mesh-that-declared-it.md)). That rule is the
|
||||||
|
answer to *what happens to my data when a module goes away*, and it is written in terms of
|
||||||
|
declared directories. **An undeclared one is not protected by it, because the mesh does not know
|
||||||
|
it is there.**
|
||||||
|
|
||||||
|
So the single rule guarding against data loss covers the configuration directories, which are
|
||||||
|
cheap to lose, and not the data directories, which are the reason the rule exists.
|
||||||
|
|
||||||
|
## Where it came from
|
||||||
|
|
||||||
|
These manifests were written by reading the arrangement being replaced and carrying its
|
||||||
|
`docker-compose` files across — service, image, ports, volumes, environment — into the new
|
||||||
|
manifest's container shape. That shape can express all of it, which is what made the
|
||||||
|
transliteration feel like progress.
|
||||||
|
|
||||||
|
**A container shape that can express a compose file will be filled in like a compose file.** The
|
||||||
|
mesh's model is larger than that: a directory is a thing the mesh owns, with an owner and a mode
|
||||||
|
and a rule about what happens when it is no longer wanted. A volume line borrowed from compose
|
||||||
|
declares none of it, and nothing complains, because a bind mount source is a string.
|
||||||
|
|
||||||
|
## What a fix has to keep
|
||||||
|
|
||||||
|
- **Every host path a container mounts is declared.** If a module wants a directory on the
|
||||||
|
machine, it says so, with who owns it and what mode — and gets the removal rule with it.
|
||||||
|
- **The check is mechanical.** A person comparing volumes against declared directories by hand is
|
||||||
|
the process that produced this. It is a few lines against the manifest and belongs beside the
|
||||||
|
other manifest checks.
|
||||||
|
- **Not by inventing directories at apply time.** The host creating what a mount needs would make
|
||||||
|
the mesh's ownership of a directory depend on which resource mentioned it first, and would put
|
||||||
|
the same undeclared path back a layer down.
|
||||||
|
|
||||||
|
## Not yet answered
|
||||||
|
|
||||||
|
**Where a module's data should live at all.** These paths were inherited whole from the
|
||||||
|
arrangement being replaced, which put everything under one directory per service. Whether that is
|
||||||
|
right here is a separate question, and a bigger one — it decides what a person backs up, and what
|
||||||
|
survives a module being removed.
|
||||||
Reference in New Issue
Block a user