Order the records the way the system is learned
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.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
---
|
||||
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`.
|
||||
Reference in New Issue
Block a user