Agent: strict config unmarshal + schema version check — reject configs with unknown fields #9

Open
opened 2026-07-26 17:00:02 +10:00 by benvin · 0 comments
Owner

Problem. The rendered-config types in internal/agent are defined
independently of the API's compiler output, and the payload carries no schema
version. If the API emits a section or field an older agent doesn't know about,
Go's default YAML/JSON unmarshal silently drops it. The agent then applies a
partial policy and reports the generation as fully applied — the convergence
view shows green while the device is missing rules.

Proposal.

  • Unmarshal strictly (yaml.KnownFields(true) / DisallowUnknownFields); any
    unknown field is a hard error, not a skip.
  • Add an explicit schema_version to the rendered config. Agent refuses to
    apply a version newer than it understands and reports
    status: incompatible-schema instead of a generation number.
  • On refusal, keep applying the cached last-good config (existing
    don't-fail-closed behaviour) so an incompatible push degrades to "stale",
    never to "silently partial".

Acceptance. An agent one release behind, fed a config with a new section:
does not apply, does not report the new generation, alerts via status. Unit
test with an unknown top-level key and an unknown nested field.

Related: shared types module issue in tomswallapi (#7 below) removes the drift
source; this issue is the defense when drift happens anyway.

**Problem.** The rendered-config types in `internal/agent` are defined independently of the API's compiler output, and the payload carries no schema version. If the API emits a section or field an older agent doesn't know about, Go's default YAML/JSON unmarshal silently drops it. The agent then applies a *partial* policy and reports the generation as fully applied — the convergence view shows green while the device is missing rules. **Proposal.** - Unmarshal strictly (`yaml.KnownFields(true)` / `DisallowUnknownFields`); any unknown field is a hard error, not a skip. - Add an explicit `schema_version` to the rendered config. Agent refuses to apply a version newer than it understands and reports `status: incompatible-schema` instead of a generation number. - On refusal, keep applying the cached last-good config (existing don't-fail-closed behaviour) so an incompatible push degrades to "stale", never to "silently partial". **Acceptance.** An agent one release behind, fed a config with a new section: does not apply, does not report the new generation, alerts via status. Unit test with an unknown top-level key and an unknown nested field. Related: shared types module issue in tomswallapi (#7 below) removes the drift source; this issue is the defense when drift happens anyway.
Sign in to join this conversation.