papa-hq reads 01 research -> 03 decision -> 02 design. The order is a scar, not a choice: 02-DESIGN existed from its initial commit, and when adr/ was finally promoted on 2026-07-13 it took the next free number rather than its place in the sequence. By then design was too settled to renumber. hal-hq was three commits old, so it is not. adr/ becomes 02-DECISIONS and 02-DESIGN becomes 03-DESIGN, and following the folder numbers now walks the process in the order it happens: research produces a decision, the decision authorises a design. 00-GENESIS becomes 00-META, matching papa's rename from the same restructure. Every path reference rewritten across documents, frontmatter, playbooks and skills. All links resolve; all 58 frontmatter blocks parse and their path fields still point at files that exist.
64 lines
3.0 KiB
Markdown
64 lines
3.0 KiB
Markdown
---
|
|
status: accepted
|
|
date: 2026-04-03
|
|
deciders: jochen
|
|
reconstructed: true
|
|
---
|
|
|
|
# 4. Managed files are generated onto nodes and never edited there
|
|
|
|
> Reconstructed after the fact from the evidence cited below.
|
|
|
|
## Context
|
|
|
|
[ADR 0003](0003-the-mesh-database-is-the-source-of-truth.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 0003 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`.
|