Files
hq/02-DECISIONS/0004-managed-files-are-generated-never-edited.md
T
jschoubben 77f3a4cea7 Consolidate: 65 decision records to 23
Every remaining cluster merged. Each was one design that had been split across
several records because it was worked out over days rather than at once.

  the node host          8 -> 1    applies not decides, depends on nothing,
                                   per operating system, root service, the
                                   launcher, episodic, what a declaration is,
                                   actions from the bundle only
  a node and how it joins 4 -> 1   what a node is, joining, the link as
                                   security boundary, the enrolment token
  modules and the graph   7 -> 1   everything is a module, no domain modules,
                                   three edges, provisioning, the core library
  substrate and control   6 -> 1   the test, seven contexts, one control plane,
    plane                          the authority is not a database, the named
                                   products, the pinned bundle
  connectivity            3 -> 1   a route is a grant, reachability declared,
                                   filter rules
  delivery                5 -> 1   reconciliation not a pipeline, artifacts,
                                   the three silos, a failed step, the verdict
  the lab                 5 -> 1   (earlier)
  how this repository     10 -> 1  (earlier)
    works

Nothing was dropped. Each consolidated record carries the reasoning of the ones
it absorbs -- the measurements, the incidents, the alternatives rejected --
because that reasoning is the only reason to keep a record at all. What is gone
is the fragmentation: eight files to read to understand tier 0, when tier 0 is
one component.

The four superseded records went too. They existed to point at their
successors, and the successors now contain what they said.

The checker made this safe. Each merge left dangling links -- 38 files after
the host merge alone -- and it named every one. Nothing was found by reading,
and a manual pass would certainly have missed some, including references inside
AGENTS.md which every session loads.
2026-08-28 20:03:24 +02:00

3.0 KiB

status, date, deciders, reconstructed
status date deciders reconstructed
accepted 2026-04-03 jochen true

4. Managed files are generated onto nodes and never edited there

Reconstructed after the fact from the evidence cited below.

Context

ADR 0048 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.