--- status: proposed date: 2026-08-27 deciders: jochen reconstructed: false extends: 0043-a-declaration-is-an-ordered-list-of-owned-resources.md --- # 52. A filter rule names its source, or it is not a rule ## Context [Research 004](../01-RESEARCH/004-lab-network/analysis.md) found this while looking for something else: > Several manifests declare `scope: public` on firewall rules — `wireguard`, `traefik`, `gitea`, > `mailu`, `qbittorrent`. It is **not part of the rule type** and is **referenced by no code** in > the firewall path. Real scoping is done with `from:`. **So five manifests appear to restrict a port and restrict nothing.** Anyone reading them — including whoever wrote the next one by copying — sees an access control that does not exist. Research 004 already names the shape: it is `how-we-build`'s *an unenforced rule is indistinguishable from a wrong one, and costs more, because people believe it.* This is that rule, in the firewall, on the modules most worth restricting. The mechanism that let it happen is worth more than the instance. `scope:` was accepted because **unknown keys were ignored**. Nothing rejected it, nothing warned, and it spread by copying for long enough to reach five manifests. ## Decision **A rule names its source.** `from:` is the only way to scope a rule, and a rule without one is open — which it must therefore say plainly rather than imply otherwise. **`scope:` is removed, not implemented.** Giving it meaning would leave two ways to express one thing, and a manifest carrying both would need a precedence rule nobody would remember. The five manifests are corrected to `from:` where they meant to restrict something, and left open where they did not — and finding out which is which is part of the work, not a formality. **An unknown key is refused.** This is the general fix and the reason to bother: > A manifest carrying a key the schema does not define is **rejected**, naming the key. The host already works this way — its declaration parser sets `DisallowUnknownFields` and collects every problem into one refusal ([ADR 0043](0043-a-declaration-is-an-ordered-list-of-owned-resources.md)). **Manifests are the layer where that discipline is missing**, and `scope:` is what missing looks like: not a wrong value, an invented one, silently accepted for months. ## Consequences - **This class of fiction stops at validation** rather than at an audit. A misspelled `form:`, an invented `scope:`, a key from a different schema — each fails on the manifest that introduces it, once, instead of spreading. - **Existing manifests will fail validation**, and some of those failures will be keys somebody believed were doing something. That is the finding, not the cost — but it means the refusal cannot be switched on without reading every manifest first. - **The firewall becomes reviewable.** Today a rule's real effect is only visible by knowing which keys are fictional. Afterwards the manifest says what happens. - **Five manifests need a decision each**, and `wireguard` and `traefik` genuinely are open to the world — they must be, which the manifest should state rather than appear to deny. - **It does not make the rules correct**, only honest. A rule that says `from:` anywhere is legitimately open; this record ensures it says so. ## References - [Research 004](../01-RESEARCH/004-lab-network/analysis.md) §7 — the finding. - [`how-we-build.md`](../00-META/how-we-build.md) — the rule about unenforced rules. - [ADR 0043](0043-a-declaration-is-an-ordered-list-of-owned-resources.md) — unknown-is-refused, where it already holds.