Jochen asked whether the order made sense. It did not -- it followed when things happened to be decided, which after consolidation is fictional anyway since record 5 alone folds decisions taken across a week. Concretely wrong before: the domain statement sat at 8, after five engineering rules; the constitution was scattered across 5, 12 and 17; the tiers landed at 15, 16, 21 and 22 with process records in between. Now it walks: what the mesh is (1-3), its tiers from the bottom up (4-8), what runs on them and how it gets there (9-10), how it is built (11-16), how it is checked (17-18), how we work (19-23). Two things made this safe rather than free. It is a permutation, not a compaction, so the renames go through temporary names -- otherwise two files want one slot and one is lost. And the reference rewrite is a single simultaneous pass, because almost every number moved into a slot another number was vacating; replacing one at a time would have cascaded and pointed things at the wrong record while still resolving. Verified: 284 [ADR NNNN](path) links across the repository, all with matching text and target. The ordering principle is now stated in 19 rather than left implicit -- the repository already said "the numbering is the flow" about its folders, and there was no reason for the records to be the exception.
64 lines
3.0 KiB
Markdown
64 lines
3.0 KiB
Markdown
---
|
|
status: accepted
|
|
date: 2026-04-03
|
|
deciders: jochen
|
|
reconstructed: true
|
|
---
|
|
|
|
# 11. Managed files are generated onto nodes and never edited there
|
|
|
|
> Reconstructed after the fact from the evidence cited below.
|
|
|
|
## Context
|
|
|
|
[ADR 0006](0006-the-substrate-and-the-control-plane.md) put every binding in the mesh
|
|
database. But the things that consume those bindings — environment files, service
|
|
definitions, daemon configuration, firewall rules — are files on a node's disk, because that
|
|
is what the software reading them requires.
|
|
|
|
So the same value exists twice: authoritatively in the database, and materialised in a file.
|
|
Any edit to the file is a change to a copy. Before this decision, environment values could be
|
|
pushed from a node back into the database, which made the direction ambiguous in both
|
|
directions at once.
|
|
|
|
## Considered options
|
|
|
|
1. **Bidirectional sync** — a node's edits flow back to the database. Rejected, and removed.
|
|
Two writers and no arbiter: whichever synced last wins, and neither is authority.
|
|
2. **Files are authoritative; the database is a cache of them.** Rejected — it inverts
|
|
ADR 0013 and returns to state that cannot be reconciled across nodes.
|
|
3. **Strictly one-directional: the database is written, files are generated.** Chosen.
|
|
|
|
## Decision
|
|
|
|
Every managed file is **derived**. A synchroniser regenerates it from the mesh database
|
|
whenever the underlying values change. The write path is the mesh tool that owns the value;
|
|
the file is an output.
|
|
|
|
This applies to generated environment files, service definitions, managed configuration, and
|
|
anything else a synchroniser lists as its own.
|
|
|
|
An edit to a managed file survives until the next synchronisation and is then overwritten,
|
|
without a warning, taking whatever it was fixing with it.
|
|
|
|
## Consequences
|
|
|
|
- **A file edited on a node is a bug with a delay on it.** This is now one of the mesh's core
|
|
values, and it is a consequence of this decision rather than a stance taken independently.
|
|
- To change a value you must know which tool owns it. That is a real cost, paid every time,
|
|
and the reason the mesh provides a way to ask whether a given file is managed.
|
|
- Debugging by editing a file no longer works, and fails in the most confusing way available:
|
|
it works, and then stops working later for no locally visible reason.
|
|
- Recovery is cheap. A node's entire managed surface can be regenerated from the database.
|
|
- Values resolve by precedence — database override, then existing file value, then generated,
|
|
then manifest default — which means an unset override does not clobber a generated
|
|
password. The subtlety is real and has caused its own confusion.
|
|
|
|
## References
|
|
|
|
- `Extract hal/env-sync module, remove syncEnvToDb`, 2026-04-03 — the commit that removed the
|
|
node-to-database direction.
|
|
- Knowledge base: `conventions/no-direct-file-mutation`, `provisioning` (resolution priority).
|
|
- Regeneration gaps: `troubleshooting/changed-manifest-default-not-rerendered`,
|
|
`troubleshooting/config-removed-from-manifest-not-pruned`.
|