Files
hq/02-DECISIONS/0012-the-mesh-creates-no-symlinks.md
T
jschoubben b4607dfc03 Numbers are identity; the reading order is a generated, checked index
Decided after measuring what renumbering actually costs: 96 references in code
comments across two repositories, none of which would have failed to compile.
They would have pointed at the wrong reasoning, which is worse than a broken
link because nothing reports it.

So a number identifies a record and never changes. It cannot also be a
position -- a position moves when the set changes, and an identity that moves
is not one.

The reading order moves into an index generated from each record's `topic:`.
Six topics, in the order somebody learns the system.

The index is WRITTEN rather than only generated on demand, which reverses what
this repository previously said. The reason it said otherwise is that a
hand-written index drifts -- but a reader looking at the folder on a forge sees
the folder, not a command, and the drift objection is answered by checking
rather than by refusing to write one. That is §5's own rule: a rule states how
it is checked.

Two checks, both confirmed to bite. index.py fails when the written order no
longer matches the records. records.py fails when a record has no topic or one
nobody defined -- the quiet failure being a record that vanishes from the order
rather than appearing in the wrong place.
2026-08-28 23:39:18 +02:00

102 lines
5.5 KiB
Markdown

---
topic: building it
status: accepted
date: 2026-08-23
deciders: jochen
reconstructed: false
---
# 12. The mesh creates no symlinks — a derived file is a copy
## Context
[ADR 0012](0012-the-mesh-creates-no-symlinks.md) responded to production data loss — a hand-made
link, resolved through a container engine's volume handling, pointing a mount somewhere it
should not have — by centralising linking in the installer and forbidding it everywhere else.
That narrowed the incident class. It did not close it. The hazard is not *who* made the link;
it is that a path can resolve somewhere other than where it appears to. A link made by the
installer resolves exactly the same way as a link made by hand. The rule made the mechanism
rarer and better-governed while leaving the mechanism in place.
Two things have changed since, and together they remove the argument that kept it.
**The original case for linking was staleness.** A copy of a service definition goes stale
silently while the catalogue moves on, so a link was the cheap way to guarantee the running
node reads a current definition. That argument assumes the node's copy is unmanaged.
**It is not.** [ADR 0011](0011-managed-files-are-generated-never-edited.md) established that
everything on a node's disk is derived from the mesh and regenerated when its inputs change,
and the installer already **reconciles** links rather than assuming them — repointing stale
ones, adopting real files it finds where a link belongs. Reconciling content is the same
operation as reconciling a pointer, plus a comparison.
So the mesh already has the machinery that makes a copy safe, and is using a link to solve a
problem that machinery solves better. Worse, a link is conceptually the wrong shape: it makes
the node's runtime state a *pointer into source*, which is the one thing
[ADR 0006](0006-the-substrate-and-the-control-plane.md) and ADR 0011 exist to prevent.
State is derived onto nodes; it does not reach back.
## Considered options
1. **Keep ADR 0019 as the final position** — centralised linking, forbidden elsewhere.
Rejected as the status quo. It governs the mechanism rather than removing it, and the
failure it was written for remains reachable by any code path the installer trusts.
2. **Keep links but harden them** — canonicalise before mounting, refuse a link that escapes
an expected root. Rejected: it is a check bolted onto a hazard, and it has to be correct in
every consumer, including container engines the mesh does not control.
3. **Copy, reconciled by the installer, with staleness detected rather than assumed away.**
Proposed here.
## Decision
*Accepted 2026-08-25. The position is settled; the migration is not designed. See "Open" below.*
**The mesh creates no symlinks.** A file a node needs is placed on that node as a real file,
derived from the mesh and reconciled by the installer like every other managed file
([ADR 0011](0011-managed-files-are-generated-never-edited.md)).
The prohibition in ADR 0019 stands and widens: it ceases to be "only the installer may link"
and becomes "nothing links, the installer included".
When this is accepted, ADR 0019 becomes superseded rather than edited — its reasoning is why
the rule exists at all, and the incident behind it is the reason anyone believes either record.
## Consequences
- The path-resolution hazard is removed rather than governed. There is no link for a container
engine to resolve, so the class of failure that cost production data is closed by
construction.
- A node's runtime state stops pointing into source. What a node holds is derived output, which
is what the mesh's model already says it is everywhere else.
- **Staleness becomes a real problem that must be answered, not assumed away.** This is the
cost, and it is the whole cost: today a link cannot be stale, and a copy can. The answer has
to be detection — the installer comparing what is on disk against what the mesh says should
be — and it must be loud, because a silently stale definition is exactly the failure shape
this mesh keeps producing ([ADR 0010](0010-delivery.md)).
- Reconciliation gets more expensive: comparing content rather than checking a pointer's
target, on every module, on every node.
- Disk usage rises, trivially, and is not a consideration.
- Existing links must be converted. A node mid-migration holds both forms, so reconciliation
has to handle finding a link where a file now belongs — the mirror image of the adoption it
already does.
## Open
- **How staleness is detected.** Content hash, version marker, or regeneration on every
reconcile. This is the decision that makes or breaks the change and it is not taken here.
- **Whether anything must keep a link** for reasons outside the mesh's control. If something
does, that is a finding worth recording rather than an exception worth granting quietly.
- **Migration order.** Converting a node's links is a change to how its services resolve their
own definitions, which is not a change to make everywhere at once.
Until those are answered this record stays `proposed`, and ADR 0019 remains the governing rule.
## References
- [ADR 0012](0012-the-mesh-creates-no-symlinks.md) — the incident, and the rule this widens.
- [ADR 0011](0011-managed-files-are-generated-never-edited.md) — the machinery that makes a
copy safe.
- [`03-DESIGN/00-as-is/05-runtime-and-installation.md`](../03-DESIGN/00-as-is/05-runtime-and-installation.md)
— what the installer does today, including reconciliation and adoption.